Microsoft Entra ID(旧 Azure AD)のユーザー一覧は、画面上の列を見ながらCSVに出したくなる場面が多い一方で、「ユーザーのダウンロード」で出てくる列が思い通りにならない、Assigned licenses(割り当て済みライセンス)のYes/Noが何を見ているのか分からない、という声もよく聞きます。本記事ではポータル側の仕様を整理しつつ、Microsoft Graph+PowerShellで“必要な列だけ”を高速に出力する実践手順までまとめます。
まず押さえる:Entra IDポータルの「ユーザーのダウンロード」は2系統ある
「列が固定に見える」「画面で列を増やしてもCSVに反映されない」という悩みは、使っているダウンロード機能が“従来版”か“改善版(Preview)”かで結論が変わります。
| 比較ポイント | 従来の「Download users」 | 改善版「Download users (Preview)」 |
|---|---|---|
| 出力される列 | 基本は固定(定義済みの属性セット) | ユーザー一覧の列(ビュー)に合わせてミラー出力 |
| 列のカスタマイズ | 不可(画面の列設定とCSV列は連動しない) | 可(一覧で選んだ列=CSV列) |
| 追加で出せる属性 | 固定セットのみ | 追加の属性が増え、Assigned licenses なども出力対象に |
| フィルターの反映 | 画面のフィルター範囲に基づく | 画面のフィルター範囲に基づく |
| 完了までの流れ | 「開始」→CSVを受領/結果は Bulk operation results で確認 | 右ペインから開始→「File is ready」→Bulk operation results (Preview) で状態確認 |
従来版:CSVの列が固定に見える理由(“定義済み属性セット”で出力される)
従来のダウンロードでは、ユーザー一覧(Microsoft_AAD_UsersAndTenants → ユーザー管理 相当の画面)で列をカスタマイズしても、CSVはその列構成に追従せず、あらかじめ決められたユーザー属性セットで出力されます。たとえば、UPN、表示名、部署、役職、利用場所など、定番のプロファイル属性が中心です。
また、ダウンロード対象ユーザーは「表示中のフィルター範囲」に基づくため、部署で絞ったり検索したりした状態で実行すると、絞り込み後のユーザーだけがCSVに入ります。
この“固定列”挙動があるため、次のような要件は従来版だけでは満たしにくいのが実情です。
- 画面で選択した列だけをそのままCSVにしたい
- 不要な列を省き、必要列+自分で計算した列(例:ライセンス有無の真偽値)だけを出したい
- CSVの列名を運用に合わせて変えたい(例:IsLicensed、LicenseNames など)
改善版(Preview):一覧の列設定がCSVに反映される
改善版の一番大きな違いは、ユーザー一覧の列(User List View)をカスタマイズし、その選択列がCSVに“ミラー出力”される点です。「以前は固定だったが、列を自由に選べるようになった」という位置付けです。
改善版では出力できる属性も増え、Assigned licenses を含む追加属性がエクスポート対象に入りました。さらに、処理速度が大幅に改善され、従来の“1時間以内に終わらないと失敗しやすい”制約の緩和も案内されています。
改善版(Preview)の操作手順(迷いがちなポイントだけ)
- Microsoft Entra admin center(entra.microsoft.com)で Microsoft Entra ID → Users → All users を開く
- コマンドバーの Download users (Preview) を選び、右側ペインで Start download
- 完了後に File is ready からCSVを取得
- 進捗や失敗理由は Bulk operation results (Preview) で確認
注意:複合プロパティ由来の列は“列だけ”が出るとは限らない
改善版でも万能ではありません。たとえば「最終サインイン日時」のように、裏側が複合(complex)プロパティで表現される列は、CSVにはその複合プロパティ全体が出力されることがあります。つまり、“表示している見た目の1列”よりも情報量の多いJSONライクな値が入るイメージです。
結局どっちを選ぶ?おすすめの判断基準
| やりたいこと | おすすめ | 理由 |
|---|---|---|
| 画面で選んだ列をそのままCSVにしたい(ポータル完結) | Download users (Preview) | 列がビュー連動で出せる |
| 列名変更、計算列追加、ライセンス名への変換など“加工済みCSV”が欲しい | Graph+PowerShellで自作 | 必要列のみ、計算列も自由、変換・整形も可能 |
| 特定条件(例:E5のみ)でサーバー側フィルターして高速に取りたい | Graph($filter)+PowerShell | API側で絞り込みができ、取得量を減らせる |
| 「ライセンスが1つでも付いている人」だけを大量に抽出したい | Graph($countフィルター) | assignedLicenses/$count を使うのが正攻法 |
Assigned licenses(Yes/No)の正体:Graphの assignedLicenses を見ている
ポータルの一覧にある Assigned licenses(割り当て済みライセンス) の Yes/No は、Graph上ではユーザーオブジェクトの assignedLicenses プロパティ(配列)が空かどうかで再現できます。assignedLicenses は「ユーザーに割り当てられたライセンス(SKU)」を表す複合型のコレクションで、要素には skuId と disabledPlans が含まれます。
| ポータル表示 | Graphでの見方 | 判定ロジック |
|---|---|---|
| Yes | assignedLicenses に1件以上入っている | assignedLicenses.Count > 0 |
| No | assignedLicenses が空(0件) | assignedLicenses.Count = 0 |
「Get-MgUserLicenseDetail」をやめると何が速くなる?
Get-MgUserLicenseDetail(/users/{id}/licenseDetails)は、ライセンスの“詳細”(SKU名、サービスプランなど)を取れる一方、ユーザーごとに呼ぶと呼び出し回数が増えがちです。Yes/Noだけが欲しいなら、ユーザー取得の同一レスポンス内で assignedLicenses を取ってしまう方がシンプルで高速です。
| 目的 | おすすめAPI/プロパティ | 向いているケース |
|---|---|---|
| ライセンス有無(Yes/No)だけ | user.assignedLicenses | 棚卸し・監査の一次抽出、CSV出力の高速化 |
| SKU名やサービスプランまで知りたい | /users/{id}/licenseDetails | Exchange/Teamsなど機能単位の有効化確認、計画の可視化 |
| テナントで利用可能なSKU一覧が欲しい | /subscribedSkus(Get-MgSubscribedSku) | skuId→skuPartNumberの変換、レポートの可読性向上 |
ポータルがどのAPIで判断しているか調べたい:現場で効く3つの確認方法
ブラウザー開発者ツール(Network)で Graph 呼び出しを確認する
Entra ID ポータルの大半は Graph API ベースで動いているため、F12(開発者ツール)→Networkで、graph.microsoft.com 宛てのリクエストを見れば「どのエンドポイント」「どの $select / $filter」を使っているかを追えます。Assigned licenses 列に関しても、ユーザー一覧取得のレスポンスに assignedLicenses(またはそれを加工した結果)が含まれているケースが多いです。
PowerShell側で「どのAPIパスを呼ぶcmdletか」を即確認する(Find-MgGraphCommand)
Graph PowerShell SDK を使っているなら、Find-MgGraphCommand で “そのcmdletが呼ぶURI” を引けます。たとえば Get-MgUser が /users を呼ぶこと、Get-MgUserLicenseDetail が /users/{id}/licenseDetails を呼ぶこと、といった対応関係を可視化できます。
Graph RESTの仕様($select・$filter)を先に理解しておく
ユーザーコレクション(/users)では、既定で返るプロパティが限定されています。assignedLicenses のような“追加で必要なプロパティ”は、$select(PowerShellなら -Property など)で明示して取得するのが基本です。
PowerShellで「Assigned licenses 相当」を高速に取る(Yes/No列を作る)
以下は、ユーザーID・表示名・UPN・部署・役職・ライセンス有無(IsLicensed)をまとめて取得し、CSVに出す最小構成例です。ポイントは assignedLicenses を同時に取って PowerShell 側で真偽値に変換することです。
# 必要なモジュール(環境によりインストール済みのこともあります)
Import-Module Microsoft.Graph.Users
# 読み取り系で最低限必要になりやすいスコープ
Connect-MgGraph -Scopes "User.Read.All"
# 取得したいプロパティをまとめて指定($select相当)
$props = @(
"id","displayName","userPrincipalName",
"department","jobTitle",
"assignedLicenses"
)
Get-MgUser -All -Property $props |
Select-Object `
Id, DisplayName, UserPrincipalName, Department, JobTitle,
@{Name="IsLicensed";Expression={ if ($_.AssignedLicenses -and $_.AssignedLicenses.Count -gt 0) { $true } else { $false } }} |
Export-Csv -Path ".\entra-users-min.csv" -NoTypeInformation -Encoding UTF8
“ライセンス名”もCSVに入れたい:skuId→skuPartNumber 変換を追加する
assignedLicenses には skuId(GUID)が入ります。レポートとして見やすくするなら、テナントの subscribedSkus から skuPartNumber を引き、skuId を名前へ変換するのが定番です。
Import-Module Microsoft.Graph.Users
Import-Module Microsoft.Graph.Identity.DirectoryManagement
Connect-MgGraph -Scopes "User.Read.All","Organization.Read.All"
# skuId(GUID) → skuPartNumber を辞書化
$skuMap = @{}
Get-MgSubscribedSku -All | ForEach-Object {
$skuMap[$_.SkuId.Guid] = $_.SkuPartNumber
}
$props = @("id","displayName","userPrincipalName","mail","userType","accountEnabled","assignedLicenses")
Get-MgUser -All -Property $props |
Select-Object `
Id, DisplayName, UserPrincipalName, Mail, UserType, AccountEnabled,
@{Name="IsLicensed";Expression={ $_.AssignedLicenses.Count -gt 0 }},
@{Name="LicenseNames";Expression={
if (-not $_.AssignedLicenses -or $_.AssignedLicenses.Count -eq 0) { "" }
else {
($_.AssignedLicenses | ForEach-Object {
$k = $_.SkuId.Guid
if ($skuMap.ContainsKey($k)) { $skuMap[$k] } else { $k }
}) -join ";"
}
}} |
Export-Csv -Path ".\entra-users-with-licenses.csv" -NoTypeInformation -Encoding UTF8
「ライセンスありユーザーだけ」をGet-MgUserでフィルターする正攻法
質問でよくあるのが、次のように書いて失敗するパターンです。
assignedLicenses ne null:assignedLicenses は配列(コレクション)なので単純比較できないassignedLicenses/any(a:a/skuId ne null):条件が複雑になり、環境やクエリ次第でサポート外になりやすい
現実的な正攻法は、assignedLicenses/$count を使って「0件かどうか」で判定することです。これは Microsoft の公式ドキュメントでも、ライセンスあり/なしユーザー抽出の方法として案内されています。
ライセンスなし(unlicensed)
Connect-MgGraph -Scopes "User.Read.All"
# assignedLicenses が 0 件のユーザー
Get-MgUser -Filter 'assignedLicenses/$count eq 0' `
-ConsistencyLevel eventual -CountVariable unlicensedUserCount -All
Write-Host "Found $unlicensedUserCount unlicensed users."
ライセンスあり(licensed)
Connect-MgGraph -Scopes "User.Read.All"
# assignedLicenses が 1 件以上のユーザー
Get-MgUser -Filter 'assignedLicenses/$count ne 0' ` -ConsistencyLevel eventual -CountVariable licensedUserCount -All`
-Select UserPrincipalName,DisplayName,AssignedLicenses |
Format-Table -Property UserPrincipalName,DisplayName,AssignedLicenses
Write-Host "Found $licensedUserCount licensed users."
ここで重要なのが、-ConsistencyLevel eventual と -CountVariable(内部的には $count=true) を付ける点です。Microsoft Graph のディレクトリオブジェクトでは、一部の高度な検索(advanced query)を有効にするために ConsistencyLevel ヘッダーと $count が必要になる、と説明されています。
特定SKU(例:E5)だけを持つユーザーを抽出する
「E5を持っているユーザーだけ」など、SKUを指定できるなら、assignedLicenses に対して any で skuId を条件にするフィルターが使えます。公式ドキュメントでは、SPE_E5 の skuId を subscribedSkus から取得してフィルターする例も示されています。
Connect-MgGraph -Scopes "User.Read.All","Organization.Read.All"
# 例:E5(SPE_E5)の skuId を取得
$e5Sku = Get-MgSubscribedSku -All | Where-Object SkuPartNumber -eq "SPE_E5"
# skuId を条件にユーザーを抽出
Get-MgUser -Filter "assignedLicenses/any(x:x/skuId eq $($e5Sku.SkuId) )" `
-ConsistencyLevel eventual -CountVariable e5licensedUserCount -All
Write-Host "Found $e5licensedUserCount E5 licensed users."
実運用でハマりやすいポイント(チェックリスト)
- assignedLicenses が空で返る:Get-MgUser は既定で返すプロパティが限定されるため、-Property($select)で明示していない可能性があります。
- $count を含むフィルターが動かない:-ConsistencyLevel eventual と -CountVariable を付け忘れると、advanced query が有効にならずエラーや想定外の結果になりえます。
- 大量ユーザーで遅い:取りたい列を絞る($select)/サーバー側フィルター($filter)で対象を減らす/出力列を計算しすぎない、が効きます。
- ライセンス名がGUIDで読めない:Get-MgSubscribedSku を併用して skuId→skuPartNumber に変換すると、レポートの可読性が上がります。
- ポータルの列と完全一致しない:改善版(Preview)はビュー連動でも、複合プロパティ由来の列は“複合全体”が出る場合があります。
まとめ
- Entra ID ポータルの「ユーザーのダウンロード」は、従来版(固定列)と改善版Preview(列をミラー出力)の2系統があり、列仕様が異なります。
- Assigned licenses(Yes/No)は、Graph の assignedLicenses(配列)が空かどうかで再現できます。Yes/Noだけなら Get-MgUserLicenseDetail より軽量です。
- 「ライセンスありユーザーだけ」をサーバー側で抽出するなら、assignedLicenses/$count を使い、-ConsistencyLevel eventual と -CountVariable をセットするのが正攻法です。
- “必要な列+自分で追加した列”のCSVが欲しい場合は、Graph+PowerShellで列設計・変換・計算を自作するのが最も確実です。

コメント