Microsoft Purview へのアクセスや Data Map API 呼び出しを「誰が・いつ・どこから・何をしたか」まで追跡できると、事故対応とコンプライアンスは一気に楽になります。本記事では、Purview 監査(Unified Audit Log)と Azure Monitor(診断ログ)を組み合わせ、ポータル・API・ネットワークの各層で確実に記録を残し、プログラムから検索・取得・可視化するための実践手順とサンプルを余すことなくまとめます。
Purview の監査とログがカバーする範囲をまず整理
Microsoft Purview の「誰が・どこから・何をしたか」を可観測化するには、主に次の 3 レイヤーを使います。
- Microsoft 365 統合監査ログ(Unified Audit Log; UAL):Purview Studio で行われた操作や Data Map API に対する論理操作(例えば検索、アセットの閲覧、用語集操作など)を記録。ユーザー/アプリ、操作名、時刻、クライアント IP、対象オブジェクトなどが入ります。
- Azure Monitor(診断設定/リソース ログ):Purview アカウントが受けたデータプレーン要求の事実(HTTP メソッド、URI、ステータス、処理時間、呼び出し元 IP など)を記録。Log Analytics / Event Hub / Storage に送信可能。
- Azure AD(Entra ID)サインイン/ディレクトリ監査:誰がどこから認証したかの証跡。Purview そのものの操作ログとは別ですが、IP やデバイス情報と照合する際に有用。
| 目的 | 推奨するログ/機能 | 主なフィールド | 長所 | 補足 |
|---|---|---|---|---|
| 「誰が」「どの操作」を行ったか | Microsoft 365 統合監査ログ(Purview のレコード種別) | CreationDate, UserId, Operation, Workload, ObjectId, ClientIP, UserAgent, ResultStatus | コンプライアンス用途に適合、検索 UI と PowerShell/API 両対応 | 保持はライセンスと保持ポリシーに依存 |
| HTTP レベルでの API 呼び出し状況 | Purview の診断ログ(例:アカウント/データプレーン要求カテゴリ) | TimeGenerated, OperationName, RequestUri, HttpStatusCode, DurationMs, CallerIpAddress, Identity | 遅延や 4xx/5xx の可視化が容易、SIEM 連携しやすい | Azure Monitor 側の保持/コスト設計が必要 |
| 発信元ネットワークの特定 | Entra ID サインインログ(Graph / ポータル) | UserId, AppId, IPAddress, ConditionalAccessStatus, DeviceDetail | 異常な場所/デバイス検知に有効 | Purview 操作ログと時刻・ユーザーで照合 |
最短で「監査できる状態」にする 6 ステップ
| 手順 | 具体的な操作 | ポイント |
|---|---|---|
| 1. 統合監査ログの取り込みを有効化 | Microsoft 365 管理センター/コンプライアンス ポータルから「監査」を有効化。PowerShell では次のコマンドで有効化できます。 # Exchange Online 管理モジュール(EXO V3)を使用 Connect-ExchangeOnline Set-AdminAuditLogConfig -UnifiedAuditLogIngestionEnabled $true Get-AdminAuditLogConfig | fl UnifiedAuditLogIngestionEnabled | 有効化後、取り込み開始まで数十分のラグが出ることがあります。 |
| 2. Purview 監査ソリューションを有効化 | Purview Studio にサインインし、ホームの「監査」カードまたは「すべてのソリューション > 監査」からセットアップを完了します。 | 表示されない場合はロールとライセンスを確認。 |
| 3. API 呼び出しを捕捉するレコード種別を確認 | Purview の監査検索でレコード種別を Purview Data Map に関する操作(例:「PurviewDataMapOperation」等、テナントで表示される名称)に絞ると、検索/参照/用語集/スキャン構成などの操作が抽出できます。 | 命名はテナント/地域/更新で変わることがあります。UI 上の表記を優先してください。 |
| 4. 診断ログの送信先を設計 | Purview アカウント > 診断設定 から、以下のカテゴリを Log Analytics / Event Hub / Storage に送信します。 アカウント操作/要求(例:PurviewAccountLogs) データプレーン要求(例:PurviewDataPlaneRequests) | Log Analytics は即時分析、Event Hub は SIEM/ストリーム処理、Storage は長期保管に最適。 |
| 5. ログの検索・取得方法を用意 | ポータル:Purview Studio > 監査 PowerShell:Search-UnifiedAuditLog Office 365 Management Activity API:Audit.General を購読 Microsoft 365 Audit(Premium)Export API:継続エクスポート Log Analytics(KQL):リアルタイム分析・検知 | API 経由の取得は自動化しやすく、SIEM 連携の起点になります。 |
| 6. Private Endpoint 前段のリバースプロキシ/ロードバランサ指針を適用 | 必須:Host ヘッダーと SNI は Private Link 側 FQDN を指すよう維持/再設定 推奨:X-Forwarded-For で元クライアント IP を継承 推奨:エンドツーエンド TLS(終端する場合は再暗号化し SNI を厳密指定) | 公式ガイドの構成例(Application Gateway + Private Link 等)の要点を下段のセクションで整理しています。 |
「誰が・どこから・何をしたか」を把握するための実用パターン
1) まずは Unified Audit Log(Purview 監査)で論理操作をつかむ
Purview Studio からの検索、データ アセットの表示、用語集(Glossary)の作成・更新、スキャンの開始/停止など、ユーザーやアプリがビジネス上どの操作を行ったかは UAL 側が最も見やすいです。PowerShell での基本は次の通り:
$start = (Get-Date).AddDays(-7)
$end = Get-Date
$op = @("PurviewDataMapOperation","PurviewCatalogOperation") # 表示される実際の名前を使用
$logs = Search-UnifiedAuditLog -StartDate $start -EndDate $end -Operations $op -ResultSize 5000
$logs |
Select-Object CreationDate, UserIds, Operation, ClientIP, UserAgent, Workload, ObjectId, ResultStatus |
Format-Table -Auto
ポイント:UserIds は複数の識別子が入ることがあり、AuditData(JSON)に詳細なパラメータ(実行された API の種類やパス)が含まれます。必要に応じて次のように展開します。
$logs | ForEach-Object {
$j = $_.AuditData | ConvertFrom-Json
[PSCustomObject]@{
Time = $_.CreationDate
User = $_.UserIds
Operation = $_.Operation
ClientIP = $_.ClientIP
ApiName = $j.Operation
Target = $j.ObjectId
Parameters = ($j.Parameters | ConvertTo-Json -Compress)
Result = $j.ResultStatus
}
} | Format-Table -Auto
2) Azure Monitor(診断ログ)で HTTP レベルの事実をつかむ
Unified Audit Log が「論理操作」を示すのに対し、診断ログは「HTTP リクエストの事実」を示します。たとえば Data Map API への /catalog/api/search/query や /glossary 呼び出しが どの URI に、どのメソッドで、どのステータスで返ったかが記録されます。Log Analytics での KQL 例を 2 つの取り込み方式(リソース固有テーブル/旧 AzureDiagnostics)に分けて示します。
// 新しい「リソース固有」スキーマ例(テーブル名は環境により異なる)
PurviewDataPlaneRequests_CL
| where TimeGenerated > ago(24h)
| project TimeGenerated, OperationName_s, httpMethod_s, RequestUri_s, HttpStatusCode_d,
DurationMs_d, CallerIpAddress_s, Identity_s, CorrelationId_g
| order by TimeGenerated desc
// 旧 AzureDiagnostics 統合テーブルを使う場合の例
AzureDiagnostics
| where TimeGenerated > ago(24h)
| where ResourceProvider == "MICROSOFT.PURVIEW"
| project TimeGenerated, Category, OperationName, httpMethod_s=HttpMethod_s,
RequestUri_s, statusCode_d=StatusCode_d, DurationMs_d=DurationMs,
CallerIpAddress_s=CallerIpAddress, Identity
コツ:クライアント側で x-ms-client-request-id を付与すると、同 ID を診断ログとアプリ ログで突合でき、トラブル時の切り分けが一気に速くなります。
3) Graph のサインイン ログと照合して「どこから」を確定
Graph の /auditLogs/signIns ではユーザーやアプリのサインイン元 IP・デバイスが分かります。Purview の UAL レコードと ユーザー+時刻で突合すると、NAT/プロキシ配下でも「実ユーザー位置の推定」が精度向上します。
# Microsoft Graph PowerShell
Connect-MgGraph -Scopes "AuditLog.Read.All"
Select-MgProfile beta
$since = (Get-Date).AddHours(-24).ToString("o")
$signins = Invoke-MgGraphRequest -Method GET -Uri "/beta/auditLogs/signIns?`$filter=createdDateTime ge $since"
# UAL 側の Purview 操作(前述 $logs)と簡易突合(擬似例)
$join = foreach ($l in $logs) {
$s = $signins.value | Where-Object { $*.userPrincipalName -in $l.UserIds -and
([datetime]$*.createdDateTime) -ge (([datetime]$l.CreationDate).AddMinutes(-10)) -and
([datetime]$_.createdDateTime) -le (([datetime]$l.CreationDate).AddMinutes(10)) } |
Select-Object -First 1
[PSCustomObject]@{
Time = $l.CreationDate
User = ($l.UserIds -join ",")
Operation = $l.Operation
UAL_IP = $l.ClientIP
SignIn_IP = $s.ipAddress
App = $s.appDisplayName
Device = $s.deviceDetail?.displayName
}
}
$join | Format-Table -Auto
これにより「Purview で用語集を変更したのは社外 IP からのアクセスだった」といった異常検知が容易になります。
ログ保管・クエリ・自動化の設計(実装例付き)
Log Analytics を中心に据えた構成
- Purview 診断ログは Log Analytics に送信し、KQL で分析・アラート。
- Unified Audit Log は PowerShell(
Search-UnifiedAuditLog)で取得し、週次/日次で Log Analytics のカスタム テーブルへ取り込み。 - Entra ID サインインは Graph API で取得し、同じワークスペースで保持。
| データ ソース | 取り込み手段 | 宛先 | 主な用途 |
|---|---|---|---|
| Purview 診断ログ | 診断設定(Azure Monitor) | Log Analytics | API 失敗率、遅延、Top 呼び出し URI |
| Unified Audit Log(Purview) | PowerShell / O365 Management Activity API | Log Analytics(カスタム テーブル) | 論理操作の追跡、監査証跡 |
| サインイン ログ | Graph Audit(signIns) | Log Analytics | 発信元 IP・デバイスの特定、条件付きアクセス検証 |
Log Analytics(KQL)サンプル集
// 直近 1 日の API 失敗(5xx/429)トップ URI
PurviewDataPlaneRequests_CL
| where TimeGenerated > ago(1d)
| where HttpStatusCode_d >= 500 or HttpStatusCode_d == 429
| summarize count() by RequestUri_s, HttpStatusCode_d
| top 20 by count_ desc
// 組織外(許可サブネット外)からの Purview 操作(UAL 側)
let allow = dynamic(["203.0.113.0/24","198.51.100.0/24"]);
PurviewDataMapOperation_CL
| where TimeGenerated > ago(7d)
| extend IP = tostring(ClientIP_s)
| where not(ipv4_is_in_range(IP, allow[0]) or ipv4_is_in_range(IP, allow[1]))
| project TimeGenerated, UserId_s, OperationName_s, IP, ObjectId_s, ResultStatus_s
// 用語集(Glossary)に対する変更操作の抽出(UAL 側)
PurviewDataMapOperation_CL
| where TimeGenerated > ago(30d)
| where OperationName_s has_any ("Glossary","Term","Category")
| summarize count() by OperationName_s, UserId_s
| order by count_ desc
// API 応答遅延のパーセンタイル
PurviewDataPlaneRequests_CL
| where TimeGenerated > ago(7d)
| summarize p50=percentile(DurationMs_d,50), p95=percentile(DurationMs_d,95) by bin(TimeGenerated, 1h)
// UAL とサインイン ログの簡易ジョイン(ユーザー+時間窓)
PurviewDataMapOperation_CL
| where TimeGenerated > ago(1d)
| project UTime=TimeGenerated, UUser=UserId_s, UOp=OperationName_s, UIP=ClientIP_s
| join kind=leftouter (
SignInLogs
| where TimeGenerated > ago(1d)
| project STime=TimeGenerated, SUser=UserPrincipalName, SIP=IPAddress, AppDisplayName
) on $left.UUser == $right.SUser and abs(datetime_diff("minute", UTime, STime)) <= 10
| project UTime, UUser, UOp, UIP, SIP, AppDisplayName
Office 365 Management Activity API(Audit.General)の基本フロー
- アプリ登録:適切な権限(例:ServiceHealth.Read は不要、監査には ActivityFeed.Read など)を付与。
- サブスクリプション開始:
POST /api/v1.0/<tenant>/activity/feed/subscriptions/start?contentType=Audit.General - 内容の一覧取得:
GET /api/v1.0/<tenant>/activity/feed/subscriptions/content?contentType=Audit.General&startTime=...&endTime=... - コンテンツのダウンロード:返却されたコンテンツ URI に対して GET。JSON 配列に各イベントが入っています。
返る JSON の Workload が「Microsoft Purview」に相当するイベント、Operation が「Data Map」関連の操作(検索・分類・用語集など)を選択して取り込みましょう。
Microsoft 365 Audit(Premium)の Export API
近リアルタイムで継続的に監査イベントを吐き出せます。SIEM への常時連携や長期保管の要件がある場合に最有力です。取り込み先は Event Hub/Storage/外部 SIEM(例:Microsoft Sentinel 以外)などに合わせて選択します。
API 呼び出し側で仕込むと捗るベストプラクティス
- x-ms-client-request-id の明示設定(GUID):失敗時の突合を秒で終わらせる鍵。
- 呼び出しごとに correlationId をアプリ ログ側にも記録。
- Purview の api-version は更新されます。固定値の更新チェックを CI に組み込む(404/410 増加の早期検知)。
- 429 対策の 指数バックオフ 実装と Retry-After 尊重。
Private Endpoint × リバースプロキシ/ロードバランサ設計の勘所
ネットワーク経路の前提
- Purview のアカウント FQDN は
<name>.purview.azure.com(Private Link 使用時はプライベート DNS ゾーンで解決)。 - アプリケーション ゲートウェイや Nginx/Envoy を前段に置く場合、名前解決と SNI が Private Link の FQDN を指すことが肝要。
推奨アーキテクチャ パターン
| パターン | TLS | 利点 | 注意点 |
|---|---|---|---|
| パススルー(終端なし) | クライアント →(SNI=PL FQDN)→ PE | 最も単純で透過、ヘッダー改変不要 | WAF 機能は使いにくい |
| 終端+再暗号化 | クライアント → 逆プロキシ(終端)→ 再暗号化(SNI=PL FQDN)→ PE | WAF/DLP/レスポンス圧縮などが可能 | Host ヘッダーと SNI を Private Link FQDN に揃えること。証明書と SNI ミスマッチに注意 |
HTTP ヘッダーの扱い
- Host は
<name>.purview.azure.com(Private Link FQDN)を維持。 - X-Forwarded-For を付加して元クライアント IP を継承(Purview 側がすべてを解釈するわけではありませんが、自組織側の逆プロキシ/WAF ログでは完全追跡が可能)。
- X-Forwarded-Proto / X-Forwarded-Host も付与しておくと再現テストが容易。
よくあるハマりポイント
- 逆プロキシが SNI を外した/書き換えたため、Private Endpoint で TLS 失敗。
- HTTP/2 の一部設定で中間機器がフレームを切り詰め、
413/431が増える(ヘッダー/フレームサイズの上限見直し)。 - アイドル タイムアウトが短く、長時間検索や大量応答で 504 が出やすい(Application Gateway のバックエンド タイムアウトを十分に)。
検証チェックリスト
- Private DNS ゾーン(例:privatelink.purview.azure.com)の A レコードが正しく解決されるか。
- 逆引き/ヘルス プローブはインターネットに出ていないか(強制トンネルの誤設定回避)。
- Proxy 経路でも
x-ms-client-request-idが失われていないか。
運用に効く「アラート・レポート」雛形
アラート(KQL ベース)
- 社内許可アドレス帯以外からの高感度操作(用語集更新、スキャン設定変更)
- API 失敗率の急増(5xx/429 が 5 分間で閾値超え)
- レイテンシ悪化(p95 > 2 秒が 10 分継続)
- 大量検索(同一ユーザーの検索が 1 分間に 50 回超)
週次レポート(例)
| メトリクス | 定義 | 意義 |
|---|---|---|
| ユニーク操作ユーザー数 | Purview(UAL)で 1 週間に操作を実施したユーザーの一意数 | 利用状況を把握し、教育や権限棚卸しの判断材料に |
| 上位 API エンドポイント | 診断ログでの RequestUri 上位 10 | 最適化対象の特定 |
| 失敗率(全体/エンドポイント別) | 4xx/5xx 件数 ÷ 総件数 | エラー傾向の早期発見 |
| p95 レイテンシ | DurationMs の 95 パーセンタイル | SLO の監視 |
権限・ロールの最小化(最小権限で安全に)
- Purview 監査検索:Audit logs または View‑Only Audit logs ロール。
- PowerShell(UAL):Exchange 管理権限(監査閲覧に必要な最小)。
- Graph(signIns):AuditLog.Read.All(アプリ権限)や Directory.Read.All。
- Purview のデータ面操作:Data Map のロール(Data Reader/Curator など)を最小付与。
ベストプラクティス:監査の取得・分析基盤は運用アカウント/アプリに分離し、MFA/条件付きアクセスで厳格化します。監査基盤のアクセス自体も監査対象に。
保持期間とコストの考え方
- Unified Audit Log の保持はライセンス(例:Standard/Premium)と監査保持ポリシーで決定。長期保持は Export API/Storage 併用。
- Log Analytics は保持期間(例:30, 90, 180, 365 日…)をワークスペース単位で調整。長期はテーブル単位のアーカイブ/検索モードも検討。
- コスト最適化:不要フィールドの投機的取り込みを避ける、サンプリングをせずに 閾値ベースでアラート化、古いデータは Storage に退避してオンデマンド分析。
トラブルシューティングの型
500 エラーが増えたとき
- 診断ログで
HttpStatusCode= 500 を時間軸にプロット(特定 URI/テナント横断かを判別)。 CorrelationIdで該当リクエストをアプリ ログと突合。- UAL に操作失敗があるか確認(
ResultStatus)。 - 前段のプロキシ/ゲートウェイ ログで同時間帯の 4xx/5xx を確認(タイムアウトやサイズ制限など)。
「誰がどこから?」が不明瞭なとき
- UAL の
ClientIPと診断ログのCallerIpAddressを比較。 - Graph の signIns(
ipAddress)をユーザー+時刻でジョイン。 - NAT 配下で IP が収束する場合、逆プロキシやファイアウォールのアクセス ログで X-Forwarded-For を確認。
サンプル:プログラムからの取得コード断片
PowerShell(Unified Audit Log)
Connect-ExchangeOnline
$from = (Get-Date).AddDays(-1)
$to = Get-Date
$ops = @("PurviewDataMapOperation") # 実際の表示名を使用
$result = Search-UnifiedAuditLog -StartDate $from -EndDate $to -Operations $ops -ResultSize 5000
$result | Export-Csv -Path ".\purview-ual.csv" -NoTypeInformation -Encoding UTF8
cURL(Management Activity API の概念例)
# サブスクリプション開始(テナント ID, トークンは既取得と仮定)
curl -X POST \
"https://manage.office.com/api/v1.0/<tenant-id>/activity/feed/subscriptions/start?contentType=Audit.General" \
-H "Authorization: Bearer <access_token>"
# コンテンツ一覧取得
curl -s
"[https://manage.office.com/api/v1.0/<tenant-id>/activity/feed/subscriptions/content?contentType=Audit.General&startTime=2025-10-26T00:00:00Z&endTime=2025-11-02T00:00:00Z](https://manage.office.com/api/v1.0/<tenant-id>/activity/feed/subscriptions/content?contentType=Audit.General&startTime=2025-10-26T00:00:00Z&endTime=2025-11-02T00:00:00Z)"
-H "Authorization: Bearer <access_token>" > list.json
# 各 contentUri を GET して JSON を収集
KQL(Purview 操作ログの短時間サマリ)
PurviewDataMapOperation_CL
| where TimeGenerated > ago(1d)
| summarize ops=count(), users=dcount(UserId_s), targets=dcount(ObjectId_s)
KQL(API 呼び出しの発信元 Top /24)
PurviewDataPlaneRequests_CL
| where TimeGenerated > ago(7d)
| extend Subnet24 = strcat(split(CallerIpAddress_s, ".")[0], ".", split(CallerIpAddress_s, ".")[1], ".", split(CallerIpAddress_s, ".")[2], ".0/24")
| summarize count() by Subnet24
| top 20 by count_ desc
公式ガイドを読む時の観点(リンクなし要約)
- 「監査のオン/オフ」:Unified Audit Log の取り込みを有効化/確認する手順。
- 「監査の検索」:Purview 監査ソリューションでの検索 UI と、検索式/エクスポート方法。
- 「Data Map 監査・診断ログ」:Purview アカウントの診断ログ カテゴリ、Log Analytics 送信、フィールド説明。
- 「Application Gateway + Private Link の構成」:SNI/Host ヘッダーの扱い、WAF と再暗号化、プライベート DNS の作法。
製品の呼称やレコード種別は更新されることがあります。UI に表示される名前・フィールドを基準に読み替えてください。
よくある質問(FAQ)
Q. Microsoft Graph の directoryAudits で Purview の操作は取れますか?
A. directoryAudits は主にテナントのディレクトリ(Entra ID)に対する操作です。Purview の操作は原則、Unified Audit Log(Purview 監査)で取得し、必要に応じてサインイン ログ(auditLogs/signIns)と突合します。
Q. 監査ログの時刻が実際より遅れて見えるのは?
A. 取り込み遅延が発生する場合があります。SLA はリアルタイムではないため、秒単位の相関が必要な分析は診断ログ(Azure Monitor)を併用してください。
Q. Private Endpoint 経由で IP が社内 NAT になる。実ユーザー IP は残せる?
A. UAL の ClientIP は Microsoft 側の観測 IP となるため NAT で収束します。逆プロキシ/WAF のアクセス ログに X-Forwarded-For を残し、サインイン ログと突合するのが実務的です。
Q. どのログをどこに送ればよいか迷う
A. 即時分析やアラートは Log Analytics、長期保管は Storage、外部 SIEM 連携やストリーム処理は Event Hub が基本指針です。すべてに送っても構いませんが、コスト最適化の観点で保持日数や対象カテゴリを選別しましょう。
まとめ
Purview でのアクセス/API 呼び出しの監査は、Unified Audit Log(論理操作)× 診断ログ(HTTP 事実)× サインイン(発信元)の三位一体で設計するのが近道です。x-ms-client-request-id と相関 ID を活用してログ同士をつなぎ、KQL とアラートで「異常が起きたときにすぐ分かる」体制を作りましょう。Private Endpoint の前段にプロキシ/ロードバランサを置く場合は、SNI/Host の整合と X-Forwarded-* の継承を徹底し、ネットワーク層の証跡も欠かさず残してください。これらを組み合わせれば、「誰が・いつ・どこから・何をしたか」を自信を持って説明できる監査基盤が完成します。

コメント