オンプレミスActive Directoryで作ったユーザーをMicrosoft Entra ID(旧Azure AD)へ同期していると、「テナント内の全ユーザー」と「各ユーザーに割り当てられた管理者ロール」をまとめて棚卸ししたくなる場面が必ず出てきます。本記事ではMicrosoft Graph PowerShellでユーザー一覧とロール情報を取得し、ロールなしのユーザーも含めてExcelで扱えるCSVへ出力する方法を、つまずきやすいポイント込みで解説します。
なぜ「ユーザー一覧+ロール一覧」をExcelに出す必要があるのか
Entra IDには、社内の同期ユーザー(Member)だけでなく、外部招待のゲストユーザー(Guest)や、運用用のサービスアカウントが混在します。さらに管理者ロール(例:全体管理者、ユーザー管理者、条件付きアクセス管理者など)が付くと、影響範囲は一気に広がります。
そのため、次のような目的で「全ユーザー×割り当てロール」をExcelで可視化できると、運用が一段ラクになります。
- 監査・セキュリティ点検(最小権限の原則/棚卸し)
- 退職者・委託先アカウントの権限確認
- ロール付与の申請・承認プロセスの見直し
- ロール変更の差分管理(いつ誰の権限が増えたか)
結論:Microsoft Graph PowerShellで「ユーザー」と「ロールメンバー」を集めて結合する
残念ながら、Entra IDには「全ユーザーを返しつつ、各ユーザーのロール割り当ても同時に返す」一発APIが用意されていません。そこで現実的には、次の2つを取得して突き合わせます。
| 取得するもの | 主な取得方法 | 得られる内容 |
|---|---|---|
| ユーザー一覧(同期+ゲスト含む) | Get-MgUser -All | DisplayName、UPN、UserType、同期情報など |
| ロールとメンバー | Get-MgDirectoryRole / Get-MgDirectoryRoleMember | 「どのロールに誰が入っているか」 |
この記事のスクリプトはこの方針で、ロールがないユーザーも必ず1行出力してExcelでフィルタできる状態にします。
なお、本記事で扱うのはEntra ID(ディレクトリ)側の管理者ロールです。AzureサブスクリプションのRBACロール(例:共同作成者、所有者)や、Microsoft 365のアプリ固有ロールは別の仕組みで、取得エンドポイントや集計方法が変わります。まずは「ディレクトリの管理者ロール棚卸し」を完成させるのが近道です。
事前準備:必要な権限と実行環境
実行環境の目安
- Windows PowerShell 5.1 または PowerShell 7 以降
- インターネット接続(PowerShell Galleryからモジュール取得)
- CSVをExcelで開けるPC(ExcelがなくてもCSVは閲覧できます)
必要なMicrosoft Graphの権限(スコープ)
委任(サインイン)で実行する場合、最低限次のスコープが必要です。運用上は「必要最小限」+「実行者のロール管理」をセットで考えてください。
| スコープ | 用途 | よくある不足時エラー |
|---|---|---|
| User.Read.All | 全ユーザー情報の読み取り | Insufficient privileges… |
| Directory.Read.All | ディレクトリ全体情報の読み取り | Authorization_RequestDenied |
| RoleManagement.Read.Directory | ロール情報の読み取り | Insufficient privileges… |
実行アカウントは、少なくともロール情報を読み取れる管理系権限が必要です。テナントのセキュリティポリシーに合わせ、全体管理者に固定せず「必要な範囲の管理ロール」で運用するのが理想です。
Microsoft Graph PowerShellモジュールのインストール
PowerShellを「管理者として実行」で開き、次を実行します(CurrentUserに入れると端末全体へ影響しにくいので安全です)。
Install-Module Microsoft.Graph -Scope CurrentUser
すでに入っている場合は更新しておくとトラブルが減ります。
Update-Module Microsoft.Graph
Microsoft Graphへ接続する
次のコマンドで接続します。初回はブラウザが開き、Microsoft 365のサインイン画面が出ます。
Connect-MgGraph -Scopes "User.Read.All","Directory.Read.All","RoleManagement.Read.Directory"
接続できたか確認したい場合は、次のコマンドで現在の接続コンテキストを確認できます。
Get-MgContext
ユーザーとロールを結合してCSVに出力するスクリプト
ここからが本題です。ポイントは「大量ユーザーでも遅くなりにくいよう、ロール情報をハッシュテーブルで索引化する」ことです。ユーザーごとにWhere-Objectで毎回検索すると、ユーザー数が多いテナントで極端に遅くなりがちです。
出力したい列(例)
最低限の棚卸しに必要な列を先に決めておくと、あとからExcelで整形する手間が減ります。
| 列名 | 内容 | 使いどころ |
|---|---|---|
| DisplayName | 表示名 | 人間が見て判別しやすい |
| UserPrincipalName | UPN(サインインID) | 監査ログや申請書のキーになる |
| UserType | Member / Guest | ゲスト棚卸し(外部)に有効 |
| OnPremisesSyncEnabled | オンプレ同期有無 | 同期ユーザーかクラウド専用かを判断 |
| Role | 割り当てロール名 | 権限棚卸しの主役 |
スクリプト(ロールがないユーザーも出力)
次のスクリプトをそのまま貼り付けて実行してください。出力先はデスクトップに、日時付きでCSVを作成します。
# =============================
# Entra ID 全ユーザー+割り当てロールをCSV出力
# =============================
# 出力先(デスクトップ)
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$outPath = Join-Path $env:USERPROFILE ("Desktop\EntraUsersWithRoles_{0}.csv" -f $timestamp)
# Excelで文字化けしにくいエンコーディング
# Windows PowerShell 5.1: UTF8 はBOM付き
# PowerShell 7+: UTF8BOM を明示推奨
$encoding = if ($PSVersionTable.PSVersion.Major -ge 7) { "utf8BOM" } else { "utf8" }
# 1) 全ユーザー取得(ゲスト含む)
# 必要なプロパティだけ取ると軽くなります
$users = Get-MgUser -All -Property Id,DisplayName,UserPrincipalName,UserType,OnPremisesSyncEnabled |
Select-Object Id,DisplayName,UserPrincipalName,UserType,OnPremisesSyncEnabled
# 2) ロールメンバーシップを取得して、UserId => RoleName[] の索引を作る
$roleMap = @{} # key: UserId, value: List[string]
Get-MgDirectoryRole -All | ForEach-Object {
$role = $_
$roleName = $role.DisplayName
# 役割のメンバー(ユーザー/グループ/サービスプリンシパル等が混ざる可能性あり)
Get-MgDirectoryRoleMember -DirectoryRoleId $role.Id -All | ForEach-Object {
$member = $_
# Graphの戻り値はユーザー/グループ/サービスプリンシパル等が混在します。
# 返ってくる型は環境やモジュールのバージョンで差が出ることがあるため、複数条件でユーザー判定します。
$odataType = $null
try { $odataType = $member.AdditionalProperties["@odata.type"] } catch { }
$isUser = $false
if ($member.PSObject.Properties.Match("UserPrincipalName").Count -gt 0) { $isUser = $true }
elseif ($odataType -eq "#microsoft.graph.user") { $isUser = $true }
# ユーザーだけを対象にする(グループ付与を個々のユーザーまで展開したい場合は別途対応が必要)
if ($isUser) {
$uid = $member.Id
if (-not $roleMap.ContainsKey($uid)) {
$roleMap[$uid] = New-Object System.Collections.Generic.List[string]
}
$roleMap[$uid].Add($roleName)
}
}
}
# 3) ユーザー一覧と結合して、CSV行データを作る
# - ロールがあるユーザーは「ユーザー×ロール」で複数行
# - ロールがないユーザーは Role="None" で1行
$rows = foreach ($u in $users) {
if ($roleMap.ContainsKey($u.Id)) {
$roles = $roleMap[$u.Id] | Sort-Object -Unique
foreach ($r in $roles) {
[PSCustomObject]@{
DisplayName = $u.DisplayName
UserPrincipalName = $u.UserPrincipalName
UserType = $u.UserType
OnPremisesSyncEnabled = $u.OnPremisesSyncEnabled
Role = $r
}
}
} else {
[PSCustomObject]@{
DisplayName = $u.DisplayName
UserPrincipalName = $u.UserPrincipalName
UserType = $u.UserType
OnPremisesSyncEnabled = $u.OnPremisesSyncEnabled
Role = "None"
}
}
}
# 4) CSVに出力
$rows | Export-Csv -Path $outPath -NoTypeInformation -Encoding $encoding
Write-Host "出力完了: $outPath"
スクリプトの読みどころ
- ゲストユーザーも対象:Get-MgUser -All はMember/Guestをまとめて取得します。
- ロールがないユーザーも1行:Role列がNoneになり、Excelで「Noneだけ除外」などが一瞬でできます。
- 高速化の工夫:ユーザーごとにロール一覧を探しに行くのではなく、先にロール側からUserIdを集めて索引化します。
Excelでの開き方と、集計をラクにするコツ
CSVはダブルクリックでも開けますが、日本語環境では文字化けや列ずれが起きることがあります。その場合はExcelの「データ」タブから読み込む方法が安定です。
- Excelを開く
- [データ]→[テキストまたはCSVから]
- 文字コードが選べる場合はUTF-8を選択
- 区切り文字がカンマになっていることを確認して読み込み
読み込んだら次の加工をしておくと、棚卸しが一気に捗ります。
- フィルターを有効化(Role列でNoneを除外、Guestだけ抽出など)
- ピボットテーブルで「ロール別人数」「Guestにロール付与があるか」を一発集計
- UserPrincipalNameで重複チェック(UPNが空のアカウントがないか)
よくある落とし穴と対策
ロールに「グループ」が割り当てられている場合
Entra IDでは、条件を満たすグループ(ロール割り当て可能なグループ)に管理者ロールを付与し、ユーザーはそのグループメンバーとして権限を得る運用ができます。この場合、Get-MgDirectoryRoleMemberで返ってくるのは「ユーザー」ではなく「グループ」になることがあります。
今回のスクリプトは「ユーザーに直接割り当てられているロール」を中心に集計します。もし「グループ経由で権限を持つユーザー」もExcelに出したい場合は、次の追加設計が必要です。
- ロールメンバーに含まれるグループを検出する
- そのグループのメンバー(ユーザー)を展開する
- (必要なら)入れ子グループも再帰的に展開する
テナント規模によっては処理が重くなるため、まずは「直接割り当て」を棚卸しし、次に「グループ割り当て」を別シートで追いかける運用が現実的です。
Privileged Identity Management(PIM)を使っている場合
PIMで「資格(Eligible)」としてロールを持ち、必要時だけアクティブ化する運用だと、単純なメンバー取得だけでは見えない情報が出ます。棚卸しの観点では、少なくとも次を分けて把握するのが安全です。
| 種類 | 意味 | 棚卸しでの扱い |
|---|---|---|
| 永続(Permanent) | 常にロールを保持 | 最優先で見直す |
| アクティブ(Active) | 現在有効になっている | 運用状況の確認に使う |
| 資格(Eligible) | 昇格できる権利を持つ | 「潜在的な権限」として要確認 |
より厳密にやる場合はRoleManagement系のAPI(役割割り当てスケジュール/資格スケジュール)も取得してExcelに出す設計に拡張します。まずは本記事の「直接割り当て」棚卸しから着手し、必要性が出たら段階的に追加するのがおすすめです。
Get-MgDirectoryRoleで一部のロールが見えない
Get-MgDirectoryRoleは、テナント内で「有効化されている」ディレクトリロールの一覧を返します。多くの運用では、実際に割り当てが発生したロールは自動的に有効化されるため棚卸し用途では困りにくいのですが、次のケースでは「想定よりロールが少ない」と感じることがあります。
- ロール定義を全件(未使用のロールも含めて)一覧化したい
- RoleManagement(役割管理)APIでの取得結果と突合したい
その場合は、RoleManagementの「ロール定義」「ロール割り当て」から集計する方法に切り替えると網羅性が上がります。まずは本記事の方法で現場の棚卸しを回し、必要性が出た時点で段階的に移行するのが現実的です。
「権限が足りない」と言われる
エラーが出るときは、ほとんどが次のいずれかです。
- Connect-MgGraphで指定したスコープが不足している
- 実行者がテナント側でロール情報を読める権限を持っていない
- 条件付きアクセスやMFA要件でサインインがブロックされている
まずはConnect-MgGraphのスコープを見直し、次に実行者の管理ロール(または読み取り用の役割)を確認してください。
大量ユーザーで遅い・止まる
ユーザー数やロール数が多いテナントでは、-Allでの取得に時間がかかります。対策は次の通りです。
- 取得プロパティを必要最小限にする(本記事のスクリプトはその形)
- まずは小さく検証:-Allを外して一部だけで動作確認する
- 出力列を増やしすぎない(Excel側で追記できるものは後回し)
もっと実務向けにする拡張アイデア
「1ユーザー1行」でロールをまとめて表示したい
監査資料で「一人につき一行」の形式が求められることがあります。その場合は、Roleを複数行にせず、セミコロン区切りで連結して出力します。
# roles を連結して1行にする例(上のスクリプトの3)を置き換え)
$rows = foreach ($u in $users) {
$roles = @()
if ($roleMap.ContainsKey($u.Id)) {
$roles = $roleMap[$u.Id] | Sort-Object -Unique
}
[PSCustomObject]@{
DisplayName = $u.DisplayName
UserPrincipalName = $u.UserPrincipalName
UserType = $u.UserType
OnPremisesSyncEnabled = $u.OnPremisesSyncEnabled
Roles = if ($roles.Count -gt 0) { ($roles -join "; ") } else { "None" }
RoleCount = $roles.Count
}
}
差分監査(前回からロールが増えた/減った)をすぐ出したい
毎月の棚卸しでは「前月からの差分」が重要です。CSVを2つ用意してCompare-Objectすれば、変更点だけを抽出できます。
$prev = Import-Csv "C:\\temp\\EntraUsersWithRoles_prev.csv"
$curr = Import-Csv "C:\\temp\\EntraUsersWithRoles_curr.csv"
Compare-Object -ReferenceObject $prev -DifferenceObject $curr -Property UserPrincipalName,Role -PassThru |
Export-Csv "C:\temp\EntraUsersWithRoles_diff.csv" -NoTypeInformation -Encoding utf8
CSVではなくXLSXで出したい場合
「Excelファイル(.xlsx)として渡したい」ケースでは、CSVをExcelで開いて保存するのが最も確実です。もしPowerShellだけでXLSXを書き出したい場合は、追加モジュール(例:ImportExcel)を利用する手もあります。ただし社内規定でサードパーティ導入が難しい場合はCSV運用が無難です。
安全に運用するための注意点
- 実行アカウントの管理:ロール情報を読み取れるアカウントは強権限になりやすいため、利用者・端末・実行ログを残す運用にします。
- 出力ファイルの保護:CSVは「権限情報そのもの」なので、共有先(メール添付、Teams投稿、ファイルサーバー)を慎重に選びます。
- ゲストの扱い:Guestに管理者ロールが付いていると影響が大きいので、ExcelでGuest+Role≠Noneを最優先で確認します。
まとめ
Entra ID(旧Azure AD)環境で「全ユーザー」と「割り当てロール」をExcelで一覧化するには、Microsoft Graph PowerShellでユーザー一覧とロールメンバーを取得し、PowerShell側で突き合わせてCSV出力するのが現実的です。ロールがないユーザーもNoneとして出力しておけば、Excelでのフィルタや監査資料作成が一気にスムーズになります。まずは本記事のスクリプトで棚卸しの土台を作り、必要に応じてグループ割り当てやPIMまで段階的に拡張していきましょう。

コメント