Microsoft Graph PowerShell入門 ── AzureAD・MSOnline廃止後のMicrosoft 365運用

· 更新日: · · PowerShell, Microsoft 365, Microsoft Entra ID, Microsoft Graph, 情報システム, 自動化, 運用改善, セキュリティ

更新履歴(4件・最終更新 2026年08月02日)

この記事に加えた変更の記録です。アーカイブした更新前のバージョンは、DOI付きの固定URLから読めます。

記事の冒頭に「この記事の知識マップ」節を追加しました。本文で扱っている概念とその関係を、要約・図・詳細ページへのリンクにまとめたものです。本文の主張は変えていません。
外部レビュー(1283件)への対応として本文を更新しました。個々の変更内容は、この下の履歴を参照してください。
無人実行の説明で、証明書を LocalMachine のストアに置いた場合に拇印を渡しても見つからない点を補いました。-CertificateThumbprint は現在のユーザーのストアしか探さないため、LocalMachine のときは証明書を自分で読み込んで -Certificate に渡す必要があります。
無人実行(アプリ専用認証)の手順を、Entra管理センターの画面の場所と証明書の作成・登録まで含めて具体化しました。あわせて委任とアプリ専用の違いを対比表と図で整理し、用語の一覧と、記載内容が2026年7月時点のものである旨の注記を加えています。
初版公開
この記事を引用する(DOI(登録済みアーカイブ): 10.5281/zenodo.21547441)

以下のDOIは過去に登録されたアーカイブを指しており、現在の本文とは一致しない場合があります。現在の本文を参照するときは、このページのURLを使用してください。

小村 豪(2026)「Microsoft Graph PowerShell入門 ── AzureAD・MSOnline廃止後のMicrosoft 365運用」合同会社小村ソフト. https://comcomponent.com/blog/powershell-microsoft-graph-introduction/

DOI(登録済みアーカイブ)
10.5281/zenodo.21547441
DOI(前回登録した版)
10.5281/zenodo.21733148

Get-MsolUser や Get-AzureADUser で書いた「ユーザー棚卸し」「ライセンス集計」「退職者処理」を、今後はどう動かせばよいのでしょうか。AzureADモジュールとMSOnlineモジュールの廃止を受け、Microsoft 365の運用スクリプトは移行が必要になりました。1

移行先は Microsoft Graph PowerShell SDK です。ただし、単なるコマンド名の置き換えではありません。認証の考え方(スコープと同意)、データの取り方(OData のフィルターとページング)、無人実行の作り方(アプリ登録と証明書)まで、設計の前提が変わります。ここを理解せずに機械的に置き換えると、「動くけれど過剰な権限で動いている」「件数が多いテナントで途中までしか取れない」といった別の問題を抱え込みます。

この記事では、社内のMicrosoft 365運用をPowerShellで自動化している情シス担当者に向けて、「移行先を選ぶ → 誰として・どの権限で接続するかを決める → 必要なデータを取得する → 業務処理へ組み込む」の順で整理します。後半には、棚卸し・ライセンス集計・退職者処理の3つのレシピをまとめています。

本記事の説明は2026年7月時点のものです。 また、本文の無人実行例はWindowsのタスクスケジューラと証明書ストアを使う構成です。例示したID・拇印・パス・アカウントは自社の値に置き換え、検証環境で確認してから使ってください。

1. まず結論

移行で押さえるのは、次の3つです。「コマンドを何に替えるか」だけでなく、「権限」と「取得方法」も見直します。

移行先と、入れるモジュールを決める

  • MSOnlineとAzureADは2024年3月30日に非推奨となり、MSOnlineは2025年5月30日に提供終了、AzureADも2025年3月30日でサポート終了後に廃止されました。1
  • 移行先はMicrosoft Graph PowerShell SDK、またはその上のMicrosoft Entra PowerShell(2025年3月にGA)です。後者はシナリオ指向で、AzureADからの移行を助ける互換オプションも持ちます。2
  • Microsoft.Graph はメタモジュールです。全部入れると重いので、実務では Microsoft.Graph.Authentication + 使うワークロードのサブモジュールだけを入れます。3

対話実行と無人実行の権限を分ける

  • 接続は Connect-MgGraph -Scopes から始めます。スコープは最小権限で。必要な権限は Find-MgGraphPermission、コマンドの所属モジュールは Find-MgGraphCommand で調べられます。45
  • 無人実行はアプリ登録+証明書によるアプリ専用認証です。-ClientId -TenantId -CertificateThumbprint で対話なしに接続します。クライアントシークレットより証明書を推奨します。46
  • 委任(Delegated)とアプリ専用(Application)では必要な権限が別物です。同じ操作でも要求するスコープが変わるので、無人実行に切り替える際は権限を付け直す必要があります。6
  • アプリ登録は用途ごとに分けます。「全部入りの1アプリ」は権限が肥大化し、事故時の影響範囲も広がります。

取得する範囲と量を、先に絞る

  • 一覧取得は -All、絞り込みは -Filter、項目は -Property。クライアント側の Where-Object で絞ると、無駄な取得とスロットリングを招きます。7
  • 大量アクセスは調整(スロットリング)されます。429応答では Retry-After に従って待つのが公式の指針です。8

目的・症状から読む

知りたいこと・困っていること 読む場所
Graph SDKとEntra PowerShellのどちらを使うか 2章: 移行先の選び方
モジュールのインストールや読み込みが重い 3章: 必要なサブモジュールだけ入れる
必要な権限が分からない・接続後に403になる 4章: 接続とスコープ、7章(3): 操作ごとの権限とロール
タスクスケジューラから無人実行したい 5章: アプリ登録と証明書
証明書が見つからない・手元でしか動かない 5.3: 実行アカウントと証明書ストア
一覧が途中までしか取れない・429が返る 6章: ページング・絞り込み・再試行
ユーザー棚卸しをCSVにしたい 7章(1): 棚卸しと最終サインイン
ライセンスの購入数・割当数・空きを見たい 7章(2): ライセンス集計
退職者のサインインを止めたい 7章(3): 対象確認と、失効の効く範囲

