Microsoft Purviewでスキャン済みアセットのGUID一覧を取得する方法|REST APIでDatabricksに保存

Microsoft Purviewでスキャン済みのAzureサービスやDatabricksテーブルのGUIDを一覧で取得したいなら、Type定義(ビルトイン)ではなく「Data Mapの検索(Discovery)API」でエンティティ(=実アセット)を列挙するのが最短ルートです。本記事ではREST APIの使い方と、DatabricksでDeltaテーブルに保存して分析へつなげる実装手順をまとめます。

目次

「ビルトインのGUIDしか返らない」原因は、参照している対象が違う

まず押さえたいのは、Microsoft Purview(Data Map)が内部的にApache Atlasのモデルを持っていて、APIが大きく「型(Type)」と「実体(Entity)」に分かれている点です。ここを取り違えると、あなたがスキャンしたテーブルやファイルのGUIDではなく、システムが持つビルトイン定義(分類や型定義)のGUIDばかりが返ってきます。

区分意味返ってくるGUIDの正体典型的なエンドポイント例今回ほしいものか?
Type(型定義)「テーブル」「カラム」などのテンプレート。属性構造・制約を定義型定義のGUID(ビルトインが多い)/catalog/api/atlas/v2/types/...、/datamap/api/atlas/v2/types/...いいえ
Classification(分類)PIIなどの分類ラベル。適用ルールやメタ情報分類定義のGUID(ビルトインが多い)/.../types/classificationdef などいいえ
Entity(エンティティ)スキャンされた「実際のアセット」(テーブル、ファイル、ダッシュボード等)アセットのGUID(あなたが欲しいGUID)/datamap/api/search/query、/catalog/api/search/query、/.../atlas/v2/entity/guid/...はい

結論として、GUID一覧が欲しい場合は「検索APIでEntityを列挙」→「レスポンスの id を回収」という流れが王道です。

スキャン済みアセットのGUID一覧を取得できるAPI

Microsoft PurviewのData Mapには、検索でアセットを引けるAPIがあります。公式のREST APIリファレンス上では、代表的に以下が「アセット一覧(検索結果)を返す」エンドポイントです。

推奨:Discovery – Query(Data Map Search)

アセットを検索し、結果の配列にGUID(id)を含めて返します。キーワード検索に加え、objectType(Tables/Filesなど)やcollectionIdで絞り込みでき、最大1000件/リクエストでページングできます。

リクエスト例(基本形)

POST {endpoint}/datamap/api/search/query?api-version=2023-09-01
Authorization: Bearer {access_token}
Content-Type: application/json

{
"keywords": "*",
"limit": 1000
}

レスポンス例(重要フィールド)

{
  "continuationToken": "<token>",
  "@search.count": 5155,
  "@search.count.approximate": true,
  "value": [
    {
      "id": "24c16e53-1bfd-4d6c-b4ce-b1f6f6f60000",
      "qualifiedName": "mssql://.../dbo/exampledata1",
      "name": "exampledata1",
      "entityType": "azure_sql_mi_table",
      "objectType": "Tables",
      "assetType": ["Azure SQL Managed Instance"],
      "classification": [],
      "label": []
    }
  ]
}

この value[].id が、スキャン済みアセット(Entity)のGUIDです。continuationToken が返ってきた場合は、次ページがある合図なので、同じAPIに continuationToken を付けて繰り返します。

endpoint の決め方(クラシック / 新ポータル)

Purviewは「どのポータル(クラシック or 新Microsoft Purview)を使っているか」でベースURLが変わります。加えて、スキャン管理(登録・スキャン実行)とカタログ/データマップ(検索・取得)でサブドメインが違う点も混同しやすいので、ここは明確に分けます。

用途クラシック(例)新Microsoft Purviewポータル(例)この用途でGUID一覧は取れる?
データマップ/カタログ(検索・Entity取得)https://{accountName}.purview.azure.comhttps://api.purview-service.microsoft.comはい
スキャン(データソース登録/スキャン実行など)https://{accountName}.scan.purview.azure.comhttps://api.scan.purview-service.microsoft.com基本的にいいえ(管理系)

GUID一覧の取得は「データマップ/カタログ側」のAPIを使う、というのがポイントです。

Discovery – Query APIの主要パラメータを押さえる

Discovery – Queryは柔軟に絞り込みできます。まずは「全件(keywords=*)を安全にページングで回収」できる状態を作り、その後に運用上の要件に合わせてフィルタを足すのが、失敗しにくい手順です。

