Microsoft 365 / Microsoft Graph PowerShellでユーザーを取得しCSVに保存する方法

Microsoft Graph PowerShell を使用すると、Microsoft 365 ユーザーを照会し、アカウントやライセンス管理作業を自動化できます。最初は 必要なモジュールをインストール → 読み取り権限で接続 → 対象の組織を確認 → ユーザー一覧を CSV に保存 の順に開始してください。以下の照会実習およびアカウント作成・ライセンス変更の例は権限や業務への影響が異なるため、区別して実行する必要があります。

Microsoft Graph PowerShell とは何ですか?

Microsoft Graphは、Microsoft Entra ID、Teams、SharePoint などのデータを扱う API(Application Programming Interface、プログラム間通信インターフェース)です。Graph PowerShell SDK(Software Development Kit)は、この API を PowerShell コマンドで使用できるようにします。既存の文書で Azure Active Directory または Azure AD と呼ばれているディレクトリサービスの現在の名称は Microsoft Entra ID です。

Graph をインストールしたからといって、すべての Microsoft 365 管理コマンドを使用できるわけではありません。Exchange Online 専用管理コマンドのように別のモジュールと接続が必要な作業もあります。この記事では、Microsoft Graph v1.0 モジュールを使用した 対話型の委任認証 について取り上げます。無人のスケジュール実行のアプリ専用認証には別途アプリ権限・証明書などの設計が必要であり、ユーザーのログイン例をそのままスケジュール実行する方法とは異なります。

準備事項とモジュールのインストール

MicrosoftはPowerShell 7以上を推奨します。下記の実習も、サポートされているPowerShell 7環境で行ってください。Windows PowerShell 5.1には別途.NET Framework・PowerShellGetなどの要件があり、あるバージョンでインストールしたモジュールが他のバージョンに自動でインストールされることはありません。SDKインストールの要件をまず確認してください。

  1. 組織が許可した管理用PCでPowerShellを開きます。$PSVersionTable.PSVersionでバージョンを確認します。
  2. Microsoft Entra管理センターのEntra ID → 概要で操作対象のテナントIDを確認します。他の組織にもアクセス可能なアカウントであれば特に重要です。
  3. Graph権限に対する管理者同意と自身の管理ロールを確認します。一般のMicrosoft個人アカウントではなく、該当組織の職場または学校アカウントを使用します。
  4. 結果ファイルを保管するアクセス制限フォルダを作成します。ユーザー一覧には名前・アカウントアドレスなどが含まれるため、公開リポジトリや公開ウェブフォルダは使用しません。

現在のユーザーの範囲に必要なモジュールだけをインストールするには、次のように実行します。インストール確認メッセージが表示されたら、リポジトリとパッケージを確認してください。Microsoft.Graphモジュール全体のインストールも可能ですが、この実習では以下のサブモジュールで十分です。

Install-Module -Name Microsoft.Graph.Authentication,Microsoft.Graph.Users,Microsoft.Graph.Identity.DirectoryManagement,Microsoft.Graph.Users.Actions -Scope CurrentUser -Repository PSGallery

インストール後、次のコマンドで使用可能なバージョンとコマンド名を確認します。この段階では組織データに接続しません。

Get-Module -ListAvailable Microsoft.Graph.Authentication,Microsoft.Graph.Users,Microsoft.Graph.Identity.DirectoryManagement,Microsoft.Graph.Users.Actions | Select-Object Name,Version
Get-Command Connect-MgGraph,Get-MgUser,Get-MgSubscribedSku,Set-MgUserLicense

実行ポリシーのためにモジュールやスクリプトがブロックされる場合は、後述の実行ポリシーの項目を先に確認してください。ポリシーエラーを403権限エラーのような問題として扱わないでください。

接続権限と管理者ロールの区別

委任認証はログインしたユーザーに代わって作業を行う方式です。-Scopesはアプリが要求するGraph権限の範囲であり、ログインした管理者ロール・対象範囲および管理者の同意も満たす必要があります。-Scopesに名前を記載しただけでは、その権限が自動承認されたり管理者ロールが付与されるわけではありません。

作業 例で要求するGraph権限 追加確認
ユーザー一覧・指定属性の取得 User.Read.All 管理者同意と参照する属性の制限
サブスクリプション SKU・ユーザーライセンスの参照 LicenseAssignment.Read.All Directory Readers など、該当 API がサポートする役割
クラウドユーザーの作成 User.Create 管理者同意とユーザー管理権限・対象
ライセンスの割り当て・回収 LicenseAssignment.ReadWrite.All License Administrator など、サポートされる役割

