Microsoft Graph PowerShell 入門 ── AzureAD・MSOnline 廢止後的 Microsoft 365 維運

· · PowerShell, Microsoft 365, Microsoft Entra ID, Microsoft Graph, 資訊系統, 自動化, 維運改善, 資安

對於將 Microsoft 365 維運自動化的現場而言,2024 年到 2025 年間最大的變化就是AzureAD 模組與 MSOnline 模組的廢止。應該有不少資訊系統人員曾用 Get-MsolUserGet-AzureADUser 撰寫「離職人員處理」「授權盤點」「新進員工帳號建立」等制式化流程,而這些命令將陸續無法使用。

遷移目標是 Microsoft Graph PowerShell SDK。不過,這並不只是單純的命令名稱替換。從驗證的思維(範圍與同意)、資料的取得方式(OData 的篩選與分頁),到無人執行的建構方式(應用程式註冊與憑證),設計上的前提都改變了。若不理解這些差異就機械式地替換,將會背上「雖然能動,但用了過度的權限」「帳號數量多的租戶只能取到一部分」之類的另一種問題。

本文以透過 PowerShell 將公司內部 Microsoft 365 維運自動化的資訊系統負責人為對象,整理廢止的來龍去脈、連線與範圍設計、無人執行的架構,以及盤點・授權彙總・離職人員處理這三個經典範例,並整理成可直接用於實務的形式。

1. 先講結論

  • MSOnline 與 AzureAD 已於 2024 年 3 月 30 日被列為不建議使用,MSOnline 已於 2025 年 5 月 30 日終止提供,AzureAD 也已於 2025 年 3 月 30 日結束支援後遭到廢止。1
  • 遷移目標是 Microsoft Graph PowerShell SDK,或建構於其上的 Microsoft Entra PowerShell(於 2025 年 3 月正式發行)。後者採情境導向設計,也提供協助從 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
  • 應用程式註冊依用途分開建立。「一個包山包海的應用程式」會讓權限不斷膨脹,一旦出事,影響範圍也會擴大。

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 月正式發行。建構於 Graph SDK 之上的情境導向模組2

該選哪一個,判斷標準如下。若想直接操作 Graph API 的結構,或需要涉及廣泛的工作負載(Exchange、Teams、Intune 等),就選 Graph PowerShell SDK。若主要以 Entra ID(舊稱 Azure AD)的識別身分管理為中心,並希望盡可能輕鬆地從 AzureAD 模組遷移,則選Microsoft Entra PowerShell。後者可與 Graph PowerShell SDK 互通,也提供協助從 AzureAD 模組遷移的向下相容選項。2

本文以泛用性高、文件也較豐富的 Graph PowerShell SDK 為主軸進行說明。

3. 安裝 ── 不要整包安裝元模組(Meta Module)

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. 連線與範圍(Scope) ── 停止「先套用 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

這兩種形態的差異,是本文中最容易引發事故的地方。畫成圖如下所示。

應用程式專用 Application ── 無人執行委任 Delegated ── 互動式登入轉為無人執行時需要重新設定權限應用程式權限需要管理員同意以憑證驗證能做的事 =所授予的權限本身已同意的委任範圍系統管理員登入本人擁有的系統管理員角色能做的事 =範圍與角色的交集

記憶方式只有一個。委任=以使用者本人身分執行,應用程式專用=無人批次。在委任模式下,「本人做不到的事,應用程式也做不到」;在應用程式專用模式下,「只要授予了應用程式,它就會一直能做到」。

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,盤點腳本就不可能誤觸寫入操作。

原則有三個。

  • 讀取用途不要求寫入範圍(若 User.Read.All 已足夠,就不要求 User.ReadWrite.All)
  • 依用途分開應用程式註冊(盤點用、帳號建立用、授權管理用)
  • 同意由管理員有意識地執行(一旦給予的同意會持續留存在租戶中)

不確定需要哪個範圍時,可用 Find-MgGraphPermission 尋找候選,並用 Find-MgGraphCommand 確認命令所要求的權限。5

5. 無人執行 ── 應用程式註冊+憑證

若要每晚透過工作排程器執行,就無法使用互動式登入。這時要切換為透過應用程式註冊(服務主體)與憑證進行的應用程式專用驗證6

步驟共有 4 個階段。這裡是實務上最容易卡關的環節,因此會具體寫出畫面上的位置與命令。6

5.1 註冊應用程式

