Microsoft GraphでRetention Label取得時に401 Unauthorizedや500エラーになる原因と対処法【/security/labels/retentionLabels】

Microsoft 365 の自動化で Microsoft Graph の Retention ラベル API を叩いたとき、「Graph Explorer では成功するのに、PowerShell や Python のスクリプトだと 401 Unauthorized や謎の 500 エラーになる」という相談が増えています。本記事では、その原因となる認証方式と権限設定の落とし穴を整理しつつ、/security/labels/retentionLabels を確実に呼び出すための実践的な手順を詳しく解説します。

目次

Microsoft GraphでRetention Label取得エラーが起きる典型パターン

現場でよく見かけるパターンを、まずはざっくり整理します。

  • Graph Explorer で /security/labels/retentionLabels を実行すると正常にレスポンスが返ってくる。
  • 同じテナント・同じユーザーのつもりで、PnP PowerShell や Microsoft Graph PowerShell、あるいは Python(クライアント資格情報フロー)から叩くと 401 Unauthorized。
  • 環境によっては、500 UnknownError(DataInsights in EOP) が返ってくるケースもある。

典型的には次のような構図になっています。

ツール / コード認証方式結果
Graph Explorer委任(ユーザーとしてサインイン)成功(ラベル一覧取得できる)
Microsoft Graph PowerShell(証明書接続)アプリのみ(クライアント資格情報)401 または 500
PnP PowerShell(アプリ登録 + シークレット / 証明書)アプリのみ401 または 500
Python + ClientSecretCredentialアプリのみ500 UnknownError / DataInsights エラーなど

同じテナントに対して同じ API を叩いているのに挙動が違う理由は、「どの種類のアクセストークンを使っているか(委任かアプリケーションか)」に集約されます。

Retention Label APIと権限モデルの前提知識

/security/labels/retentionLabels が属する機能

/security/labels/retentionLabels は Microsoft Graph の security 名前空間に属する API で、Microsoft Purview(旧 Microsoft 365 コンプライアンス)の Records Management(レコード マネジメント) 機能に紐づいています。Exchange / SharePoint などのコンテンツに対して、保持期間や削除タイミングを制御する「保持ラベル(Retention ラベル)」の定義そのものを取得する API です。

このため、単純なユーザー情報やグループ情報の API よりも厳しめの権限設計になっています。

公式ドキュメントの権限一覧

Microsoft Graph の公式ドキュメントで、Retention ラベル一覧 API(v1.0)の権限を確認すると、次のように記載されています。

Permission type許可される権限
Delegated(組織アカウント)RecordsManagement.Read.All / RecordsManagement.ReadWrite.All
Delegated(個人 Microsoft アカウント)Not supported
ApplicationNot supported

つまり、この API は 委任された権限(Delegated)でのみ利用可能であり、アプリケーション許可(Application)はサポートされていません。これが最大のポイントです。

以前のコミュニティ記事や一部ブログでは、「アプリケーション許可でも RecordsManagement.Read.All が使える」と書かれているものもありますが、現在の v1.0 ドキュメントでは明確に Application: Not supported と示されています。

委任とアプリケーションの違いがエラーの原因

Graph Explorer では、ブラウザ上でユーザーとしてサインインし、委任された権限でアクセストークンが発行されます。一方、多くの運用スクリプトは「アプリ登録 + シークレット or 証明書」でトークンを取得する クライアント資格情報フローを使っており、これは アプリケーション許可になります。

Retention ラベル API は「委任専用」なので、同じ RecordsManagement.Read.All という名前の権限でも、Delegated と Application では動作が全く異なります。Application 側の RecordsManagement.Read.All は API 自体がサポートしていないため、トークンに権限が入っていても 401 や 500 を返してしまうのです。

エラーコード別に原因を切り分ける

401 Unauthorized の場合

401 Unauthorized は、「トークンが正しくない」か「必要なスコープが付与されていない」場合に返ります。Retention ラベル API の場合、ほぼ次のいずれかです。

  • 発行したトークンが アプリケーション許可であり、API 側がサポートしていない。
  • 委任トークンだが、RecordsManagement.Read.All または RecordsManagement.ReadWrite.All が scp クレームに含まれていない。
  • aud(Audience)が https://graph.microsoft.com ではない。

トークンは jwt.ms などで中身を確認し、以下のポイントをチェックするとよいでしょう。

チェック項目期待値
tokentypeBearer
audhttps://graph.microsoft.com
scp(委任)RecordsManagement.Read.All または RecordsManagement.ReadWrite.All を含む
roles(アプリケーション)Retention ラベル API ではサポートされない(= 使ってはいけない)

特に重要なのは scp に RecordsManagement.Read.All があるか です。これが無ければ、Graph Explorer では動いてもスクリプトでは必ず失敗します。

500 UnknownError(DataInsights in EOP)の場合