初めて使う場合は2〜6章を順に読み、必要なレシピへ進んでください。退職者処理は読み取りではなく、アカウントを変更する操作です。 接続と権限の前提を確認せずにコードだけを実行しないでください。

図の実線は常に成り立つ関係、破線は条件付きの関係です(成立条件は詳細ページの各関係の説明に記載)。関係すべての一覧(全24件、根拠・確度つき)と主要概念の定義は知識マップ詳細ページにまとめています。データ: JSON-LD / Turtle

2. 廃止の経緯と、いま何を選ぶか

まず事実関係を整理します。本記事の内容は2026年7月時点のものです。以下の日付はいずれもMicrosoftが公表した廃止スケジュールで、すでに経過済みです。1

モジュール 状態
MSOnline(Get-MsolUser など) 2024年3月30日に非推奨。2025年5月30日で廃止
AzureAD(Get-AzureADUser など) 2024年3月30日に非推奨。2025年3月30日でサポート終了し、その後廃止
Microsoft Graph PowerShell SDK 現行。Graph APIをそのままコマンドレット化したもの
Microsoft Entra PowerShell 2025年3月にGA。Graph SDK上に構築されたシナリオ指向のモジュール2

Graph SDKとEntra PowerShellを、扱う業務で選ぶ

どちらを使うかの目安はこうです。Graph APIの構造をそのまま扱いたい・幅広いワークロード(Exchange、Teams、Intuneなど)に触れたいならGraph PowerShell SDK。Entra ID(旧Azure AD)のID管理が中心で、AzureADモジュールからの移行をなるべく楽にしたいならMicrosoft Entra PowerShellです。後者はGraph PowerShell SDKと相互運用でき、AzureADモジュールからの移行を支援する後方互換オプションも提供されています。2

本記事では、汎用性が高くドキュメントも豊富なGraph PowerShell SDKを軸に説明します。

3. インストール ── メタモジュールを丸ごと入れない

Microsoft.Graph は多数のサブモジュールを束ねたメタモジュールです。丸ごと入れるとインストールも読み込みも重く、実行環境によっては読み込みだけで数十秒かかることもあります。3

インストール例は「全部入り」と「必要な分だけ」の比較

次のコードは、上からすべて実行する手順ではありません。Microsoft.Graph を丸ごと入れる例と、サブモジュールを選ぶ例を並べています。実務では後者を選び、末尾の Find-MgGraphCommand と Find-MgGraphPermission でコマンドの所属や権限を調べます。

# 【重い】全ワークロードを入れる
Install-Module Microsoft.Graph -Scope CurrentUser

# 【実務】認証 + 使うワークロードだけを入れる
Install-Module Microsoft.Graph.Authentication -Scope CurrentUser   # 必須
Install-Module Microsoft.Graph.Users          -Scope CurrentUser   # ユーザー
Install-Module Microsoft.Graph.Groups         -Scope CurrentUser   # グループ
Install-Module Microsoft.Graph.Identity.DirectoryManagement -Scope CurrentUser  # ライセンス等
Install-Module Microsoft.Graph.Users.Actions  -Scope CurrentUser   # ユーザーに対する操作
                                                                   # (Revoke-MgUserSignInSession など)

# どのコマンドがどのモジュールにあるか、必要な権限は何かを調べる
Find-MgGraphCommand -Command Get-MgUser | Select-Object Module, Permissions -First 1
Find-MgGraphPermission user.read -PermissionType Delegated

実行環境はPowerShell 7を軸にする

PowerShell 7での利用が推奨されます。Windows PowerShell 5.1でも動作しますが、性能面でも将来性の面でも7を選ぶ理由が大きい領域です(「Windows PowerShell 5.1とPowerShell 7の違い」)。3

4. 接続とスコープ ── 「とりあえずReadWrite.All」をやめる

4.1 先に用語を揃える

以降で繰り返し出てくる言葉を、先に短く定義しておきます。

用語 意味
委任(Delegated) サインインしたユーザー本人として実行する形態。実際にできることは「アプリに同意されたスコープ」と「そのユーザーが持つロール」の両方で決まる6
アプリ専用(Application) ユーザーを介さずアプリ自身として実行する形態。無人バッチはこちら。証明書またはクライアントシークレットで認証する6
アプリ登録 / サービスプリンシパル アプリ登録はアプリの定義。サービスプリンシパルはそれをテナントに実体化したもので、権限やロールはこちらに紐づく
スコープ(アクセス許可) User.Read.All のような権限名。委任用とアプリケーション用は別枠で、同じ名前でも付け直しが必要6
OData Open Data Protocol。Graphのクエリ構文の土台で、-Filter -Property などは対応するODataのクエリオプションに変換されてサーバー側で処理される7
調整(スロットリング) 要求が多すぎるときにサービス側が意図的に拒否する仕組み。HTTP 429と Retry-After ヘッダーで返る8
継続的アクセス評価(CAE) トークンの有効期限を待たずに、失効などのイベントをリソース側が受け取ってアクセスを打ち切る仕組み。対応するアプリ・リソースでのみ働く9

まず「誰として実行するか」を決める

特に重要なのは、表の最初の2つ、委任とアプリ専用の違いです。図では、ユーザー本人として動く場合と、アプリ自身として動く場合を分けています。

アプリ専用 Application ── 無人実行委任 Delegated ── 対話サインイン無人化するときは権限を付け直すアプリケーション許可管理者の同意が必要証明書で認証できること =付与された許可そのもの同意した委任スコープ管理者がサインイン本人が持つ管理者ロールできること =スコープとロールの重なり

覚え方はひとつです。委任=ユーザー本人として実行、アプリ専用=無人バッチ。委任では「本人にできないことはアプリにもできない」、アプリ専用では「アプリに与えた分だけ、常にできてしまう」。

4.2 対話的な接続

対話的な接続は Connect-MgGraph -Scopes です。指定したスコープに対して同意画面が出て、同意結果はテナントに記録されます。4

接続・接続先の確認・切断を分けて使う