在 Microsoft Entra 管理中心(https://entra.microsoft.com)中,依序進行以下操作。

識別碼 > 應用程式 > 應用程式註冊 > 新增註冊

輸入名稱(例如:M365-Inventory-Batch),支援的帳戶類型選擇「僅此組織目錄中的帳戶(單一租戶)」,然後按下「註冊」。若只是要從腳本無人執行,則不需要設定重新導向 URI。

註冊完成後,請記下「概觀」頁面中顯示的以下兩項,這是連線時要用的值。

概觀頁面顯示名稱 Connect-MgGraph 的參數
應用程式 (用戶端) 識別碼 -ClientId
目錄 (租用戶) 識別碼 -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】記下連線時要用的拇印(Thumbprint)
$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 注意事項

有兩個注意事項。

(1) 委任與應用程式專用所需的權限並不相同。若要將原本以 -Scopes 'User.Read.All' 互動式執行的腳本改為無人化,必須在應用程式註冊端以應用程式權限重新設定同種類的權限,並取得管理員同意。6

(2) 憑證有到期日。「到期當天夜間批次全數失敗」是實際上經常發生的事故。請務必將有效期限登記在行事曆上,並將更新程序文件化。關於憑證資訊的保管,亦請一併參閱「PowerShell 中安全處理憑證資訊」。雖然也能用用戶端密碼連線,但由於明文攜帶的風險以及到期管理的繁瑣,建議採用憑證。

6. 資料的取得方式 ── -All / -Filter / -Property

Graph 是以分頁為前提的 API。預設只會回傳一頁的資料,若需要全部資料,就加上 -All7

# 【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

重點有三個。

  • -Filter 是 OData 運算式,會在伺服器端進行篩選。Where-Object 則是在本機篩選,因此會先取得全部資料再捨棄
  • -Property 篩選欄位能讓回應變輕量。有些預設不會回傳的屬性,只要明確指定就會回傳
  • -Property 取得的項目,也要寫進 Select-Object。取得與顯示是分開的,只寫一邊會出現空白欄位

若要使用 startsWithendsWith 之類的進階查詢,或只想取得件數,則要併用 -ConsistencyLevel eventual-CountVariable7

# 只計算有效使用者的數量(不取得全部資料,只取得計數)
Get-MgUser -Filter 'accountEnabled eq true' -ConsistencyLevel eventual -CountVariable total -Top 1 | Out-Null
"有效使用者: $total 筆"

-Top 1 | Out-Null 是不常見的寫法,這裡補充說明。-CountVariable 取得的是符合條件的全部件數,不受 -Top 的值影響。-Top 決定的只是單次回應會回傳的物件數量。也就是說,-Top 1 並非「只計算 1 筆」的指定,而是因為要取得計數,勢必得發出一次請求,而這個指定是為了讓實際回傳的資料量降到最低。若省略,會回傳預設一頁份量的使用者物件,再以 Out-Null 捨棄。另外,-ConsistencyLevel eventual 是使用 -CountVariablestartsWith 等進階查詢時的必要指定。7

大量存取會被調整(節流),並回傳 HTTP 429。官方的指引是「依照 Retry-After 標頭指定的秒數等待後再重試」。8 SDK 的命令組在內部會進行一定程度的重試,但當要跑數千筆規模的迴圈時,從根本減少取得次數(以 -Filter-Property 篩選、一次取得所需資訊)才是更確實的做法。重試設計本身請參閱「PowerShell 的錯誤處理與重試設計」。

7. 三個經典範例

(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 預設會將所有欄位以引號括起,因此第一行會是以下形式。

"DisplayName","UserPrincipalName","Department","JobTitle","AccountEnabled","建立日期","最後成功登入","最後互動式登入嘗試","最後非互動式登入"

第二行以後,會依相同順序排列每位使用者的值。若預期的欄位出現空白,可能是 -PropertySelect-Object 其中一處漏寫,或是權限不足。特別是 最後成功登入 之後的三欄若全部空白,請懷疑是接下來會提到的 AuditLog.Read.All 權限不足。

SignInActivity 對於篩選出休眠帳號很有效。不過看哪個欄位很重要LastSignInDateTime 記錄的是互動式登入的嘗試(包含失敗),不包含應用程式或服務的非互動式登入。若只靠這個欄位判定,可能會因為攻擊者的登入失敗而看起來像「有在使用」,也可能讓實際運作中的服務帳戶看起來像「休眠」。判定休眠時,請使用反映成功的互動式・非互動式登入的 LastSuccessfulSignInDateTime10不過只有這個屬性無法透過 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) 的登入已停止"

這裡有三個要點需要掌握。第一個是連線目標的應用程式註冊。省略 -ClientId 會以 Microsoft Graph PowerShell SDK 的預設應用程式連線,已同意的權限會被記錄在該組織共用的應用程式上。要落實第 4 章「依用途分開應用程式註冊」的原則,必須透過 -ClientId 明確指定自家的應用程式註冊。4

另一個是範圍。在 Graph 中,每項操作都定義了所需的權限,並非「能寫入使用者就什麼都能做」。變更 accountEnabled 與使登入工作階段失效,各自擁有專屬的最小權限,若只同意其中一項就執行,即使連線成功,也會在中途的命令上出現權限不足的錯誤。各項操作所需的權限,請以 Find-MgGraphPermission 及對應 Graph API 參考文件中的權限表確認。119

第三點是,以委任方式執行時,只有範圍是不夠的。像上面這樣以使用者本人身分登入執行時,變更 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 以識別身分管理為主・要從 AzureAD 遷移就用 Entra;工作負載廣泛就用 Graph SDK2
安裝 全部安裝 / 僅子模組 為了縮短啟動時間與相依性,以子模組為單位安裝3
驗證(互動式) 交由管理員帳號自行處理 / 最小範圍 盤點只用 .Read. 系列。同意會留存在租戶中4
驗證(無人) 用戶端密碼 / 憑證 建議使用憑證。事先決定好到期管理與更新程序6
權限的粒度 一個包山包海的應用程式 / 依用途分開應用程式註冊 可以限縮出事時的影響範圍
清單取得 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(限定欄位)三件套。429 則依循 Retry-After
  • 光是每月執行一次盤點・授權彙總・離職人員處理這三項,就能大幅降低授權浪費與帳號放置不管這兩大常見風險。

範例程式碼下載

本文所提到的程式碼,已整理成可直接執行的形式提供下載。內容包含連線、休眠帳號篩選、授權彙總、離職人員處理。

下載範例程式碼(zip)

本文的範例因依賴 Windows 與租戶環境,並未進行實際執行驗證。所有檔案都已完成語法解析與 PSScriptAnalyzer 靜態分析,但實際運作務必請在自己的驗證機上確認。

# 語法解析 + 靜態分析(在 Windows 以外的環境也能執行)
./Invoke-SampleTests.ps1

設定值(路徑、伺服器名稱、租用戶識別碼等)僅為範例。請勿直接在正式環境中執行,務必依自家環境調整後再使用。

相關文章

相關諮詢領域

合同會社小村軟體提供 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

  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, signInActivity resource type。關於取得使用者的 signInActivity 屬性需要 AuditLog.Read.All 與 User.Read.All 兩項存取權限、存在租戶授權要求,以及 lastSignInDateTime 代表互動式登入的嘗試(包含成功・失敗),相對地 lastSuccessfulSignInDateTime 代表成功的互動式・非互動式登入、lastNonInteractiveSignInDateTime 代表非互動式登入的說明。  2

  11. 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 月正式發行)。
