Entra ID(旧Azure AD)SAMLエンタープライズアプリのユーザー属性とクレームをMicrosoft Graph/PowerShellで取得する方法(claimsMappingPoliciesとclaimsPolicy)

Entra ID(旧 Azure AD)のSAMLエンタープライズアプリで「ユーザー属性とクレーム(Attributes & Claims)」を設定しているのに、Microsoft Graph/PowerShellで取得しようとすると空になる――この“あるある”は、取得先のAPIが違うことが原因です。この記事では、claimsMappingPoliciesとclaimsPolicy(Custom Claims Policy)の違いを整理し、読み取り・棚卸しの具体例までまとめます。

目次

現象の整理:「ポータルでは見えるのに、Get-Mg…は空」

SAMLのエンタープライズアプリ(サービス プリンシパル)で、Microsoft Entra 管理センター(旧 Azure ポータル)上の「ユーザー属性とクレーム(Attributes & Claims)」には設定が見えているのに、PowerShell/Microsoft Graph で次のように取得すると空(0件)になることがあります。

  • PowerShell(例):Get-Mg(Beta)ServicePrincipalClaimMappingPolicy
  • Graph REST(例):GET /servicePrincipals/{id}/claimsMappingPolicies

スコープに Application.Read.All / Policy.Read.All を付けているのに空で、「APIが壊れているのでは?」と疑ってしまうパターンです。

最初に結論:claimsMappingPolicies は「割り当てた Claims Mapping Policy」しか返さない

claimsMappingPolicies(PowerShellの Get-Mg(Beta)ServicePrincipalClaimMappingPolicy など)が返すのは、サービス プリンシパルに“割り当てられている” claimsMappingPolicy オブジェクトだけです。ドキュメント上も「assigned to a servicePrincipal(サービス プリンシパルに割り当てられたもの)」を列挙すると明記されています。citeturn4view0turn4view1

つまり、ポータルの「ユーザー属性とクレーム」で設定していても、それが Claims Mapping Policy として割り当てられていない構成なら、claimsMappingPolicies が空になるのは正常です。これはMicrosoft Q&Aでも同じ整理で回答されています。citeturn8view0

混乱の原因:「ユーザー属性とクレーム」には“2つの管理系統”がある

現在のEntra IDでは、トークン(SAML/OIDC/OAuthなど)に出すクレームを管理する仕組みが大きく2系統あります。

系統Graph/PowerShellの主な取得先特徴よくある用途
Claims Mapping PolicyGET /servicePrincipals/{id}/claimsMappingPolicies Get-MgServicePrincipalClaimMappingPolicy / Get-MgBetaServicePrincipalClaimMappingPolicyポリシー(claimsMappingPolicy)を作成してサービス プリンシパルに割り当てる方式。 割り当てたものだけ取得される。citeturn4view0turn4view1複数アプリで同じポリシーを使い回す、変換ルールを含む高度な定義を管理する。
Custom Claims Policy(Preview)GET /beta/servicePrincipals/{id}/claimsPolicy Get-MgBetaServicePrincipalClaimPolicyサービス プリンシパルに1つだけ紐づく“カスタムクレーム”のポリシー。 管理センターのクレーム設定(claims customization)と同じ基盤を使う、という位置づけ。citeturn2view4turn2view5 Graphの /beta で提供され、仕様変更の可能性あり。citeturn2view0turn2view1ポータルで編集している「ユーザー属性とクレーム」をAPIで読み取り・更新したい(ただしbeta前提)。

この“二系統”を区別せずに「ポータルで見える=claimsMappingPoliciesで取れるはず」と考えると、空結果でハマります。

Claims Mapping Policy を読み取る:claimsMappingPolicies(v1.0でも可)

まずは、従来からある Claims Mapping Policy 側です。ここで取得できるのは「割り当て済みポリシーのみ」ですが、その前提を理解しているなら棚卸しに非常に向いています。citeturn4view0turn4view1

Graph REST での取得(割り当て済みのみ)

サービス プリンシパルは Object ID(id)でも、Application (Client) ID(appId)でも指定できます。citeturn4view1