次の例は、接続後に Get-MgContext でアカウント・テナント・スコープ・認証形態を確認し、最後に切断します。業務処理は接続中に行うので、後の取得例を試すときは、Disconnect-MgGraph を実行する前に行うか、必要なスコープで接続し直してください。

# 読み取りだけの棚卸し用途。書き込み権限を要求しない
Connect-MgGraph -Scopes 'User.Read.All', 'Organization.Read.All' -NoWelcome

Get-MgContext | Format-List Account, TenantId, Scopes, AuthType   # 現在の接続を確認
Disconnect-MgGraph

ここが移行時に一番差が出るところです。Get-MsolUser 時代は「管理者アカウントでログインすれば全部できる」でしたが、Graphでは操作ごとに必要なスコープが定義されており、同意した範囲でしか動きません。これは制約ではなく安全装置です。棚卸しスクリプトが誤って書き込みを行う事故は、.Read.All しか同意していなければ起こりません。

スコープ設計の原則は3つ

  • 読み取り用途に書き込みスコープを要求しない(User.Read.All で足りるなら User.ReadWrite.All を求めない)
  • 用途ごとにアプリ登録を分ける(棚卸し用、アカウント作成用、ライセンス管理用)
  • 同意は管理者が意識的に行う(一度与えた同意はテナントに残り続ける)

必要なスコープが分からないときは Find-MgGraphPermission で候補を探し、Find-MgGraphCommand でコマンドが要求する権限を確認します。5

5. 無人実行 ── アプリ登録+証明書

タスクスケジューラから毎晩動かすなら、対話ログインは使えません。アプリ登録(サービスプリンシパル)と証明書によるアプリ専用認証に切り替えます。6

手順は「アプリを登録する → アプリケーション許可に同意する → 証明書を準備する → 実行アカウントで接続を確かめる」の4段階です。アプリ登録ができただけでは、まだ業務処理は動きません。以下では、画面の設定と実行側の準備を分けて説明します。6

5.1 アプリを登録する