項目役割実務での使いどころ注意点
keywords全文検索キーワード(検索可能フィールドに適用)まずは * で全件、特定ソース名やテーブル名で部分抽出* は全件ヒットしやすく重い。limitとページング前提
limit1回で返す件数一括エクスポートなら 1000最大 1000。それ以上は continuationToken で回す
continuationToken次ページ取得用トークン全件回収・定期同期で必須トークンが返らない=終端
filter絞り込み条件(and/or/not、属性条件など)objectType=Tables、collectionId、entityTypeでの限定条件を強くしすぎると「取得漏れ」が起きる
orderbyソート条件更新日時で追いかけたいとき増分設計は「更新時刻の単独依存」に注意(遅延・再処理があり得る)
facets集計用のファセット(assetType/classification等)「どのソースが多いか」をAPIだけで概観まずは取得→Databricks集計でも十分

実装の流れ:DatabricksでGUID一覧を取得してDeltaテーブルに保存する

ここからは「現場でそのまま動かせる」ことを意識して、Databricks前提の実装例をまとめます。Microsoft Tech Communityの記事ではPython SDK(azure-purview-catalog)でのエクスポート例が紹介されていますが、Databricksでは依存関係やネットワーク制約があることも多いので、まずは requests だけで呼べるREST直叩き版を中心に紹介します。

事前準備:サービスプリンシパルと権限

最低限、以下を揃えます。

  • Microsoft Entra IDでアプリ登録(クライアントID / クライアントシークレット / テナントID)
  • Purview側で、対象コレクションにサービスプリンシパルを付与(Data Reader もしくは Data Curator)
  • Databricks Secret Scope(またはKey Vault連携)に機密情報を格納

コレクション権限は「付与したコレクション配下だけが見える」設計なので、想定より件数が少ない場合は、まずここを疑うのが近道です。

ステップ:アクセストークンを取得する

Purview Data MapのデータプレーンAPIは、Microsoft Entra IDのトークン(リソース/スコープが https://purview.azure.net)をBearerとして渡します。

Databricks(Python)例:トークン取得

import requests

tenant_id = dbutils.secrets.get("my_scope", "TENANT_ID")
client_id = dbutils.secrets.get("my_scope", "CLIENT_ID")
client_secret = dbutils.secrets.get("my_scope", "CLIENT_SECRET")

token_url = f"[https://login.microsoftonline.com/{tenant_id}/oauth2/token](https://login.microsoftonline.com/{tenant_id}/oauth2/token)"
payload = {
"grant_type": "client_credentials",
"client_id": client_id,
"client_secret": client_secret,
"resource": "[https://purview.azure.net](https://purview.azure.net)"
}
token_resp = requests.post(token_url, data=payload, timeout=30)
token_resp.raise_for_status()
access_token = token_resp.json()["access_token"]

もし社内標準でv2.0エンドポイントを使う場合は、/oauth2/v2.0/token と scope=https://purview.azure.net/.default で同等に取得できます(環境のポリシーに合わせてください)。

ステップ:Discovery – Queryでアセットをページング取得する

最大1000件/回なので、全件取得では必ず continuationToken でループします。途中で失敗しても再実行しやすいよう、レスポンスを正規化してから蓄積します。

Databricks(Python)例:GUID一覧を全件回収

import time
import requests

# Purviewのエンドポイント(クラシック or 新ポータルに合わせて切り替え)

purview_endpoint = dbutils.secrets.get("my_scope", "PURVIEW_ENDPOINT")

# 例: "https://{accountName}.purview.azure.com"  または "[https://api.purview-service.microsoft.com](https://api.purview-service.microsoft.com)"

api_url = f"{purview_endpoint}/datamap/api/search/query?api-version=2023-09-01"

headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
}

def fetch_all_assets(object_type=None, limit=1000, sleep_sec=0.2, max_retry=5):
results = []
token = None


while True:
    body = {
        "keywords": "*",
        "limit": limit
    }

    # 任意:Tablesだけに限定(最初はNoneで全件→後から絞るのがおすすめ)
    if object_type:
        body["filter"] = {"and": [{"objectType": object_type}]}

    if token:
        body["continuationToken"] = token

    # 簡易リトライ(429/5xx対策)
    for attempt in range(max_retry):
        resp = requests.post(api_url, headers=headers, json=body, timeout=60)
        if resp.status_code in (429, 500, 502, 503, 504):
            backoff = (2 ** attempt) * 1.0
            time.sleep(backoff)
            continue
        resp.raise_for_status()
        data = resp.json()
        break
    else:
        raise RuntimeError("Purview API request failed after retries")

    values = data.get("value", [])
    results.extend(values)

    token = data.get("continuationToken")
    if not token:
        break

    time.sleep(sleep_sec)

return results


# 例1:全部取る(FilesやDashboardsも含む)