GET https://graph.microsoft.com/v1.0/servicePrincipals/{id}/claimsMappingPolicies
GET https://graph.microsoft.com/v1.0/servicePrincipals(appId='{appId}')/claimsMappingPolicies

必要な権限の組み合わせはドキュメントの「Permissions」に従って付与してください(最小権限の組み合わせが提示されています)。citeturn4view1

PowerShell(Microsoft Graph PowerShell SDK)での取得

Get-MgBetaServicePrincipalClaimMappingPolicy の説明も「assigned to a servicePrincipal」と明記されており、このコマンドが“ポータルの表示そのもの”を返すわけではないことが読み取れます。citeturn4view0

# 例:割り当て済みの claimsMappingPolicy を取得
Import-Module Microsoft.Graph.Applications

Connect-MgGraph -Scopes "Policy.Read.All","Application.ReadWrite.All"

$spId = "<サービスプリンシパルのObject ID>"
$assigned = Get-MgServicePrincipalClaimMappingPolicy -ServicePrincipalId $spId

$assigned | Select-Object Id, DisplayName, Definition

claimsMappingPolicy は定義(definition)がJSON文字列として入るため、エクスポート時はそのままJSONで保持する運用が楽です(CSVに入れる場合はエスケープに注意)。

「空だった」ケースでまず確認すること

確認ポイント見方判断
claimsMappingPolicy を割り当てているかGraph/PSで .../claimsMappingPolicies を叩く0件なら「割り当てがない」可能性が高い(正常)。citeturn4view0turn4view1
そもそも狙っているのは“どの設定”かポータルの「ユーザー属性とクレーム」なのか、ポリシー割り当てなのかポータル表示=claimsMappingPolicies とは限らない

ポータルの「ユーザー属性とクレーム」を読み取る:claimsPolicy(Custom Claims Policy / beta)

ここがアップデートの要点です。以前は「ポータルで設定している『ユーザー属性とクレーム』をそのままGraph/PowerShellで読むのは不可」とされていましたが、後に Custom Claims Policy(Preview) という形で、Graph(beta)から取得できるルートが整備されています。citeturn8view0turn2view4turn2view5

公式の整理としても、Custom Claims Policy(Preview)は、管理センターの claims customization(SAMLのAttributes & Claimsを含む)と「同じ基盤(same underlying policy)」を使い、管理場所をポータルとGraph/PowerShellで行き来できる、という位置づけです。citeturn2view4

Graph REST(beta)での取得

GET https://graph.microsoft.com/beta/servicePrincipals/{servicePrincipalId}/claimsPolicy

このエンドポイントは /beta 配下で提供され、ドキュメントでも「betaは変更され得る」「本番利用は推奨されない」と明示されています。運用ルール(検証→適用→監視)を決めて扱うのが安全です。citeturn2view0turn2view1

レスポンスの中核は claims 配下で、どのクレームをどのソースから出すか、といった構成が入ります(includeBasicClaimSet などの制御もここ)。citeturn4view6turn2view5

PowerShell での取得(Microsoft.Graph.Beta)

PowerShellでは Microsoft.Graph.Beta.Applications の Get-MgBetaServicePrincipalClaimPolicy を使うのが分かりやすいです。必要権限として Policy.Read.ApplicationConfiguration(または上位権限)が提示されています。citeturn6view1

# 例:Custom Claims Policy(claimsPolicy)を取得
Install-Module Microsoft.Graph.Beta -Scope CurrentUser
Import-Module Microsoft.Graph.Beta.Applications

Connect-MgGraph -Scopes "Policy.Read.ApplicationConfiguration"

$spId = "<サービスプリンシパルのObject ID>"
$policy = Get-MgBetaServicePrincipalClaimPolicy -ServicePrincipalId $spId

$policy | Select-Object Id, IncludeBasicClaimSet, IncludeApplicationIdInIssuer, AudienceOverride, Claims

また、Graph SDKは既定で v1.0 を使うため、betaのAPIや型を扱う場合は「betaを使う」前提が必要です(この注意は公式にも明記されています)。citeturn5view0

