Entra IDポータル「ユーザーのダウンロード」CSV列のカスタマイズ方法とAssigned licenses判定|Microsoft Graph PowerShell

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)の操作手順(迷いがちなポイントだけ)

  1. Microsoft Entra admin center(entra.microsoft.com)で Microsoft Entra ID → Users → All users を開く
  2. コマンドバーの Download users (Preview) を選び、右側ペインで Start download
  3. 完了後に File is ready からCSVを取得
  4. 進捗や失敗理由は Bulk operation results (Preview) で確認

注意:複合プロパティ由来の列は“列だけ”が出るとは限らない

改善版でも万能ではありません。たとえば「最終サインイン日時」のように、裏側が複合(complex)プロパティで表現される列は、CSVにはその複合プロパティ全体が出力されることがあります。つまり、“表示している見た目の1列”よりも情報量の多いJSONライクな値が入るイメージです。

結局どっちを選ぶ?おすすめの判断基準

やりたいことおすすめ理由
画面で選んだ列をそのままCSVにしたい(ポータル完結)Download users (Preview)列がビュー連動で出せる
列名変更、計算列追加、ライセンス名への変換など“加工済みCSV”が欲しいGraph+PowerShellで自作必要列のみ、計算列も自由、変換・整形も可能
特定条件(例:E5のみ)でサーバー側フィルターして高速に取りたいGraph($filter)+PowerShellAPI側で絞り込みができ、取得量を減らせる
「ライセンスが1つでも付いている人」だけを大量に抽出したいGraph($countフィルター)assignedLicenses/$count を使うのが正攻法

Assigned licenses(Yes/No)の正体:Graphの assignedLicenses を見ている

ポータルの一覧にある Assigned licenses(割り当て済みライセンス) の Yes/No は、Graph上ではユーザーオブジェクトの assignedLicenses プロパティ(配列)が空かどうかで再現できます。assignedLicenses は「ユーザーに割り当てられたライセンス(SKU)」を表す複合型のコレクションで、要素には skuId と disabledPlans が含まれます。

ポータル表示Graphでの見方判定ロジック
YesassignedLicenses に1件以上入っているassignedLicenses.Count > 0
NoassignedLicenses が空(0件)assignedLicenses.Count = 0

「Get-MgUserLicenseDetail」をやめると何が速くなる?

Get-MgUserLicenseDetail(/users/{id}/licenseDetails)は、ライセンスの“詳細”(SKU名、サービスプランなど)を取れる一方、ユーザーごとに呼ぶと呼び出し回数が増えがちです。Yes/Noだけが欲しいなら、ユーザー取得の同一レスポンス内で assignedLicenses を取ってしまう方がシンプルで高速です。

目的おすすめAPI/プロパティ向いているケース
ライセンス有無(Yes/No)だけuser.assignedLicenses棚卸し・監査の一次抽出、CSV出力の高速化
SKU名やサービスプランまで知りたい/users/{id}/licenseDetailsExchange/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で列設計・変換・計算を自作するのが最も確実です。

この記事を書いた人

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

コメント

コメントする

目次