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)
この記事はZenodoにアーカイブされています。常に最新版へ解決されるDOIと、いま表示している版に固定されたDOIの両方を下に示します。
小村 豪(2026)「Microsoft Graph PowerShell入門 ── AzureAD・MSOnline廃止後のMicrosoft 365運用」合同会社小村ソフト. https://doi.org/10.5281/zenodo.21547441 https://staging.comcomponent.com/blog/powershell-microsoft-graph-introduction/
- DOI(最新版)
- 10.5281/zenodo.21547441
- DOI(この版)
- 10.5281/zenodo.21733148
Microsoft 365の運用を自動化している現場にとって、2024年から2025年にかけての一番大きな変化はAzureADモジュールとMSOnlineモジュールの廃止でした。「退職者処理」「ライセンス棚卸し」「新入社員のアカウント作成」といった定型処理を Get-MsolUser や Get-AzureADUser で書いていた情シスは少なくないはずで、これらは順次動かなくなります。
移行先は Microsoft Graph PowerShell SDK です。ただし、単なるコマンド名の置き換えではありません。認証の考え方(スコープと同意)、データの取り方(OData のフィルターとページング)、無人実行の作り方(アプリ登録と証明書)まで、設計の前提が変わります。ここを理解せずに機械的に置き換えると、「動くけれど過剰な権限で動いている」「件数が多いテナントで途中までしか取れない」といった別の問題を抱え込みます。
この記事では、社内のMicrosoft 365運用をPowerShellで自動化している情シス担当者を対象に、廃止の経緯の整理、接続とスコープ設計、無人実行の構成、そして棚卸し・ライセンス集計・退職者処理という定番3レシピまでを、実務で使える形にまとめます。
1. まず結論
- 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
- 一覧取得は
-All、絞り込みは-Filter、項目は-Property。クライアント側のWhere-Objectで絞ると、無駄な取得とスロットリングを招きます。7 - 大量アクセスは調整(スロットリング)されます。429応答では
Retry-Afterに従って待つのが公式の指針です。8 - アプリ登録は用途ごとに分けます。「全部入りの1アプリ」は権限が肥大化し、事故時の影響範囲も広がります。
この記事の知識マップ
この記事はMicrosoft Graph PowerShell SDKへの移行を扱い、廃止されたAzureAD・MSOnlineモジュールの後継であるGraph SDKと、その上に構築されたMicrosoft Entra PowerShellを対比します。Connect-MgGraphは対話サインインの委任アクセス許可と、アプリ登録・証明書によるアプリケーションアクセス許可の2形態を持ち、証明書の保管場所によって-CertificateThumbprintが証明書を見つけられないことがあります。Get-MgUserなどのデータ取得はWhere-Objectでの後段絞り込みを避け、-Allと-Filter/-Propertyでサーバー側に絞り込ませることでMicrosoft Graphの調整(スロットリング)を避けます。退職者処理ではアカウント無効化とRevoke-MgUserSignInSessionを組み合わせ、即時遮断には継続的アクセス評価が必要になります。
flowchart LR
accTitle: Microsoft Graph PowerShellの知識マップ
accDescr: AzureAD・MSOnlineモジュールの廃止とMicrosoft Graph PowerShell SDKへの移行、Connect-MgGraphにおける委任とアプリ専用(証明書)の2つの認証形態、証明書ストアの違いによる落とし穴、Get-MgUserでのページングとサーバー側絞り込みによるスロットリング回避、退職者処理とセッション失効の関係を示す図
microsoft_graph_powershell_sdk["Microsoft Graph PowerShell SDK"]
connect_mggraph["Connect-MgGraph"]
azuread_module["AzureADモジュール"]
msonline_module["MSOnlineモジュール"]
microsoft_entra_powershell["Microsoft Entra PowerShell"]
entra_id["Microsoft Entra ID"]
delegated_permission["委任(Delegated)アクセス許可"]
application_permission["アプリケーション(Application)アクセス許可"]
app_registration["アプリ登録(サービスプリンシパル)"]
admin_consent["管理者の同意"]
client_certificate["クライアント証明書"]
mggraph_certificatethumbprint_param["-CertificateThumbprint(Connect-MgGraph)"]
localmachine_store["コンピューターの証明書ストア"]
thumbprint["拇印(サムプリント)"]
currentuser_store["ユーザーの証明書ストア"]
task_scheduler["タスクスケジューラの無人実行"]
get_mguser["Get-MgUser"]
mggraph_paging["-All(Microsoft Graphのページング取得)"]
mggraph_server_side_filter["-Filter/-Property(サーバー側の絞り込み)"]
where_object["Where-Object"]
mggraph_throttling["Microsoft Graphの調整(スロットリング)"]
dormant_account_detection["休眠アカウントの検出(最終サインイン日時)"]
leaver_processing["退職者処理(サインインブロックとセッション失効)"]
revoke_mguser_signinsession["Revoke-MgUserSignInSession"]
continuous_access_evaluation["継続的アクセス評価(CAE)"]
microsoft_graph_powershell_sdk -->|"の後継"| azuread_module
microsoft_graph_powershell_sdk -->|"の後継"| msonline_module
microsoft_entra_powershell -->|"利用する"| microsoft_graph_powershell_sdk
microsoft_entra_powershell -.->|"推奨される対応"| entra_id
connect_mggraph -->|"利用する"| delegated_permission
connect_mggraph -->|"利用する"| application_permission
application_permission -->|"前提とする"| app_registration
application_permission -->|"前提とする"| admin_consent
connect_mggraph -->|"で構成できる"| client_certificate
connect_mggraph -->|"利用する"| mggraph_certificatethumbprint_param
mggraph_certificatethumbprint_param -->|"両立しない"| localmachine_store
mggraph_certificatethumbprint_param -->|"利用する"| thumbprint
client_certificate -.->|"に保存される"| currentuser_store
client_certificate -.->|"に保存される"| localmachine_store
task_scheduler -.->|"前提とする"| application_permission
get_mguser -->|"前提とする"| connect_mggraph
get_mguser -->|"利用する"| mggraph_paging
get_mguser -->|"利用する"| mggraph_server_side_filter
where_object -->|"用いるのは非推奨"| get_mguser
mggraph_server_side_filter -->|"軽減する"| mggraph_throttling
dormant_account_detection -->|"前提とする"| get_mguser
leaver_processing -->|"利用する"| revoke_mguser_signinsession
application_permission -->|"推奨される対応"| leaver_processing
revoke_mguser_signinsession -.->|"前提とする"| continuous_access_evaluation
図の実線は常に成り立つ関係、破線は条件付きの関係です(成立条件は詳細ページの各関係の説明に記載)。関係すべての一覧(全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 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
# 【重い】全ワークロードを入れる
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での利用が推奨されます。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形態の違いが、この記事でいちばん事故につながる箇所です。図にすると次のようになります。
flowchart TD
subgraph D["委任 Delegated ── 対話サインイン"]
U["管理者がサインイン"] --> DS["同意した委任スコープ"]
U --> DR["本人が持つ管理者ロール"]
DS --> DX["できること =<br/>スコープとロールの重なり"]
DR --> DX
end
subgraph A["アプリ専用 Application ── 無人実行"]
C["証明書で認証"] --> AS["アプリケーション許可<br/>管理者の同意が必要"]
AS --> AX["できること =<br/>付与された許可そのもの"]
end
DX -.->|"無人化するときは<br/>権限を付け直す"| AS
覚え方はひとつです。委任=ユーザー本人として実行、アプリ専用=無人バッチ。委任では「本人にできないことはアプリにもできない」、アプリ専用では「アプリに与えた分だけ、常にできてしまう」。
4.2 対話的な接続
対話的な接続は Connect-MgGraph -Scopes です。指定したスコープに対して同意画面が出て、同意結果はテナントに記録されます。4
# 読み取りだけの棚卸し用途。書き込み権限を要求しない
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つを控えます。これが接続に使う値です。
| 概要ページの表示名 | Connect-MgGraph のパラメーター |
|---|---|
| アプリケーション (クライアント) ID | -ClientId |
| ディレクトリ (テナント) ID | -TenantId |
なお、Entra管理センターのナビゲーション名称は改称されることがあります。左メニューの表記が変わっていても、たどり着く先のページ名は「アプリの登録」です。これを目印にしてください。
5.2 アプリケーション許可を追加し、管理者の同意を与える
同じアプリの画面で、次をたどります。
管理 > APIのアクセス許可 > アクセス許可の追加 > Microsoft Graph > アプリケーションの許可
ここで「アプリケーションの許可」を選ぶことが要点です。隣の「委任されたアクセス許可」を選ぶと、無人実行では機能しません。必要な権限(棚卸しなら User.Read.All など)にチェックを入れ、「アクセス許可の追加」で確定します。
この時点ではまだ使えません。同じ画面上部の
「(テナント名) に管理者の同意を与えます」
ボタンを押して確定します。押した後、一覧の「状態」列が「(テナント名) に付与されました」という表示に変われば完了です。ここを忘れて「権限は付けたのに403で落ちる」というのが定番の詰まりどころです。
5.3 証明書を作る・アップロードする・置く
自己署名証明書で十分です。作成はPowerShellの New-SelfSignedCertificate で行います。
# 【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 に置いた場合はこちら
$cert = Get-ChildItem -Path 'Cert:\LocalMachine\My\A1B2C3D4E5F6...'
Connect-MgGraph -ClientId $clientId -TenantId $tenantId -Certificate $cert -NoWelcome
「手元では動いたのにタスクスケジューラでだけ証明書が見つからない」というトラブルは、ほぼこの置き場所と渡し方の食い違いです。
5.4 スクリプトから接続する
# 無人実行用の接続(対話なし)
$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 注意点
注意点が2つあります。
(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
ポイントは3つです。
-FilterはOData式で、サーバー側で絞り込みます。Where-Objectはローカルで絞るため、全件取得してから捨てることになります-Propertyで列を絞ると応答が軽くなります。既定では返らないプロパティも、明示すれば返るものがあります-Propertyで取得した項目はSelect-Objectにも書く。取得と表示は別なので、片方だけだと空欄になります
startsWith や endsWith のような高度なクエリ、あるいは件数だけが欲しい場合は、-ConsistencyLevel eventual と -CountVariable を併用します。7
# 有効なユーザー数だけを数える(全件を取得せずカウントを得る)
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 は、-CountVariable や startsWith などの高度なクエリを使うために必須の指定です。7
大量アクセスをすると調整(スロットリング) され、HTTP 429が返ります。公式の指針は「Retry-After ヘッダーの秒数に従って待ってから再試行する」ことです。8 SDKのコマンドレットはある程度の再試行を内部で行いますが、数千件規模のループを回すときは、そもそも取得回数を減らす(-Filter と -Property で絞る、1回で必要な情報を取る)ほうが確実です。リトライ設計そのものは「PowerShellのエラー処理と再実行設計」を参照してください。
7. 定番レシピ3つ
(1) ユーザー棚卸しを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の列構成は、Select-Object に書いた順序と名前がそのままヘッダー行になります。Export-Csv は既定で全項目を引用符で囲むため、1行目は次の形です。
"DisplayName","UserPrincipalName","Department","JobTitle","AccountEnabled","作成日","最終サインイン成功","最終対話サインイン試行","最終非対話サインイン"
2行目以降に、同じ順序でユーザーごとの値が並びます。期待した列が空欄で並ぶ場合は、-Property と Select-Object のどちらかに書き漏らしているか、権限が足りていません。特に 最終サインイン成功 以降の3列がすべて空なら、次に述べる AuditLog.Read.All の不足を疑ってください。
SignInActivity は、休眠アカウントの洗い出しに効きます。ただしどのフィールドを見るかが重要です。LastSignInDateTime は対話サインインの試行(失敗も含む)を記録し、アプリやサービスによる非対話サインインを含みません。これだけで判定すると、攻撃者のログイン失敗で「使われている」ように見えたり、実際に動いているサービスアカウントが「休眠」に見えたりします。休眠判定には、成功した対話・非対話サインインを反映する LastSuccessfulSignInDateTime を使ってください。10ただしこのプロパティだけは User.Read.All では取得できず、AuditLog.Read.All が追加で必要です(加えてテナント側のライセンス要件もあります)。権限が足りないとエラーになるか値が空になるので、使わないなら -Property から外してください。必要な権限は Find-MgGraphPermission で確認できます。10CSVの文字コードの扱いは「PowerShellでExcel・CSV業務処理を自動化する」にまとめています。
(2) ライセンスの消費状況を集計する
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) 退職者処理(サインインブロックとセッション失効)
次のコードは「委任(対話サインイン)」での実行例です。-Scopes を指定しているのがその印で、実行するとブラウザーでのサインインが求められます。無人実行にするには、5章のとおりアプリ専用に組み替える必要があります。両者の違いを先に整理します。
| 委任(下のコード例) | アプリ専用(無人実行) | |
|---|---|---|
| 誰として動くか | サインインした担当者本人 | アプリ(サービスプリンシパル)自身 |
Connect-MgGraph の指定 |
-ClientId + -Scopes |
-ClientId + -TenantId + -CertificateThumbprint |
| 証明書 | 不要 | 必須(またはクライアントシークレット) |
| 権限の種類 | 委任されたアクセス許可 | アプリケーションの許可(要・管理者の同意)6 |
| 追加で必要なもの | サインインした本人のEntra管理者ロール(後述)11 | 対象が管理者ならアプリ自体への上位ロール割り当て11 |
| 向く場面 | その場の個別対応、-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
もうひとつはスコープです。Graphでは操作ごとに必要な権限が定義されており、「ユーザーを書き込めるから何でもできる」わけではありません。accountEnabled の変更とサインインセッションの失効はそれぞれ専用の最小権限を持つため、片方だけ同意して実行すると、接続には成功しても途中のコマンドで権限不足エラーになります。各操作に必要な権限は Find-MgGraphPermission と、対応するGraph APIリファレンスの権限表で確認してください。119
そして3つ目に、委任実行ではスコープだけでは足りません。上のように利用者本人としてサインインして実行する場合、accountEnabled の変更にはMicrosoft Entraの管理者ロールも必要です。委任の権限は「アプリがそのユーザーの代わりに何をできるか」を決めるだけで、サインインしたユーザー自身の権限を引き上げはしないためです。権限に同意していてもロールがなければ、実行時に403で失敗します。ドキュメントは、テナント内のすべての管理者に対してこのプロパティを更新できる最小のロールを Privileged Authentication Administrator とし、一般には「対象より上位の管理者ロールが必要」と定めています。11 退職者処理の対象が一般利用者か管理者かで必要なロールが変わるため、運用担当のアカウントに何を割り当てるかは事前に決めておいてください。証明書によるアプリ専用実行でも、対象が管理者の場合はアプリ自体への上位ロールの割り当てが必要です。11
この処理を夜間バッチにするなら、変えるのは接続部分だけです。
# ── アプリ専用(無人実行)に組み替える場合 ──
# 事前準備: アプリ登録の「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したから即座に全アクセスが止まる」と考えず、重要なケースではアカウント無効化と併用し、反映までの時間差を織り込んでください。
アカウントの即時削除は避け、まずサインインを止めるのが実務の定石です。メールボックスやOneDriveの引き継ぎが終わる前に削除すると復旧に手間がかかります。この種の「取り返しがつかない操作」は、-WhatIf に対応した自作関数でラップし、対象一覧を先に出力して確認してから実行する形にしておくと安全です(「PowerShellの引数設計とモジュール化」)。
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に従います。 - 棚卸し・ライセンス集計・退職者処理の3つを月次で回すだけでも、ライセンスの無駄と放置アカウントというよくある2大リスクは大きく減らせます。
サンプルコードのダウンロード
この記事で扱ったコードは、そのまま動かせる形にまとめて配布しています。接続・休眠アカウント抽出・ライセンス集計・退職者処理が入っています。
この記事のサンプルは、Windowsやテナントに依存するため実行検証はしていません。構文解析とPSScriptAnalyzerによる静的解析までは全ファイルに対して実施していますが、動作は必ずご自身の検証機で確認してください。
# 構文解析 + 静的解析(Windows以外でも実行できる)
./Invoke-SampleTests.ps1
設定値(パス、サーバー名、テナントIDなど)は例です。そのまま本番環境で実行せず、自社の環境に合わせて読み替えてください。
関連記事
- PowerShellでの資格情報の安全な扱い ── 平文パスワードをスクリプトから追放する
- Windows PowerShell 5.1とPowerShell 7の違い ── 社内スクリプト移行の実務ガイド
- PowerShellでExcel・CSV業務処理を自動化する ── 集計・突合・帳票出力の実務レシピ
- PowerShellのエラー処理と再実行設計 ── try/catchが効かない罠からexit code・リトライの定石まで
- WinForms / WPFアプリにMicrosoft Entra IDのログインを組み込む
- タスクスケジューラのタスクが実行されない・0x1で終わる ── 原因の切り分けと安全な運用設計
関連する相談領域
合同会社小村ソフトでは、Microsoft 365運用スクリプトのGraph移行、アプリ登録と権限設計のレビュー、棚卸し・退職者処理といった定型業務の自動化を扱っています。
参考リンク
-
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
-
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
-
Microsoft Learn, Install the Microsoft Graph PowerShell SDK. Microsoft.Graphがサブモジュール群を含むメタモジュールであること、必要なサブモジュールだけを個別にインストールできること、Microsoft.Graph.Authenticationが認証に必須であること、対応するPowerShellのバージョンについて。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Connect-MgGraph. -Scopesによる委任アクセス許可の要求、-ClientId / -TenantId / -CertificateThumbprintによるアプリ専用認証、-CertificateThumbprintと-CertificateSubjectNameが現在のユーザーの証明書ストアから証明書を取得すること(ローカルコンピューターのストアを使う場合は自分で読み込んで-Certificateに渡すこと)、Get-MgContextによる現在の接続情報の確認、Disconnect-MgGraphによる切断について。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, Find Microsoft Graph PowerShell commands and permissions. Find-MgGraphCommandによるコマンドの所属モジュール・必要な権限・対応APIの検索、Find-MgGraphPermissionによる権限名の検索について。 ↩ ↩2
-
Microsoft Learn, Use app-only authentication with the Microsoft Graph PowerShell SDK. アプリ登録・アプリケーション許可・管理者の同意という手順、証明書を用いた無人認証の構成、委任アクセス許可とアプリケーションアクセス許可の違いについて。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10
-
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
-
Microsoft Learn, Microsoft Graph throttling guidance. リソース単位のスロットリングでHTTP 429が返ること、応答のRetry-Afterヘッダーに指定された秒数を待ってから再試行すべきこと、要求数そのものを減らす設計が推奨されることについて。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, user: revokeSignInSessions (Microsoft Graph API). サインインセッションの失効に必要なアクセス許可としてUser.RevokeSessions.Allが最小権限に挙げられていること、管理者の同意が必要であること、無効化の対象が更新トークンとブラウザーのセッションCookieであり、発行済みのアクセストークンは有効期限まで使用され得ること(即時反映には継続的アクセス評価が関わること)について。 ↩ ↩2 ↩3
-
Microsoft Learn, signInActivity resource type. ユーザーのsignInActivityプロパティの取得にAuditLog.Read.AllとUser.Read.Allの両方のアクセス許可が必要であること、テナントのライセンス要件があること、lastSignInDateTimeが対話的サインインの試行(成功・失敗を含む)を表すのに対し、lastSuccessfulSignInDateTimeが成功した対話・非対話サインインを、lastNonInteractiveSignInDateTimeが非対話サインインを表すことについて。 ↩ ↩2
-
Microsoft Learn, Update user (Microsoft Graph API). ユーザーの更新に必要なアクセス許可がプロパティ単位で定義されており、accountEnabledの変更にはUser.EnableDisableAccount.Allが最小権限として挙げられていること、より広いUser.ReadWrite.Allでも実行できることについて。 委任シナリオでは適切なスコープに加えてMicrosoft Entraの管理者ロールが必要であり、テナント内のすべての管理者に対してaccountEnabledを更新できる最小のロールがPrivileged Authentication Administratorであること、一般には対象より上位の管理者ロールが必要であること、アプリ専用シナリオでも対象が管理者の場合はアプリに上位の管理者ロールの割り当てが必要であることについて。 ↩ ↩2 ↩3 ↩4 ↩5
関連する記事
同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。
winget + PowerShellでPCキッティングを自動化する ── 手順書を実行可能にする
新入社員PCのセットアップを再現可能にする方法をまとめます。wingetによるアプリ導入とexport/import、WinGet Configurationの宣言的な構成、PowerShellで補う設定、無人実行時の注意点までを解説します。
PowerShellのセキュリティ強化 ── ログ・AMSI・言語モード・JEA
PowerShellを禁止せずに安全に使うための実務をまとめます。スクリプトブロックログとトランスクリプションの有効化、AMSIと古いバージョンの無効化、言語モードによる制限、JEAによる権限委任までを解説します。
PowerShellモジュールの社内配布と更新 ── PSResourceGetと社内リポジトリ
共有フォルダーのps1をコピーして使い回す運用から卒業する方法をまとめます。モジュールマニフェストの書き方、バージョニング、PSResourceGetによる社内リポジトリの構築と配布・更新、署名との組み合わせまで解説します。
PowerShellでの資格情報の安全な扱い ── 平文パスワードをスクリプトから追放する
PowerShellスクリプトの平文パスワードを安全な保管へ移行する手順を整理します。SecureStringの実像と限界、Export-ClixmlによるDPAPI保存の仕組み、SecretManagement/SecretStoreの使いどころまで解説します。
PowerShell Remoting(WinRM)入門 ── 複数台のWindowsを一括管理する
PowerShell Remoting(WinRM)で複数台のWindowsを一括管理する入門。仕組みとポート5985/5986、Enable-PSRemotingで起きること、ワークグループのTrustedHosts、Invoke-CommandとPSSession、se...
関連トピック
このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。
Windows技術トピック
Windows 開発、不具合調査、既存資産活用の技術トピックをまとめた入口です。
よくある質問
この記事のテーマについて、相談時によくある質問をまとめています。
- 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の組み合わせでカウントだけ取得できます。