「GETしたら404/空」になったときの考え方

claimsPolicy は“常に存在する設定”というより「Custom Claims Policy を持っている場合に取得できる設定」です。次の切り分けで原因を特定しやすくなります。

症状可能性が高い原因対処の方向性
.../claimsMappingPolicies が空Claims Mapping Policy を割り当てていない必要ならポリシーを作成・割り当てる(割り当てなければ空は正常)。citeturn4view0turn4view1
.../claimsPolicy が取得できない/存在しないCustom Claims Policy(Preview)がまだ作成されていない、またはbetaの取り扱い/権限が不足権限(Policy.Read.ApplicationConfiguration等)とbeta利用を再確認。citeturn6view1turn5view0
ポータルでクレーム編集できない/表示がロックされるClaims Mapping Policy を利用している場合、ポータルのclaims customizationと競合することがあるどちらの方式で管理するかを統一する(後述)。citeturn2view4turn4view3

どっちを読めばいい?目的別のおすすめ

「読み取りたいもの」が何かで、正解のAPIが変わります。迷ったら、次の基準が実務では一番ブレません。

目的おすすめ理由
“ポータルのAttributes & Claims相当”を取りたい(SAMLのクレーム棚卸し)claimsPolicy(Custom Claims Policy / beta)ポータルのclaims customizationと同じ基盤で管理できる整理が公式に示されているため。citeturn2view4turn2view5
複数アプリで同じ定義を使い回す/ポリシーで統制したいclaimsMappingPolicies1つのclaimsMappingPolicyを複数アプリへ割り当てる運用に向く。citeturn2view4turn4view3
betaを避けたい(仕様変更リスクを取りたくない)claimsMappingPoliciesclaimsMappingPolicies は v1.0 のAPI/コマンドが用意されている。citeturn4view0turn4view1

実装例:全SAMLエンタープライズアプリを棚卸ししてCSV化する

「どのアプリが何を出しているか」を一覧化したい場合は、(1) SAMLアプリの抽出、(2) claimsMappingPolicies と claimsPolicy の両方を見に行く、(3) 見つかった方を“そのアプリの実設定”として記録、の順に進めるのが実務的です。

ステップ:SAMLアプリ(サービス プリンシパル)を集める

サービス プリンシパルには preferredSingleSignOnMode というプロパティがあり、値として password / saml / external / oidc が定義されています。citeturn4view2

ただし実データとしては、古いアプリや一部のケースで preferredSingleSignOnMode が空になることがある、という報告もあるため、これだけに依存して抽出すると漏れる可能性があります。citeturn7view0

そのため、最初は以下のように「まずは取れるものを取る(= preferredSingleSignOnMode が saml のもの)」を基本にしつつ、漏れが疑われる場合はタグ・命名規則・管理対象アプリの台帳など、別の情報源と突き合わせるのがおすすめです。

棚卸しスクリプト例(読み取り専用)

以下は「claimsMappingPolicies(v1.0)」と「claimsPolicy(beta)」を両方確認して、取れた方をCSVに落とす例です。環境によって必要スコープが変わるため、ドキュメントのPermissionsに従って調整してください。citeturn4view1turn6view1

# --- モジュール ---
Import-Module Microsoft.Graph.Applications
Import-Module Microsoft.Graph.Beta.Applications

# --- 認証(読み取りが目的の例。必要に応じて追加) ---
Connect-MgGraph -Scopes @(
  "Application.Read.All",
  "Policy.Read.All",
  "Policy.Read.ApplicationConfiguration"
)

# --- サービスプリンシパル取得(まずはsamLのものを優先) ---
$servicePrincipals = Get-MgServicePrincipal -All -Property Id,AppId,DisplayName,PreferredSingleSignOnMode

$samlSps = $servicePrincipals | Where-Object { $_.PreferredSingleSignOnMode -eq "saml" }