読み取りの演習にグループ変更権限やディレクトリ全体の書き込み権限を同時に要求する必要はありません。権限の委任・アプリケーションの区別および管理者同意の有無は、Microsoft Graph 権限参照で確認します。

個別の参照コマンドを試すには、以下のように接続します。入力するテナント ID は組織で確認した実際の値であり、ログイン後に Account・TenantId・Scopes を確認してください。ログイン方法は、インストールされているバージョンや環境によってブラウザや Windows 認証ウィンドウなどが使用されることがあります。

$tenantId = Read-Host '操作対象のテナント ID'
Connect-MgGraph -TenantId $tenantId -Scopes 'User.Read.All' -ContextScope Process
Get-MgContext | Select-Object Account,TenantId,AuthType,Scopes

-ContextScope Processは認証コンテキストを現在のPowerShellプロセスの範囲で使用するためのオプションです。権限なしで実行したConnect-MgGraphの結果を常にUser.Readと仮定せず、実際のコンテキストを確認してください。別のアカウント・権限に切り替える際はDisconnect-MgGraph後、必要な範囲で再接続します。接続と認証方法についてはGraph PowerShell 認証ガイドを参照できます。

ユーザー取得とCSV保存の完全な例

Get-MgUser -Allは、ページ分けで返されるユーザー一覧を継続して照会します。ただし、すべてのプロパティを自動的に取得するという意味ではありません。Select-Object *は既に取得したオブジェクトのプロパティを展開するだけで、既定の戻り値にない値は-Propertyでサーバーにリクエストする必要があります。この違いはGet-MgUser 説明に記載されています。

次の全コードをm365-users-report.ps1としてUTF-8で保存します。スクリプトはユーザー情報を読み取り、同じフォルダに日時を付けたCSVを作成し、接続を切断します。アカウントやライセンスは変更しません。既存の接続で実習を行った場合は、まずDisconnect-MgGraphで終了してから実行してください。

param(
    [Parameter(Mandatory)]
    [Guid]$TenantId
)

$ErrorActionPreference = 'Stop'
$connected = $false
$file = Join-Path $PSScriptRoot ("m365-users-{0}.csv" -f (Get-Date -Format 'yyyyMMdd-HHmmss'))

try {
    Connect-MgGraph -TenantId $TenantId.ToString() -Scopes 'User.Read.All' -ContextScope Process -NoWelcome
    $connected = $true
    $context = Get-MgContext
    if ($context.TenantId -ne $TenantId.ToString()) {
        throw '接続先のテナントが指定したテナントと異なります。'
    }

    $properties = 'Id','DisplayName','UserPrincipalName','Mail','AccountEnabled','UsageLocation','AssignedLicenses'
    $users = @(Get-MgUser -All -Property $properties)
    if ($users.Count -eq 0) {
        throw 'ユーザーの取得結果がありません。接続と権限を確認してください。'
    }
    $report = @($users | Select-Object Id,DisplayName,UserPrincipalName,Mail,AccountEnabled,UsageLocation,
        @{Name='AssignedSkuCount';Expression={ @($_.AssignedLicenses | Where-Object { $null -ne $_ }).Count }})

    $report | Export-Csv -Path $file -NoTypeInformation -Encoding utf8BOM -NoClobber
    Write-Host ("保存先: {0} / ユーザー数: {1}" -f $file, $report.Count)
    $report | Select-Object -First 5 | Format-Table DisplayName,UserPrincipalName,AssignedSkuCount -AutoSize
}
finally {
    if ($connected) {
        Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null
    }
}

PowerShellでファイルがあるフォルダに移動した後、次のように実行します。ファイルのパスは実際に保存した場所に合わせ、入力欄には組織のテナントIDを貼り付けます。

$targetTenant = Read-Host '操作対象のテナント ID'
.\m365-users-report.ps1 -TenantId $targetTenant

成功するとCSVのパス・ユーザー数が表示され、画面には最初の5名が出力されます。ファイルの行数と画面のユーザー数を照合し、知っているテストアカウント1名の表示名・ログインアドレス・利用場所・ライセンスを管理センターと比較してください。AssignedSkuCountは割り当てられた製品SKU数であり、個々のサービス数や購入したライセンス数ではありません。

Mailが空のユーザーがいても、すぐに照会失敗と判断しません。ログイン名であるUserPrincipalNameとメールアドレスは異なる属性です。AccountEnabledがtrueでも、ライセンス・ポリシー・リスク判断などのために実際のログインやサービス利用が制限されることがあります。このレポートだけでユーザーが最近ログインしたか、業務を正常に行っていると判断しないでください。