all_assets = fetch_all_assets(object_type=None)

# 例2:テーブルだけに限定して取り直す

table_assets = fetch_all_assets(object_type="Tables")

print(f"all_assets: {len(all_assets)}, table_assets: {len(table_assets)}")

ここで回収できる id がGUIDです。あとはDatabricksに保存し、資産量・ソース別の件数・更新頻度などを可視化できます。

ステップ:Spark DataFrameに変換してDeltaテーブルへ保存

Purviewの検索レスポンスはネストを含むことがあるため、まずは「分析に必要な列だけをフラット化」してから保存すると扱いやすくなります。特に、assetType・objectType・entityType・collectionId・updateTime(あれば)を残すと、後工程が楽になります。

Databricks(Python)例:フラット化してDeltaへ保存

from pyspark.sql import functions as F

# Pythonのdict配列をSpark DataFrameへ

raw_df = spark.createDataFrame(all_assets)

# よく使う列だけ整形(存在しない列があっても落ちにくいようにcoalesce)

assets_df = (
raw_df
.withColumnRenamed("id", "guid")
.withColumn("assetType0", F.expr("assetType[0]"))
.withColumn("classification_count", F.size(F.col("classification")))
.withColumn("label_count", F.size(F.col("label")))
.select(
"guid",
"name",
"qualifiedName",
"objectType",
"entityType",
"assetType0",
"collectionId",
"updateTime",
"createTime",
"owner",
"classification_count",
"label_count"
)
.withColumn("snapshot_date", F.current_date())
)

target_table = "governance.purview_assets_inventory"

(
assets_df
.write
.format("delta")
.mode("append")
.saveAsTable(target_table)
)

この形にしておくと、例えば次のような分析がすぐできます。

  • assetType別の件数(どのソースが多いか)
  • objectType=Tablesだけ抽出して、テーブル資産の規模把握
  • collectionId別の資産量(運用単位・責任境界の見える化)
  • classification_countが0でない資産の比率(ラベリング進捗)

おすすめのテーブル設計:スナップショット型と最新参照型を分ける

コスト管理や資産推移を見たい場合、「毎日/毎週のスナップショット(append)」と「最新状態(upsert)」を分けると運用が安定します。

テーブル保存方式主な用途ポイント
purview_assets_inventoryappend(snapshot_date付き)資産増減、スキャン対象量の推移、コスト/負荷の見立て日次/週次でジョブ化しやすい
purview_assets_latestMERGE(guidでupsert)常に最新のGUID一覧を参照して他データとJOIN分析側のJOINが軽くなる

MERGE例(最新状態を維持)

from delta.tables import DeltaTable

latest_table = "governance.purview_assets_latest"

# 初回は作成

assets_df.write.format("delta").mode("overwrite").saveAsTable(latest_table)

# 2回目以降はMERGE

t = DeltaTable.forName(spark, latest_table)
(
t.alias("t")
.merge(
assets_df.alias("s"),
"t.guid = s.guid"
)
.whenMatchedUpdateAll()
.whenNotMatchedInsertAll()
.execute()
)

削除の扱いは要注意です。Purview側のコネクタやスキャンの仕様によっては「元データで削除されてもPurview内のアセットが即時に消えない」ケースがあります。削除も含めて厳密に追いたい場合は、スナップショットの差分(当日存在しないguid)を「削除候補」として別テーブルに出す運用が現実的です。

「ビルトインしか返らない」問題を解くためのチェックリスト

症状と対処を、現場でよくあるパターンに絞って整理します。

症状よくある原因対処
Type定義っぽいGUIDしか取れないType系のAPI(typedefs等)を叩いているEntity検索(/datamap/api/search/query)で value[].id を回収する
件数が1000で止まるAPIの上限(limit最大1000)でページングしていないcontinuationToken が無くなるまでループする
403/権限エラーになるPurviewのコレクション権限が不足対象コレクションにData Reader/Data Curatorを付与。サブコレクションのみ付与の場合、見える範囲が限定される
データは取れるが欲しいソースが含まれないcollectionIdフィルタや権限範囲のミスマッチまずフィルタ無しで取得→assetType/collectionIdを確認→必要な絞り込みを追加
新ポータルのつもりがクラシックに見える/挙動が違うendpointの使い分け・ネットワーク(Firewall/Private Endpoint/DNS)の影響クラシック:{accountName}.purview.azure.com、新:api.purview-service.microsoft.com を明確に切り替え。許可リストやDNSも確認
429(Too Many Requests)が出る短時間に大量リクエスト指数バックオフでリトライ、limitを大きくして回数を減らす、ジョブ時間帯を分散