# --- 結果格納 ---
$rows = foreach ($sp in $samlSps) {

  # 1) Claims Mapping Policy(割り当てが無ければ空)
  $cmp = $null
  try {
    $cmp = Get-MgServicePrincipalClaimMappingPolicy -ServicePrincipalId $sp.Id -ErrorAction Stop
  } catch {
    $cmp = $null
  }

  # 2) Custom Claims Policy(beta)
  $ccp = $null
  try {
    $ccp = Get-MgBetaServicePrincipalClaimPolicy -ServicePrincipalId $sp.Id -ErrorAction Stop
  } catch {
    $ccp = $null
  }

  # JSONとして保持(CSVに入れるため軽く整形)
  $cmpJson = if ($cmp) { ($cmp | ConvertTo-Json -Depth 20) } else { "" }
  $ccpJson = if ($ccp) { ($ccp | ConvertTo-Json -Depth 50) } else { "" }

  [pscustomobject]@{
    DisplayName               = $sp.DisplayName
    ServicePrincipalId        = $sp.Id
    AppId                     = $sp.AppId
    PreferredSingleSignOnMode = $sp.PreferredSingleSignOnMode

    ClaimsMappingPolicyCount  = @($cmp).Count
    HasClaimsPolicy           = [bool]$ccp

    ClaimsMappingPoliciesRaw  = $cmpJson
    ClaimsPolicyRaw           = $ccpJson
  }
}

# --- 出力 ---
$rows | Export-Csv -Path ".\entra-saml-claims-inventory.csv" -NoTypeInformation -Encoding UTF8

棚卸しの目的が「人が読むこと」なら、CSVの列に“生JSON”を入れるより、別途 .json をアプリごとに吐き出す(Out-File で保存)運用の方が保守しやすいです。逆に「差分監視(Gitで管理)」が目的なら、JSON保存の方が適しています。

落とし穴:appId と Service Principal の Id を混同しない

クレーム設定の取得は、基本的に サービス プリンシパル(Enterprise Application)のObject ID を使って行います。claimsMappingPolicies は appId でも指定できる一方、運用上は “同名アプリ” や “マルチテナント/複数インスタンス” で混乱しやすいので、棚卸しではObject ID(servicePrincipalId)基準が安全です。citeturn4view1

運用のコツ:方式を混在させない(混ざると調査が難しくなる)

公式の整理では、Claims Mapping Policy と Custom Claims Policy(Preview)はいずれも「クレームをカスタマイズする手段」ですが、運用上はどちらを“正”として管理するかを決めておくことが重要です。Claims Mapping Policy を採用すると、ポータルの claims customization での編集と競合し得る(= UI側で編集できない/期待通りに反映されない)という注意もあります。citeturn2view4turn4view3

おすすめの決め方は以下です。

  • アプリごとに個別要件が強く、ポータル運用も残したい → Custom Claims Policy(Preview) を前提にAPI連携(betaのリスクは許容する)citeturn2view4turn2view5
  • 同一ルールを複数アプリへ横展開したい、IaCで統制したい → Claims Mapping Policy を採用し、割り当てと定義をコード管理 citeturn4view3turn4view1

補足:ポータルでの操作場所(読者の確認用)

SAMLトークンのクレーム(Attributes & Claims)は、管理センター上では次の手順で確認・編集します。citeturn5view1

  • Entra ID → Enterprise apps → 対象アプリ → Single sign-on → Attributes & Claims

「ポータルの画面とAPI結果が一致しない」場合は、まず “どの方式(claimsMappingPolicies / claimsPolicy)で管理している状態か” を確認すると、原因が最短で特定できます。

まとめ

  • claimsMappingPolicies は サービス プリンシパルに割り当てた Claims Mapping Policy だけを返すので、割り当てがなければ空でも正常。citeturn4view0turn4view1
  • ポータルの「ユーザー属性とクレーム」をAPIで扱いたい場合は、Custom Claims Policy(Preview)を /beta/servicePrincipals/{id}/claimsPolicy で取得する整理が有力。citeturn2view4turn2view5turn5view0
  • 棚卸しは、両方のエンドポイントを確認して「どちらで管理しているか」を記録すると後工程(移行、標準化、監査)が一気に楽になる。

この記事を書いた人

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

コメント

コメントする

目次