照会結果がない場合やCSV生成に失敗した場合は完了として記録しません。出力パスの権限・同名ファイル・ファイルロックやエラーメッセージを確認してください。レポートは実行時点の照会結果であるため、アカウントが変わった場合は再実行して比較し、以前のファイルは組織の保管ポリシーに従って管理してください。

ライセンス製品とユーザー割り当ての照会

SKU(Stock Keeping Unit)は、ここでライセンス製品を区別する単位です。SkuPartNumberは製品識別文字列、SkuIdはGraphコマンドで使用する製品GUIDです。ユーザーの割り当てを変更する際、サブスクリプションオブジェクトIDやサービスプランIDをSKU IDの代わりに入れないように区別します。

全体のCSVスクリプトは最後に接続を切断するため、以下の個別コマンド実行時には新たに接続します。サブスクリプションSKUの取得には読み取り権限に加え、該当操作をサポートする役割が必要です。サブスクリプションSKU取得APIの役割・権限を確認してください。

$tenantId = Read-Host '操作対象のテナント ID'
Connect-MgGraph -TenantId $tenantId -Scopes 'User.Read.All','LicenseAssignment.Read.All' -ContextScope Process
Get-MgContext | Select-Object Account,TenantId,Scopes
Get-MgSubscribedSku -All | Select-Object SkuPartNumber,SkuId,CapabilityStatus,ConsumedUnits,@{Name='EnabledUnits';Expression={$_.PrepaidUnits.Enabled}}

$userId = Read-Host '確認するユーザーの UPN またはオブジェクト ID'
Get-MgUserLicenseDetail -UserId $userId -All | Select-Object SkuPartNumber,SkuId,ServicePlans

ConsumedUnitsは消費されたライセンス数、PrepaidUnits.Enabledは利用可能な状態で提供された単位です。停止・警告状態や製品条件があるため、単純な差をすぐに請求数量や全製品の残りのライセンス数として解釈しないでください。実際の購入・請求内容はサブスクリプション・請求・ライセンス数量照合手順で別途確認します。

Get-MgUserLicenseDetailには直接割り当てられたライセンスとグループから継承されたライセンスが含まれます。この結果だけで割り当て原因を区別することはできません。グループベースの割り当ての有無は管理センターや割り当て状態情報を追加で確認してください。ユーザーライセンス詳細APIで委任権限・サポート役割も確認できます。

ユーザー・ライセンスの変更作業は別途実行する

次の例は組織のデータを変更する作業です。読み取りレポートの後で一括で実行せず、承認されたテストユーザーと変更前の記録を用意した後、必要な権限で再接続してください。下記の -WhatIf は実行しようとする作業を事前に確認するオプションであり、サーバーのすべての有効性・権限・ライセンス条件を検査する模擬実験ではありません。

クラウドのテストユーザーの作成

クラウドで直接管理されるユーザーのみこの方法で作成します。同期組織のアカウントは元ディレクトリで作成するかをまず確認してください。テナントで検証済みのドメインと重複しないログイン名、パスワードポリシーを満たす一時パスワードが必要です。外部コラボレーションのゲスト招待とは別の作業です。ユーザー作成APIの必須値と権限に従って、最小作成権限であるUser.Createを確認し、ユーザー管理に必要な役割・対象を確認します。

Disconnect-MgGraph
$tenantId = Read-Host '操作対象のテナント ID'
Connect-MgGraph -TenantId $tenantId -Scopes 'User.Create','User.Read.All' -ContextScope Process
Get-MgContext | Select-Object Account,TenantId,Scopes

$upn = Read-Host '作成するテストユーザーの UPN(検証済みドメイン)'
$nickname = Read-Host 'メールのエイリアス(例: graph-test01)'
$securePassword = Read-Host 'ポリシーを満たす一時パスワード' -AsSecureString
$params = @{
    AccountEnabled = $true
    DisplayName = 'Graph テストユーザー'
    UserPrincipalName = $upn
    MailNickname = $nickname
    PasswordProfile = @{
        Password = [System.Net.NetworkCredential]::new('', $securePassword).Password
        ForceChangePasswordNextSignIn = $true
    }
}
try {
    New-MgUser -BodyParameter $params -WhatIf
}
finally {
    $params.PasswordProfile.Password = $null
    Remove-Variable securePassword
}

入力と対象を確認した後、実際に作成するにはコードの-WhatIfを削除して再実行します。パスワードはコマンドライン文字列やスクリプトファイルに固定しないでください。SDKリクエストには平文パスワード値が必要なため、この作業でデバッグ出力やリクエスト本文の記録を使用せず、一時パスワードも組織の承認されたルートで渡します。