フィルタ設計のコツ:最初は「広く取って、後から絞る」

「Databricksのテーブルだけ」「Azure SQLだけ」など、最初から狙い撃ちしたくなりますが、運用設計としては段階を踏む方が安全です。特にPurviewはコネクタやバージョンで entityType/assetType の表記が変わることがあるため、まずは全件を取り、実データを見てから確実な条件を採用するのが失敗しにくいです。

よく使う絞り込みパターン(Discovery – Query)

以下は実務で出番が多い絞り込み例です。

目的例(リクエストbodyのイメージ)コメント
テーブルだけ欲しい{"keywords":"*","limit":1000,"filter":{"and":[{"objectType":"Tables"}]}}まずはこれが一番シンプル
特定コレクション配下だけ{"keywords":"*","limit":1000,"filter":{"and":[{"collectionId":"<collection-guid>"}]}}運用単位(部門/プロジェクト)で棚卸したいとき
特定のassetTypeだけ{"keywords":"*","limit":1000,"filter":{"and":[{"assetType":"Azure Databricks"}]}}assetType表記は環境で揺れることがあるので、最初に全件から確認推奨
qualifiedNameに文字列を含むもの{"keywords":null,"limit":1000,"filter":{"and":[{"attributeName":"qualifiedName","operator":"contains","attributeValue":"databricks"}]}}「URIのパターン」が安定しているなら強い

Databricks由来のアセットだけを厳密に取りたい場合でも、まずは objectType=Tables でテーブル一覧を作り、そこから entityType と assetType0 の実値を見て条件を確定させる方が、取得漏れを減らせます。

Python SDKで実装したい場合(Tech Communityサンプルをベースにする)

社内標準でAzure SDKを使う方針なら、Tech Communityで紹介されているように azure-purview-catalog を使う方法もあります。検索(Discovery)を呼び出す形は同じで、戻ってくるデータに id(GUID)や entityType が含まれます。

インストール例(必要に応じて)

pip install azure-identity
pip install azure-purview-catalog
pip install pandas

SDK例(概念)

from azure.identity import ClientSecretCredential
from azure.purview.catalog import PurviewCatalogClient

credential = ClientSecretCredential(tenant_id=tenant_id, client_id=client_id, client_secret=client_secret)
client = PurviewCatalogClient(endpoint=purview_endpoint, credential=credential)

response = client.discovery.query(search_request={"keywords": "*"})

# response["value"] の各要素に id が入る(=GUID)

ただし、SDK経由でも「最大1000件」問題は残るため、全件抽出するならページング(continuationToken)を意識した実装が必要です。大量データをエクスポートする運用では、REST直叩き+リトライ制御の方が挙動を掴みやすい場面もあります。

運用に乗せるための実践ポイント

GUIDを取れるようになったら、次は「継続的に、壊れずに回る」状態を作るのが価値になります。最後に、実務で効くポイントをまとめます。

  • ジョブはスナップショット型で始める:まずは日次で全件をappendし、増減の傾向を掴む。後から増分最適化しても遅くない。
  • 列は欲張らない:最初は guid / qualifiedName / name / objectType / entityType / assetType / collectionId / updateTime があれば分析は十分進む。
  • フィルタは後置き:最初から狙い撃ちすると取得漏れの原因になる。全件→観察→条件確定が安全。
  • 429・ネットワーク要因を織り込む:指数バックオフ、タイムアウト、ジョブの再実行性(冪等)を最初から入れる。
  • Purview側の仕様差を前提にする:コネクタ・ポータル・ネットワーク(Private Endpoint/Firewall)で見え方が変わることがあるので、endpointと権限範囲の記録を残す。

まとめ

Microsoft Purviewでスキャン済みアセットのGUID一覧を取得するには、ビルトイン定義(Type/Classification)ではなく「Entity(実アセット)を検索で列挙するAPI」を使うのが正攻法です。Discovery – Query(/datamap/api/search/query)で全件をページング取得し、レスポンスの value[].id を回収してDatabricksのDeltaテーブルへ保存すれば、資産量・ソース別分布・分類進捗などの分析がすぐに始められます。

参考(公式・一次情報)

  • https://learn.microsoft.com/en-us/rest/api/purview/datamapdataplane/discovery/query?view=rest-purview-datamapdataplane-2023-09-01
  • https://learn.microsoft.com/en-us/purview/data-gov-api-create-assets
  • https://techcommunity.microsoft.com/discussions/azurepurview/exporting-microsoft-purview-data-assets-using-the-rest-api/3948965
  • https://techcommunity.microsoft.com/blog/azurearchitectureblog/exploring-purview%E2%80%99s-rest-api-with-python/2208058

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次