安裝 Microsoft.Graph 模組很耗時,有辦法變輕量嗎?
可以。Microsoft.Graph 是元模組(Meta Module),底下包含大量子模組,若整包安裝,不論安裝或載入都會花費相當時間。實務上,較合理的做法是只安裝實際會用到的工作負載子模組:使用者管理用 Microsoft.Graph.Users、群組用 Microsoft.Graph.Groups、驗證則是必要的 Microsoft.Graph.Authentication,依此類推。哪個命令屬於哪個模組,可以用 Find-MgGraphCommand 查詢。
想從工作排程器無人執行,卻跳出了互動式登入畫面。
請切換為透過應用程式註冊(服務主體)與憑證進行的應用程式專用驗證。在 Microsoft Entra ID 中註冊應用程式,對所需的應用程式權限(Application permissions)授予管理員同意後,將 -ClientId・-TenantId・-CertificateThumbprint 傳給 Connect-MgGraph,即可在不進行互動的情況下連線。相較於用戶端密碼,憑證更為安全,有效期限的管理也更明確。請務必將憑證放在執行帳戶的憑證存放區,並訂出到期前更新的維運方式。
Connect-MgGraph 的 -Scopes 該指定什麼?
只指定想執行的命令所要求的最小權限即可。可以透過 Find-MgGraphPermission 或該命令的文件確認需要哪些權限,若只是讀取,像 User.Read.All 這類 .Read. 系列權限就已足夠。若目的是盤點或稽核,請不要要求寫入權限。由於一旦同意的權限會被記錄在租戶中,「先用 Directory.ReadWrite.All 同意再說」會成為日後的風險。依用途分開應用程式註冊、也分開權限,才是安全的做法。
用 Get-MgUser 處理帳號數量多的租戶時,只能取得一部分資料。
這是因為 Microsoft Graph 的回應會分頁。加上 -All,就會自動依序取得所有頁面。此外,大量取得時可能會被調整(節流)並收到 429 回應,因此基本做法是只用 -Property 篩選必要欄位,並將篩選條件以 OData(-Filter)推給伺服器端處理,而不是在用戶端使用 Where-Object。如果只想要件數,可以搭配 -ConsistencyLevel eventual 與 -CountVariable,只取得計數。

作者檔案

本文作者的個人檔案頁面。

Go Komura

小村軟體有限公司 代表

以 Windows 軟體開發、技術諮詢與故障調查為中心,在難以重現的故障調查與既有資產仍在運作的專案上具有優勢。

回到部落格一覽