Microsoft Entra管理センター(https://entra.microsoft.com)で、次をたどります。

ID > アプリケーション > アプリの登録 > 新規登録

名前(例: M365-Inventory-Batch)を入力し、サポートされているアカウントの種類は「この組織ディレクトリのみに含まれるアカウント(シングルテナント)」を選んで「登録」を押します。リダイレクトURIは、スクリプトからの無人実行だけなら設定不要です。

登録できたら、接続に使う2つのIDを控える

登録後の「概要」ページに表示される次の2つを控えます。これが接続に使う値です。

概要ページの表示名 Connect-MgGraph のパラメーター
アプリケーション (クライアント) ID -ClientId
ディレクトリ (テナント) ID -TenantId

なお、Entra管理センターのナビゲーション名称は改称されることがあります。左メニューの表記が変わっていても、たどり着く先のページ名は「アプリの登録」です。これを目印にしてください。

5.2 アプリケーション許可を追加し、管理者の同意を与える

同じアプリの画面で、次をたどります。

管理 > APIのアクセス許可 > アクセス許可の追加 > Microsoft Graph > アプリケーションの許可

ここで「アプリケーションの許可」を選ぶことが要点です。隣の「委任されたアクセス許可」を選ぶと、無人実行では機能しません。必要な権限(棚卸しなら User.Read.All など)にチェックを入れ、「アクセス許可の追加」で確定します。

許可の追加と、管理者の同意は別の操作

この時点ではまだ使えません。同じ画面上部の

「(テナント名) に管理者の同意を与えます」

ボタンを押して確定します。押した後、一覧の「状態」列が「(テナント名) に付与されました」という表示に変われば完了です。ここを忘れて「権限は付けたのに403で落ちる」というのが定番の詰まりどころです。

5.3 証明書を作る・アップロードする・置く

証明書の準備は、作成・公開鍵の登録・秘密鍵の配置に分けて考えます。Entra側へ渡すものと、実行環境に置くものを混同しないことが大切です。

作成する: 拇印と公開鍵を用意する

自己署名証明書で十分です。作成はPowerShellの New-SelfSignedCertificate で行います。次の例は現在のユーザーのストアに作るので、無人実行に使う場合は実行アカウントを先に決めてください。出力先の C:\temp も例であり、存在して書き込めるフォルダーを指定します。

# 【1】証明書を作る(有効期限は2年。運用に合わせて調整する)
$cert = New-SelfSignedCertificate `
    -Subject           'CN=M365-Inventory-Batch' `
    -CertStoreLocation 'Cert:\CurrentUser\My' `
    -KeySpec           Signature `
    -KeyExportPolicy   Exportable `
    -KeyAlgorithm      RSA `
    -KeyLength         2048 `
    -HashAlgorithm     SHA256 `
    -NotAfter          (Get-Date).AddYears(2)

# 【2】接続に使う拇印(サムプリント)を控える
$cert.Thumbprint

# 【3】アップロード用に公開鍵だけを .cer に書き出す(秘密鍵は含まれない)
Export-Certificate -Cert $cert -FilePath 'C:\temp\M365-Inventory-Batch.cer'

登録する: アップロードするのは公開鍵だけ

書き出した .cer を、Entra管理センターのアプリ画面で

管理 > 証明書とシークレット > 証明書 タブ > 証明書のアップロード

からアップロードします。アップロードするのは .cer(公開鍵)だけです。秘密鍵を含む .pfx を上げてはいけません。

配置する: タスクの実行アカウントから使えるストアへ

秘密鍵をどこに置くかは、タスクスケジューラの実行アカウントに合わせます。

実行アカウント 証明書ストア 接続時の渡し方 補足
特定のユーザー/サービスアカウント Cert:\CurrentUser\My(そのアカウントで作成・インポート) -CertificateThumbprint 作成したアカウント以外からは見えない
SYSTEM、または複数アカウントから使う Cert:\LocalMachine\My -Certificate(自分で読み込んで渡す) 作成には管理者権限が必要。実行アカウントに秘密鍵の読み取り許可を与える

ここで見落としやすいのが、-CertificateThumbprint と -CertificateSubjectName は現在のユーザーの証明書ストアしか探さないことです。4 証明書を Cert:\LocalMachine\My に置いた場合、拇印を渡しても見つかりません。ローカルコンピューターのストアを使うなら、自分で読み込んで -Certificate に渡します。

以下は LocalMachine を使う場合の接続例です。$clientId と $tenantId には5.1で控えた値を先に設定し、A1B2C3D4E5F6... は実際の拇印へ置き換えます。次の5.4の CurrentUser 用の例とは、置き場所に応じて使い分けてください。

# LocalMachine に置いた場合はこちら
$cert = Get-ChildItem -Path 'Cert:\LocalMachine\My\A1B2C3D4E5F6...'
Connect-MgGraph -ClientId $clientId -TenantId $tenantId -Certificate $cert -NoWelcome

「手元では動いたのにタスクスケジューラでだけ証明書が見つからない」というトラブルは、ほぼこの置き場所と渡し方の食い違いです。

5.4 スクリプトから接続する

次は実行アカウントの CurrentUser ストアに証明書がある場合の例です。IDと拇印は例示値のまま使わず、5.1〜5.3で準備した値に置き換えます。

接続後は AuthType が AppOnly であることを確かめ、try の中で業務処理を行い、finally で切断する構成です。ここに書いてあるのは接続の枠組みで、棚卸しなどの処理自体はまだ入っていません。

# 無人実行用の接続(対話なし)
$connect = @{
    ClientId              = '11111111-2222-3333-4444-555555555555'
    TenantId              = '66666666-7777-8888-9999-000000000000'
    CertificateThumbprint = 'A1B2C3D4E5F6...'    # 実行アカウントの CurrentUser ストアにある証明書
    NoWelcome             = $true
}
Connect-MgGraph @connect

# 接続形態の確認: AuthType が AppOnly ならアプリ専用で接続できている
Get-MgContext | Format-List AppName, ClientId, TenantId, AuthType, Scopes

try {
    # 業務処理
}
finally {
    Disconnect-MgGraph
}

初回は、実際にタスクを登録する前にタスクスケジューラの実行アカウントで上のスクリプトを走らせて確認してください。タスクスケジューラ側の落とし穴(実行アカウント、「ユーザーがログオンしているかどうかにかかわらず実行する」の設定、作業ディレクトリなど)は「タスクスケジューラのタスクが実行されない・0x1で終わる」にまとめています。

5.5 注意点

無人化の前に、アプリケーション許可を設定し直す

(1) 委任とアプリ専用では必要な権限が別物です。対話的に -Scopes 'User.Read.All' で動いていたスクリプトを無人化するときは、アプリ登録側にアプリケーション許可として同じ種類の権限を付け直し、管理者同意が必要です。6

運用開始前に、証明書の更新方法を決める

(2) 証明書には期限があります。期限切れの当日に夜間バッチが全滅する、というのは実際によくある事故です。有効期限をカレンダーに登録し、更新手順を文書化してください。資格情報の保管については「PowerShellでの資格情報の安全な扱い」もあわせて参照してください。クライアントシークレットでも接続はできますが、平文で持ち回るリスクと期限管理の煩雑さから、証明書を推奨します。

6. データの取り方 ── -All / -Filter / -Property

取得では、「どのユーザーか」「どの項目か」「全ページが必要か」を別々に指定します。表示する列の選択は、その後の処理です。

対象・項目・ページ数を、取得側で指定する

Graphはページングを前提としたAPIです。既定では1ページ分しか返らないため、全件が必要なら -All を付けます。7

# 【NG】ページングを考えず、クライアント側で絞る(遅い・取りすぎ・スロットリングの原因)
Get-MgUser | Where-Object { $_.Department -eq '営業部' }

# 【OK】サーバー側で絞り、必要な項目だけ、全ページを取得する
Get-MgUser -All -Filter "department eq '営業部'" `
           -Property Id, DisplayName, UserPrincipalName, AccountEnabled, Department |
    Select-Object DisplayName, UserPrincipalName, AccountEnabled

-Propertyは取得、Select-Objectは出力列の指定

  • -Filter はOData式で、サーバー側で絞り込みます。Where-Object は取得した後のローカル処理なので、取得量は減らせません。上のNG例では -All もないため、全ページを調べたことにもなりません。
  • -Property で取得する項目を絞ると応答が軽くなります。既定では返らないプロパティも、明示すれば返るものがあります。
  • 出力したい項目は Select-Object にも指定します。 取得と表示は別です。上のOK例では Id と Department も取得していますが、画面には DisplayName、UserPrincipalName、AccountEnabled の3列だけを出します。

件数だけ欲しい場合は、返すユーザー数を最小にする

高度なクエリや、次の件数取得の例では、-ConsistencyLevel eventual と -CountVariable を併用します。startsWith や endsWith が使える条件は対象プロパティによって異なり、startsWith を使うだけで一律に高度なクエリになるわけではありません。実際の対応条件は公式の表で確認してください。710

# 有効なユーザー数だけを数える(全件を取得せずカウントを得る)
Get-MgUser -Filter 'accountEnabled eq true' -ConsistencyLevel eventual -CountVariable total -Top 1 | Out-Null
"有効ユーザー: $total 件"

-Top 1 | Out-Null は見慣れないイディオムなので補足します。-CountVariable に入るのは条件に一致する全体の件数であり、-Top の値には左右されません。-Top が決めるのは1回の応答で返ってくるオブジェクトの数だけです。つまり -Top 1 は「1件だけ数える」指定ではなく、カウントを得るために1回は投げざるを得ない要求で、返ってくる実データを最小にするための指定です。省略すると既定のページ分のユーザーオブジェクトが返り、それを Out-Null で捨てることになります。この件数取得や、高度なクエリの条件に当たる検索では -ConsistencyLevel eventual が必要です。演算子名だけで判断せず、対象プロパティと組み合わせて確認します。710

429が返ったら待つ。その前に要求数を減らす

大量アクセスをすると調整(スロットリング) され、HTTP 429が返ります。公式の指針は「Retry-After ヘッダーの秒数に従って待ってから再試行する」ことです。8 SDKのコマンドレットはある程度の再試行を内部で行いますが、数千件規模のループを回すときは、そもそも取得回数を減らす(-Filter と -Property で絞る、1回で必要な情報を取る)ほうが確実です。リトライ設計そのものは「PowerShellのエラー処理と再実行設計」を参照してください。

7. 定番レシピ3つ

ここからは、準備した接続を業務処理に使います。(1)と(2)は読み取り、(3)はアカウントを変更する操作です。各コードは独立した用途の例であり、上からすべて続けて実行する手順ではありません。無人実行へ組み込むときは、5章の認証と権限の準備を先に済ませます。

(1) ユーザー棚卸しをCSVに出す

取得前に確認すること

この例は、ユーザー情報とサインインの情報を取得し、日付付きのCSVへ出力します。SignInActivity には User.Read.All に加えて AuditLog.Read.All とテナントのライセンス要件があるため、単なるユーザー一覧より前提が増えます。11 出力先の D:\棚卸 は、自社の保存先へ置き換えてください。

ユーザー一覧を取得し、CSVへ書き出す

# SignInActivity(最終サインイン日時)は User.Read.All だけでは取得できず、
# AuditLog.Read.All が別途必要。取らないなら -Property から外すこと
Connect-MgGraph -Scopes 'User.Read.All', 'AuditLog.Read.All' -NoWelcome

$users = Get-MgUser -All -Property Id, DisplayName, UserPrincipalName, AccountEnabled,
                              Department, JobTitle, CreatedDateTime, SignInActivity |
    Select-Object DisplayName, UserPrincipalName, Department, JobTitle, AccountEnabled,
                  @{ n = '作成日'; e = { $_.CreatedDateTime } },
                  # 休眠判定には「成功したサインイン」を使う。LastSignInDateTimeは
                  # 対話サインインの「試行」で、失敗も含み、非対話は含まない
                  @{ n = '最終サインイン成功'; e = { $_.SignInActivity.LastSuccessfulSignInDateTime } },
                  @{ n = '最終対話サインイン試行'; e = { $_.SignInActivity.LastSignInDateTime } },
                  @{ n = '最終非対話サインイン'; e = { $_.SignInActivity.LastNonInteractiveSignInDateTime } }

# 日本語を含むCSVはUTF-8(BOM付き)にするとExcelで開いても化けない。
# エンコーディング名はバージョンで違う: 7以降は utf8BOM、5.1は UTF8(BOM付き)
$enc = if ($PSVersionTable.PSVersion.Major -ge 6) { 'utf8BOM' } else { 'UTF8' }
$users | Export-Csv -Path "D:\棚卸\users_$(Get-Date -f yyyyMMdd).csv" -Encoding $enc -NoTypeInformation

CSVの列と、値が空のときの確認点

出力されるCSVの列構成は、Select-Object に書いた順序と名前がそのままヘッダー行になります。Export-Csv は既定で全項目を引用符で囲むため、1行目は次の形です。

"DisplayName","UserPrincipalName","Department","JobTitle","AccountEnabled","作成日","最終サインイン成功","最終対話サインイン試行","最終非対話サインイン"

2行目以降に、同じ順序でユーザーごとの値が並びます。期待した列が空なら、まず -Property に必要な取得項目があるか、Select-Object が正しい値を参照しているか、権限が足りているかを確認します。 特に 最終サインイン成功 以降の3列がすべて空なら、次に述べる AuditLog.Read.All とライセンスの前提を確認してください。空欄だけで休眠アカウントとは断定せず、取得条件とフィールドの意味を読み分けます。

休眠判定では「試行」と「成功」を分ける

SignInActivity は、休眠アカウントの洗い出しに効きます。ただしどのフィールドを見るかが重要です。LastSignInDateTime は対話サインインの試行(失敗も含む)を記録し、アプリやサービスによる非対話サインインを含みません。これだけで判定すると、攻撃者のログイン失敗で「使われている」ように見えたり、実際に動いているサービスアカウントが「休眠」に見えたりします。休眠判定には、成功した対話・非対話サインインを反映する LastSuccessfulSignInDateTime を使ってください。11

サインイン情報が不要なら、取得項目から外す

ただしこのプロパティだけは User.Read.All では取得できず、AuditLog.Read.All が追加で必要です(加えてテナント側のライセンス要件もあります)。権限が足りないとエラーになるか値が空になるので、使わないなら -Property から外してください。必要な権限は Find-MgGraphPermission で確認できます。11

CSVの文字コードの扱いは「PowerShellでExcel・CSV業務処理を自動化する」にまとめています。

(2) ライセンスの消費状況を集計する

この例はSKUごとに、購入数・割当済み数・差分の「空き」を表示します。Sort-Object 空き により、空きの少ない順に並びます。ライセンスを割り当てたり解除したりする処理ではなく、消費状況を確認する例です。

Connect-MgGraph -Scopes 'Organization.Read.All' -NoWelcome

Get-MgSubscribedSku | Select-Object `
    SkuPartNumber,
    @{ n = '購入数';   e = { $_.PrepaidUnits.Enabled } },
    @{ n = '割当済み'; e = { $_.ConsumedUnits } },
    @{ n = '空き';     e = { $_.PrepaidUnits.Enabled - $_.ConsumedUnits } } |
    Sort-Object 空き

「余っているライセンスがあるのに追加購入していた」「退職者のライセンスが解放されていない」は、月次で回すだけで防げます。

(3) 退職者処理(サインインブロックとセッション失効)

実行前に、対象と権限を確認する

このレシピは確認用の表示だけではなく、実際にアカウントを無効化し、セッションを失効させます。 例示のUPNを置き換えるだけで本番実行せず、対象者・専用アプリ・必要な許可・担当者の管理者ロールを先に確認してください。

アカウントの即時削除は避け、まずサインインを止めるのが実務の定石です。メールボックスやOneDriveの引き継ぎが終わる前に削除すると復旧に手間がかかります。この種の「取り返しがつかない操作」は、-WhatIf に対応した自作関数でラップし、対象一覧を先に出力して確認してから実行する形にしておくと安全です(「PowerShellの引数設計とモジュール化」)。

下のコード自体には、その -WhatIf 用のラッパーは入っていません。また、セッション失効を要求しても発行済みのアクセストークンが即座にすべて使えなくなるとは限らない点は、このレシピの末尾で説明します。

まず、委任とアプリ専用の前提を比べる

次のコードは「委任(対話サインイン)」での実行例です。-Scopes を指定しているのがその印で、実行するとブラウザーでのサインインが求められます。無人実行にするには、5章のとおりアプリ専用に組み替える必要があります。両者の違いを先に整理します。

  委任(下のコード例) アプリ専用(無人実行)
誰として動くか サインインした担当者本人 アプリ(サービスプリンシパル)自身
Connect-MgGraph の指定 -ClientId + -Scopes -ClientId + -TenantId + -CertificateThumbprint
証明書 不要 必須(またはクライアントシークレット)
権限の種類 委任されたアクセス許可 アプリケーションの許可(要・管理者の同意)6
追加で必要なもの サインインした本人のEntra管理者ロール(後述)12 対象が管理者ならアプリ自体への上位ロール割り当て12
向く場面 その場の個別対応、-WhatIf での事前確認 夜間バッチ、人事システム連携

委任で実行する: サインインのブロックとセッション失効

# ── 委任(対話サインイン)での実行例 ──
# 実行するとサインイン画面が出る。証明書は不要
#
# 書き込みを伴うので、専用のアプリ登録・専用スコープで実行する。
# -ClientId を省略するとSDK既定の共有アプリで接続され、同意した権限が
# そのアプリ(組織で共有)に積み上がってしまう。退職者処理のような
# 破壊的な操作こそ、専用のアプリ登録に権限を隔離する
#
# 操作ごとに必要な権限が違うため、両方の操作分をまとめて要求する
#   accountEnabled の変更 → User.EnableDisableAccount.All(最小権限)
#   サインインセッションの失効 → User.RevokeSessions.All(最小権限)
# いずれも User.ReadWrite.All でも実行できるが、権限が広くなる
$connect = @{
    ClientId  = '99999999-aaaa-bbbb-cccc-dddddddddddd'   # 退職者処理専用のアプリ登録
    TenantId  = '66666666-7777-8888-9999-000000000000'
    Scopes    = 'User.Read.All', 'User.EnableDisableAccount.All', 'User.RevokeSessions.All'
    NoWelcome = $true
}
Connect-MgGraph @connect

# Revoke-MgUserSignInSession は Microsoft.Graph.Users.Actions が必要
Import-Module Microsoft.Graph.Users.Actions

$upn = 'taro.yamada@example.co.jp'
$user = Get-MgUser -UserId $upn -Property Id, DisplayName, AccountEnabled

# 1. サインインをブロック(削除は猶予期間を置いてから)
Update-MgUser -UserId $user.Id -AccountEnabled:$false

# 2. 更新トークンとブラウザーのセッションCookieを失効させる
#    注意: 発行済みのアクセストークンは有効期限まで使える場合がある(後述)
Revoke-MgUserSignInSession -UserId $user.Id

Write-Host "$($user.DisplayName) のサインインを停止しました"

接続先: 書き込み用途は専用のアプリ登録へ

ここで押さえるべき点が3つあります。ひとつは接続先のアプリ登録です。-ClientId を省略するとMicrosoft Graph PowerShell SDKの既定アプリで接続され、同意した権限は組織で共有されるそのアプリに記録されます。「用途ごとにアプリ登録を分ける」という4章の原則を実現するには、-ClientId で自社のアプリ登録を明示する必要があります。4

スコープ: 2つの操作に、それぞれ必要な許可を用意する

もうひとつはスコープです。Graphでは操作ごとに必要な権限が定義されており、「ユーザーを書き込めるから何でもできる」わけではありません。accountEnabled の変更とサインインセッションの失効はそれぞれ専用の最小権限を持つため、片方だけ同意して実行すると、接続には成功しても途中のコマンドで権限不足エラーになります。各操作に必要な権限は Find-MgGraphPermission と、対応するGraph APIリファレンスの権限表で確認してください。129

管理者ロール: 同意しただけでは本人の権限は上がらない

そして3つ目に、委任実行ではスコープだけでは足りません。上のように利用者本人としてサインインして実行する場合、accountEnabled の変更にはMicrosoft Entraの管理者ロールも必要です。委任の権限は「アプリがそのユーザーの代わりに何をできるか」を決めるだけで、サインインしたユーザー自身の権限を引き上げはしないためです。権限に同意していてもロールがなければ、実行時に403で失敗します。ドキュメントは、テナント内のすべての管理者に対してこのプロパティを更新できる最小のロールを Privileged Authentication Administrator とし、一般には「対象より上位の管理者ロールが必要」と定めています。12 退職者処理の対象が一般利用者か管理者かで必要なロールが変わるため、運用担当のアカウントに何を割り当てるかは事前に決めておいてください。証明書によるアプリ専用実行でも、対象が管理者の場合はアプリ自体への上位ロールの割り当てが必要です。12

無人実行へ組み替える: 権限と証明書を準備してから接続を替える

アプリケーション許可への管理者同意、証明書、必要な管理者ロールを用意した後は、コード上で置き換えるのは接続部分です。準備まで不要になるわけではありません。次は5.3で説明した実行アカウントの CurrentUser ストアを使う例で、LocalMachine の場合は同節の -Certificate の渡し方に合わせます。

# ── アプリ専用(無人実行)に組み替える場合 ──
# 事前準備: アプリ登録の「APIのアクセス許可 > アプリケーションの許可」に
#   User.Read.All / User.EnableDisableAccount.All / User.RevokeSessions.All
# を追加し、管理者の同意を与えておく(5.2)。証明書は5.3で作ったものを使う
$connect = @{
    ClientId              = '99999999-aaaa-bbbb-cccc-dddddddddddd'
    TenantId              = '66666666-7777-8888-9999-000000000000'
    CertificateThumbprint = 'A1B2C3D4E5F6...'   # -Scopes は指定しない
    NoWelcome             = $true
}
Connect-MgGraph @connect

# これ以降(Import-Module 〜 Revoke-MgUserSignInSession)は委任版とまったく同じ

失効の効果: 発行済みアクセストークンの即時停止とは限らない

Revoke-MgUserSignInSession の効果範囲も正確に理解しておく必要があります。このコマンドが無効化するのは更新トークンとブラウザーのセッションCookieであり、すでに発行済みのアクセストークンは、その有効期限が切れるまで使える場合があります。9 即時遮断を求めるなら、継続的アクセス評価(CAE)に対応したアプリケーション・リソースであることが前提になります。「revokeしたから即座に全アクセスが止まる」と考えず、重要なケースではアカウント無効化と併用し、反映までの時間差を織り込んでください。

8. 実務の定石(判断表)

最後に、移行や運用を決めるときの基準を一覧にします。単に接続できたかではなく、権限・取得量・資格情報の更新・変更前の確認までを一組で見直してください。

論点 選択肢 判断の目安
モジュール Graph SDK / Entra PowerShell ID管理中心・AzureADからの移行ならEntra、幅広いワークロードならGraph SDK2
インストール 全部入り / サブモジュールのみ 起動時間と依存を抑えるためサブモジュール単位で3
認証(対話) 管理者アカウント任せ / 最小スコープ 棚卸しは .Read. 系のみ。同意はテナントに残る4
認証(無人) クライアントシークレット / 証明書 証明書を推奨。期限管理と更新手順を先に決める6
権限の粒度 全部入り1アプリ / 用途ごとにアプリ登録 事故時の影響範囲を限定できる
一覧取得 Where-Object / -Filter + -All + -Property サーバー側で絞る。取得量がそのまま速度と安定性になる7
429対策 即時リトライ / Retry-After に従う 公式指針。そもそも取得回数を減らすのが先8
危険な操作 直接実行 / -WhatIf + 対象一覧の事前確認 退職者処理・一括削除は必ず対象を目視できる形に

9. まとめ

  • MSOnlineとAzureADは廃止済みです。移行先はMicrosoft Graph PowerShell SDK、またはその上のMicrosoft Entra PowerShellです。
  • Microsoft.Graph はメタモジュールなので、実務では認証+使うワークロードのサブモジュールだけを入れます。
  • 接続はスコープの最小化が原則です。棚卸し用途に書き込み権限を要求せず、用途ごとにアプリ登録を分けます。
  • 無人実行はアプリ登録+証明書。委任とアプリ専用では必要な権限が別物である点、証明書に期限がある点が事故の元です。
  • データ取得は -All(ページング)、-Filter(サーバー側の絞り込み)、-Property(項目の限定)の3点セット。429は Retry-After に従います。
  • 棚卸し・ライセンス集計に加え、退職者処理が適切に行われているかも月次で見直すと、ライセンスの無駄と放置アカウントというよくある2大リスクを減らせます。退職者のサインイン停止を月次の棚卸しまで待つ、という意味ではありません。

サンプルコードのダウンロード

この記事で扱ったコードは、スクリプトとして使える形にまとめて配布しています。接続・休眠アカウント抽出・ライセンス集計・退職者処理が入っています。設定値と権限の準備、検証環境での確認は必要です。

サンプルコードをダウンロード(zip)

この記事のサンプルは、Windowsやテナントに依存するため実行検証はしていません。構文解析とPSScriptAnalyzerによる静的解析までは全ファイルに対して実施していますが、動作は必ずご自身の検証機で確認してください。

# 構文解析 + 静的解析(Windows以外でも実行できる)
./Invoke-SampleTests.ps1

設定値(パス、サーバー名、テナントIDなど)は例です。そのまま本番環境で実行せず、自社の環境に合わせて読み替えてください。

関連記事

関連する相談領域

合同会社小村ソフトでは、Microsoft 365運用スクリプトのGraph移行、アプリ登録と権限設計のレビュー、棚卸し・退職者処理といった定型業務の自動化を扱っています。

参考リンク

  1. Microsoft Community Hub(Microsoft Entra Blog), Action required: MSOnline and AzureAD PowerShell retirement - 2025 info and resources. MSOnlineとAzureADの両PowerShellモジュールが2024年3月30日に非推奨となったこと、MSOnlineの廃止が2025年春に実施され2025年5月30日をもって提供終了となること、AzureADが2025年3月30日でサポート終了となりその後廃止されること、移行先がMicrosoft Graph PowerShell SDKおよびMicrosoft Entra PowerShellであることについて。 ↩ ↩2 ↩3

  2. Microsoft Learn, What is Microsoft Entra PowerShell?. Microsoft Entra PowerShellがMicrosoft Graph PowerShell SDK上に構築されたシナリオ指向のモジュールであり、Graph PowerShell SDKのコマンドレットと相互運用できること、AzureADモジュールからの移行を支援する後方互換オプションを提供することについて。GA(一般提供)の告知はMicrosoft Entra PowerShell module now generally available(2025年3月)。 ↩ ↩2 ↩3 ↩4

  3. Microsoft Learn, Install the Microsoft Graph PowerShell SDK. Microsoft.Graphがサブモジュール群を含むメタモジュールであること、必要なサブモジュールだけを個別にインストールできること、Microsoft.Graph.Authenticationが認証に必須であること、対応するPowerShellのバージョンについて。 ↩ ↩2 ↩3 ↩4

  4. Microsoft Learn, Connect-MgGraph. -Scopesによる委任アクセス許可の要求、-ClientId / -TenantId / -CertificateThumbprintによるアプリ専用認証、-CertificateThumbprintと-CertificateSubjectNameが現在のユーザーの証明書ストアから証明書を取得すること(ローカルコンピューターのストアを使う場合は自分で読み込んで-Certificateに渡すこと)、Get-MgContextによる現在の接続情報の確認、Disconnect-MgGraphによる切断について。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6

  5. Microsoft Learn, Find Microsoft Graph PowerShell commands and permissions. Find-MgGraphCommandによるコマンドの所属モジュール・必要な権限・対応APIの検索、Find-MgGraphPermissionによる権限名の検索について。 ↩ ↩2

  6. Microsoft Learn, Use app-only authentication with the Microsoft Graph PowerShell SDK. アプリ登録・アプリケーション許可・管理者の同意という手順、証明書を用いた無人認証の構成、委任アクセス許可とアプリケーションアクセス許可の違いについて。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10

  7. Microsoft Learn, Paging Microsoft Graph data in your app. Microsoft Graphの応答がページングされること、PowerShell SDKで-Allを指定すると全ページを取得できること、$filterや$selectに相当する-Filter / -Propertyによるサーバー側での絞り込み、高度なクエリでの-ConsistencyLevel eventualと件数取得について。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6

  8. Microsoft Learn, Microsoft Graph throttling guidance. リソース単位のスロットリングでHTTP 429が返ること、応答のRetry-Afterヘッダーに指定された秒数を待ってから再試行すべきこと、要求数そのものを減らす設計が推奨されることについて。 ↩ ↩2 ↩3 ↩4

  9. Microsoft Learn, user: revokeSignInSessions (Microsoft Graph API). サインインセッションの失効に必要なアクセス許可としてUser.RevokeSessions.Allが最小権限に挙げられていること、管理者の同意が必要であること、無効化の対象が更新トークンとブラウザーのセッションCookieであり、発行済みのアクセストークンは有効期限まで使用され得ること(即時反映には継続的アクセス評価が関わること)について。 ↩ ↩2 ↩3

  10. Microsoft Learn, Advanced query capabilities on Microsoft Entra ID objects. 高度なクエリではConsistencyLevelと$countの指定が必要になること、演算子の対応が対象プロパティによって異なり、startsWithは既定の条件で使えるプロパティもあることについて。 ↩ ↩2

  11. Microsoft Learn, signInActivity resource type. ユーザーのsignInActivityプロパティの取得にAuditLog.Read.AllとUser.Read.Allの両方のアクセス許可が必要であること、テナントのライセンス要件があること、lastSignInDateTimeが対話的サインインの試行(成功・失敗を含む)を表すのに対し、lastSuccessfulSignInDateTimeが成功した対話・非対話サインインを、lastNonInteractiveSignInDateTimeが非対話サインインを表すことについて。 ↩ ↩2 ↩3

  12. Microsoft Learn, Update user (Microsoft Graph API). ユーザーの更新に必要なアクセス許可がプロパティ単位で定義されており、accountEnabledの変更にはUser.EnableDisableAccount.Allが最小権限として挙げられていること、より広いUser.ReadWrite.Allでも実行できることについて。 委任シナリオでは適切なスコープに加えてMicrosoft Entraの管理者ロールが必要であり、テナント内のすべての管理者に対してaccountEnabledを更新できる最小のロールがPrivileged Authentication Administratorであること、一般には対象より上位の管理者ロールが必要であること、アプリ専用シナリオでも対象が管理者の場合はアプリに上位の管理者ロールの割り当てが必要であることについて。 ↩ ↩2 ↩3 ↩4 ↩5

同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。

このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。

よくある質問

この記事のテーマについて、相談時によくある質問をまとめています。

AzureADモジュールやMSOnlineモジュールは、もう使えないのですか?
はい、いずれも廃止済みです。MSOnlineとAzureADの両モジュールは2024年3月30日に非推奨(deprecated)となり、MSOnlineは2025年5月30日をもって提供終了、AzureADも2025年3月30日でサポートが終了し、その後に廃止されました。まだ動いているように見えるスクリプトがあっても、それはいつ止まってもおかしくない状態です。移行先はMicrosoft Graph PowerShell SDK、またはその上に構築されたMicrosoft Entra PowerShellモジュール(2025年3月にGA)です。
Microsoft.Graphモジュールをインストールすると重いのですが、軽くできますか?
できます。Microsoft.Graphはメタモジュールで、配下に多数のサブモジュールを持つため、全体を入れるとインストールにも読み込みにも時間がかかります。実務では、使うワークロードのサブモジュールだけを入れるのが現実的です。ユーザー管理ならMicrosoft.Graph.Users、グループならMicrosoft.Graph.Groups、認証は必須のMicrosoft.Graph.Authentication、といった具合です。どのコマンドがどのモジュールに属するかはFind-MgGraphCommandで調べられます。
タスクスケジューラから無人実行したいのですが、対話ログインが出てしまいます。
アプリ登録(サービスプリンシパル)と証明書によるアプリ専用認証に切り替えてください。Microsoft Entra IDでアプリを登録し、必要なアプリケーション許可(Application permissions)に管理者の同意を与えたうえで、Connect-MgGraphに-ClientId・-TenantId・-CertificateThumbprintを渡すと、対話なしで接続できます。クライアントシークレットより証明書のほうが安全で、有効期限管理も明確です。証明書は実行アカウントの証明書ストアに置き、期限切れ前に更新する運用を必ず決めてください。
Connect-MgGraphの-Scopesには何を指定すればよいですか?
実行したいコマンドが要求する最小の権限だけを指定します。何が必要かはFind-MgGraphPermissionやコマンドのドキュメントで確認でき、読み取りだけならUser.Read.Allのように.Read.系で足ります。棚卸しや監査が目的なら書き込み権限を要求しないでください。一度同意した権限はテナントに記録されるため、「とりあえずDirectory.ReadWrite.Allで同意」は将来のリスクになります。用途ごとにアプリ登録を分け、権限も分けるのが安全です。
Get-MgUserで件数の多いテナントを扱うと、途中までしか取れません。
Microsoft Graphの応答はページングされるためです。-Allを付けると全ページを自動的にたどって取得します。加えて、大量取得では調整(スロットリング)を受けて429応答が返ることがあるので、必要な項目だけを-Propertyで絞り、フィルターはクライアント側のWhere-ObjectではなくOData(-Filter)でサーバー側に押し付けるのが基本です。件数だけが欲しい場合は、-ConsistencyLevel eventualと-CountVariableの組み合わせでカウントだけ取得できます。

著者プロフィール

記事の著者プロフィールページです。

小村 豪

合同会社小村ソフト 代表

Windows ソフト開発、技術相談、不具合調査を中心に、既存資産が残る案件や原因が見えにくい障害調査に強みがあります。

ブログ一覧に戻る