作成後、Get-MgUser -UserId $upn -Property Id,DisplayName,UserPrincipalName,AccountEnabledでアカウントを再確認します。ユーザー作成だけでは、アプリのライセンス・メールボックス・グループの参加はすべて完了しません。テストアカウントの削除も業務アカウントの削除と同様にデータの確認が必要です。退社者アカウントとデータ引き継ぎ手続きを確認した後、管理センターで対象・復元可能範囲を確認して整理してください。

直接割り当てたライセンスの回収

回収前にユーザー・製品・直接/グループ割り当ての有無およびデータ保存条件を確認します。この例の-RemoveLicensesにはSKU ID文字列配列を渡し、@{SkuId=...}形式のハッシュテーブルは入れません。追加する製品がない場合は-AddLicenses @()も併用します。Set-MgUserLicense構文を確認してください。

Disconnect-MgGraph
$tenantId = Read-Host '操作対象のテナント ID'
Connect-MgGraph -TenantId $tenantId -Scopes 'LicenseAssignment.ReadWrite.All' -ContextScope Process
Get-MgContext | Select-Object Account,TenantId,Scopes

$userId = Read-Host 'ライセンスを削除するテストユーザーの UPN またはオブジェクト ID'
$skuId = [Guid](Read-Host '確認済みの削除対象 SkuId')
Set-MgUserLicense -UserId $userId -AddLicenses @() -RemoveLicenses @($skuId.ToString()) -WhatIf

実際の変更は承認および対象確認後に-WhatIfを削除して実行します。LicenseAssignment.ReadWrite.Allへの同意とLicense Administratorなどのサポート役割がすべて必要です。グループベースの割り当てはユーザー直接回収コマンドだけで処理せず、元のグループの割り当てを確認してください。ライセンス割り当てAPIの権限・役割・リクエスト形式が基準です。

変更後に必要な読み取り権限で再接続し、ライセンスの詳細と管理センターを照合します。元に戻す場合は、残りのライセンス数、利用場所、以前の利用停止サービスプランまで確認して、元の構成に再割り当てする必要があります。ライセンスを再適用しても、すでに実行されたデータ削除がすべて取り消されると仮定しないでください。回収前に保管と引き継ぎを完了することが重要です。

Windows 実行ポリシーを確認して元に戻す

実行ポリシーはスクリプトやモジュールの実行条件です。Windows でブロックエラーが発生した場合は、有効なポリシーと範囲ごとの値を確認します。

Get-ExecutionPolicy
Get-ExecutionPolicy -List

組織ポリシーが許可し、現在のユーザー範囲で変更する必要がある場合のみ、以前の値を記録した後に RemoteSigned を確認します。すでに許可されている設定がある場合は、形式を合わせるために変更しません。組織のグループポリシーの MachinePolicy・UserPolicy が優先される場合は、担当者に確認し、回避しないでください。

$previousUserPolicy = Get-ExecutionPolicy -Scope CurrentUser
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
Get-ExecutionPolicy -List

作業後、同じセッションで元の値に復元するには、以下のように実行します。新しいウィンドウで復元する場合は、あらかじめ記録した実際の値を使用します。RemoteSigned もインターネットから受け取ったすべてのコードを信頼するという意味ではありません。 実行ポリシーの範囲と優先順位 を確認してください。

Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy $previousUserPolicy

よくあるエラーと作業終了

症状 確認手順
コマンドを認識できない 実行中の PowerShell のバージョンとモジュールのインストール場所を確認します。Get-MgUser は Microsoft.Graph.Users にあります。Get-Command でコマンドを探し、インストール・読み込みエラーを区別します。
403 または権限不足 接続アカウント・テナント・Scopes、管理者の同意、その API のサポート役割を確認します。無条件で全書き込み権限を追加しません。
ユーザー・オブジェクトを見つけられない 組織と UPN・オブジェクト ID、削除・同期状態を確認します。他のテナントのアカウント名を入力していないか確認します。
属性が空 -Property で要求したか、そのアカウントに実際に値があるか、別途権限が必要な属性かを区別します。
サインインがブロックされる エラーコード・時刻を記録し、条件付きアクセス・MFA・許可される認証方法およびサインインログを確認します。ログイン成功のためにセキュリティポリシーをオフにしません。

インストールバージョンとコマンドの詳細なエラー、リクエストID・発生時刻を一緒に記録すると、再現やサポート依頼に役立ちます。Graph PowerShell エラー診断の方法を使用する場合でも、トークン・パスワード・個人情報が含まれるログをそのまま共有しません。

個別コマンドの演習が終わったら接続を切り、結果ファイルの保存場所を確認します。ログアウトは、既に行ったアカウント・ライセンス変更を元に戻す作業とは異なります。

Disconnect-MgGraph