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 |
| Application | Not 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 などで中身を確認し、以下のポイントをチェックするとよいでしょう。
| チェック項目 | 期待値 |
|---|---|
| tokentype | Bearer |
| aud | https://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 で正しく動く状態を作っておくと、トークンの中身比較などがしやすくなります。
- Graph Explorer にアクセスし、対象テナントのアカウントでサインイン。
- 左側の HTTP メソッドを
GET、URL をhttps://graph.microsoft.com/v1.0/security/labels/retentionLabelsに設定。 - Modify permissions ボタンをクリックし、
RecordsManagement.Read.All(またはRecordsManagement.ReadWrite.All)を探して 同意を与える。 - 「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 ラベル以外にもさまざまなオブジェクトがあります。その中には、アプリケーション許可をサポートするものも存在します。
| API | Application 許可 | 備考 |
|---|---|---|
/security/labels/retentionLabels | Not supported | 本記事の主役。委任のみ対応。 |
/security/labels/citations/{id} | RecordsManagement.Read.All などがサポートされる | citationTemplate を扱う API。 |
/security/labels/retentionLabels/{id}/eventType | Application 許可あり | 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 の credential | ClientSecretCredential を使っていないか | 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 の情報ガバナンスをスクリプトから安全かつ確実に操作できるようになります。

コメント