クライアント資格情報(アプリのみ)で Retention ラベル API を呼び出したときに、次のような 500 エラーが返る事例が Microsoft Q&A などで報告されています。

500 - {"error":{"code":"UnknownError","message":"{"ErrorCode":"DataInsightsRequestError","Message":"Failed to contact DataInsights in EOP ..."} ... }}

このメッセージ自体は内部のコンポーネント名(DataInsights, EOP)が出てくるため難解ですが、実質的には「このパスではアプリケーション許可が扱えない」と読めます。公式な修正策は提示されておらず、アプリのみで叩くのではなく委任トークンを使うことが唯一の現実解です。

v1.0エンドポイントを使うべき理由

Retention ラベル API は当初 /beta で提供されていましたが、現在は v1.0 にも昇格しています。

エンドポイント用途備考
/v1.0/security/labels/retentionLabels本番利用仕様が安定。Application は非対応。
/beta/security/labels/retentionLabels検証・新機能の確認仕様変更の可能性あり。本番利用非推奨。

本番コードでは必ず v1.0 を利用し、/beta は「新しいプロパティの挙動を試す」程度の用途に留めるのが安全です。

典型的な HTTP 要求は以下の通りです。

GET https://graph.microsoft.com/v1.0/security/labels/retentionLabels
Authorization: Bearer <アクセストークン>

特定ラベルだけ取得したい場合は、ラベル ID(GUID)を指定します。

GET https://graph.microsoft.com/v1.0/security/labels/retentionLabels/{retentionLabelId}

Graph Explorerでの確認手順

まずは Graph Explorer で正しく動く状態を作っておくと、トークンの中身比較などがしやすくなります。

  1. Graph Explorer にアクセスし、対象テナントのアカウントでサインイン。
  2. 左側の HTTP メソッドを GET、URL を https://graph.microsoft.com/v1.0/security/labels/retentionLabels に設定。
  3. Modify permissions ボタンをクリックし、RecordsManagement.Read.All(または RecordsManagement.ReadWrite.All)を探して 同意を与える。
  4. 「Run query」をクリックしてレスポンスを確認。

ここで成功するのにスクリプトでは失敗する場合、「Graph Explorer で使われているトークン」と「スクリプトで取っているトークン」の違いを確認するのがトラブルシュートの近道です。

Microsoft Graph PowerShellでの正しい呼び出し方

Retention ラベルを PowerShell から扱うなら、まずは Microsoft Graph PowerShell SDK を素直に使うのが最も簡単です。

1. v1.0プロファイルを選択

Import-Module Microsoft.Graph.Security

Select-MgProfile -Name "v1.0"

2. 委任権限で Connect-MgGraph

ここが最重要ポイントです。証明書 / シークレットでのアプリのみ接続ではなく、-Scopes を指定して 対話サインインを行います。

Connect-MgGraph -Scopes "RecordsManagement.Read.All"
# 更新も行いたい場合は
# Connect-MgGraph -Scopes "RecordsManagement.ReadWrite.All"

初回は管理者による同意が必要です。承認されると、ユーザーの委任トークンに RecordsManagement.Read.All が含まれるようになります。

3. ラベル一覧・個別取得

あとは SDK のコマンドを実行するだけです。

# 一覧取得
Get-MgSecurityLabelRetentionLabel

# 特定ラベルだけ取得
Get-MgSecurityLabelRetentionLabel -RetentionLabelId "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

アプリのみ(ClientId + 証明書)の接続はNG

次のような書き方は、Retention ラベル API では動きません。

# これはアプリケーション許可になるので NG
Connect-MgGraph -ClientId "<アプリID>" -TenantId "<テナントID>" `
  -CertificateThumbprint "<証明書拇印>"

この方式で取得したトークンには roles クレームに RecordsManagement.Read.All が入っていたとしても、Retention ラベル API は Application 許可をサポートしていないため 401 や 500 を返します。

PnP PowerShellから利用したい場合の考え方

PnP PowerShell は主に SharePoint Online / Teams の運用向けツールですが、Get-PnPAccessToken で取得したトークンを使って Graph REST API を叩くこともできます。

Retention ラベル API を呼び出す場合、アプリのみ接続は避けて、対話型(委任)で接続する必要があります。

対話接続でトークンを取得する例

# 対話サインイン(Azure AD アプリに RecordsManagement.Read.All を追加しておく)
Connect-PnPOnline -Url "https://<テナント名>.sharepoint.com" -Interactive

# Graph 用アクセストークンを取得
$token = Get-PnPAccessToken

# Retention ラベル一覧を REST で取得
$headers = @{
  "Authorization" = "Bearer $token"
}

Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/security/labels/retentionLabels" `
  -Headers $headers -Method Get

実務上は、PnP から無理に呼ぶよりも、Retention ラベル周りだけは Microsoft Graph PowerShell SDK に任せた方が管理しやすいケースも多いです。

Python(msgraph-core + azure-identity)での実装例

Python で Retention ラベルを取得する場合も、ClientSecretCredential(クライアント資格情報)ではなく、DeviceCodeCredential や InteractiveBrowserCredential を使った委任方式が必須です。

Device Code フローを使う例

事前に Azure AD アプリを「パブリック クライアント」に設定し、RecordsManagement.Read.All の委任権限を追加しておきます。

from msgraph.core import GraphClient, APIVersion
from azure.identity import DeviceCodeCredential

tenant_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
client_id = "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"

credential = DeviceCodeCredential(
    tenant_id=tenant_id,
    client_id=client_id
)

# 委任権限なので .default ではなく、API スコープ名を指定
scopes = ["https://graph.microsoft.com/RecordsManagement.Read.All"]

client = GraphClient(
    credential=credential,
    scopes=scopes,
    api_version=APIVersion.v1
)

response = client.get("/security/labels/retentionLabels")
print(response.json())

ここで scopes=["https://graph.microsoft.com/.default"] のように指定すると、クライアント資格情報フロー前提のスコープ解決になり、Application 許可でトークンが発行されてしまいます。その結果、Retention ラベル API では 401 や 500 UnknownError になります。

クライアント資格情報フローが使えない場合の設計案

「どうしても完全なバックグラウンドジョブとして動かしたい」という要件に対しては、残念ながら現時点で Retention ラベル API にアプリケーション許可はありません。

代案としては次のようなものがあります。

  • 人がサインインする前提で、管理者 PC から定期的にスクリプトを実行する。
  • Web アプリでユーザーにサインインしてもらい、On-Behalf-Of フローでバックエンドから Graph を呼び出す(ただしユーザーコンテキストが必要)。
  • アプリのみで取得できる別 API(たとえば一部の security ラベル関連 API)は活用しつつ、Retention ラベルだけは別処理と割り切る。

レガシーな手段として「サービスアカウントの ID/PASS をどこかに保存し、ROPC でトークンを取得する」といった方法もありますが、セキュリティ上強く非推奨です。

Retention Label APIと他の security APIの違い

Graph の security 名前空間には、Retention ラベル以外にもさまざまなオブジェクトがあります。その中には、アプリケーション許可をサポートするものも存在します。

APIApplication 許可備考
/security/labels/retentionLabelsNot supported本記事の主役。委任のみ対応。
/security/labels/citations/{id}RecordsManagement.Read.All などがサポートされるcitationTemplate を扱う API。
/security/labels/retentionLabels/{id}/eventTypeApplication 許可ありretentionEventType を取得する API。

つまり、「同じ RecordsManagement.* 系だから全部アプリのみでいけるはず」と思い込むとハマります。API ごとにドキュメントの Permission テーブルを確認することが非常に重要です。

よくあるハマりどころチェックリスト

最後に、Retention ラベル取得でつまずいたときに見直すべきポイントをチェックリスト形式でまとめます。

項目確認内容OK の状態
エンドポイント/beta ではなく /v1.0 を使っているかhttps://graph.microsoft.com/v1.0/security/labels/retentionLabels
権限の種類アプリケーション許可で呼び出していないか委任権限(Delegated)のみを使用
権限名正しい RecordsManagement.* を付与しているかRecordsManagement.Read.All または RecordsManagement.ReadWrite.All
トークンのクレームscp に RecordsManagement.* が入っているかscp クレーム内に該当スコープを確認
audトークンの受信者が Graph かaud=https://graph.microsoft.com
Graph PowerShell 接続方式-ClientId や証明書で接続していないか-Scopes を指定した対話接続のみ利用
Python の credentialClientSecretCredential を使っていないかDeviceCodeCredential / InteractiveBrowserCredential 等を利用

まとめ

Retention ラベル API(/security/labels/retentionLabels)で 401 Unauthorized や 500 UnknownError が発生する原因の大半は、「アプリのみで呼び出そうとしている」または「委任スコープの不足」にあります。

  • Retention ラベル API は 委任権限専用で、Application 許可は公式に「Not supported」。
  • Graph Explorer で動くのにスクリプトで失敗する場合、ほぼ必ず「Explorer は委任、スクリプトはアプリのみ」という認証方式の違いが原因。
  • 本番では v1.0 エンドポイント(/v1.0/security/labels/retentionLabels)を利用し、RecordsManagement.Read.All または RecordsManagement.ReadWrite.All を付与して Connect-MgGraph / DeviceCodeCredential などで委任トークンを取得する。
  • 500 UnknownError(DataInsights in EOP)は、アプリのみで呼んだときの「仕様上の行き止まり」のような挙動で、実質的な解は 委任に切り替えること。

上記のポイントを押さえておけば、「Graph Explorer では動くのにスクリプトだけ動かない」というよくある罠を避け、Microsoft 365 の情報ガバナンスをスクリプトから安全かつ確実に操作できるようになります。

この記事を書いた人

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

コメント

コメントする

目次