如何加速基于Microsoft Graph PowerShell SDK的脚本运行效率
PowerShell Graph脚本性能优化方案
核心性能瓶颈分析
你的脚本耗时1-2小时处理1000+用户,主要原因集中在:
- 串行API调用:循环内针对每个用户单独发起1-2次Graph请求,网络延迟累加是最大耗时点
- 不必要的磁盘IO:第一个脚本中导出再导入CSV的操作完全多余,增加了磁盘读写开销
- 低效集合操作:普通数组
+=会频繁重建数组,性能远低于专用集合类型 - 重复请求冗余:可通过扩展属性一次性获取多字段,无需单独调用API获取登录活动或许可证信息
具体优化措施及脚本
一、优化后第一个脚本(针对特定许可证用户)
直接在内存中处理用户数据,一次性获取所需字段,避免循环内API调用和磁盘IO:
# 必须在PowerShell 5.1中运行,否则无法获取账户创建日期 # Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser $maximumfunctioncount = 8192 # 仅在未安装时安装模块,避免重复操作 if (-not (Get-Module -ListAvailable Microsoft.Graph)) { Install-Module Microsoft.Graph -Scope CurrentUser -Force -AllowClobber } Connect-MgGraph -Scopes "Directory.Read.All","User.Read.All","AuditLog.Read.All" Select-MgProfile -Name "Beta" $LicenseSku = 'ENTERPRISEPACK' # 获取目标许可证SKU信息 $licenseDetails = Get-MgSubscribedSku -All | Where-Object SkuPartNumber -eq $LicenseSku # 一次性获取符合条件的用户,同时包含登录活动和创建日期 $targetUsers = Get-MgUser -Filter "assignedLicenses/any(x:x/skuId eq $($licenseDetails.SkuId) )" ` -ConsistencyLevel eventual -All ` -Select UserPrincipalName, CreatedDateTime, SignInActivity # 使用高效List存储结果,替代普通数组 $Result = [System.Collections.Generic.List[Object]]::new() foreach($user in $targetUsers) { $userprops = [ordered]@{ UserPrincipalName = $user.UserPrincipalName LastSignInDateTime = $user.SignInActivity.LastSignInDateTime AccountCreationDate = $user.CreatedDateTime } $Result.Add([pscustomobject]$userprops) } # 一次性导出到CSV,避免循环内Append的IO损耗 $FinalCSVName = "c:\temp\License CSV_$LicenseSku.csv" $Result | Export-Csv -Path $FinalCSVName -NoTypeInformation $Result
二、优化后第二个脚本(全用户活动报告)
批量获取许可证信息,减少循环内API调用次数:
# 连接至Microsoft Graph Connect-MgGraph -scope User.Read.All, AuditLog.read.All Select-MgProfile -name beta # 一次性获取所有用户的核心信息+登录活动 $AllUsers = Get-MgUser -All -Select DisplayName, UserPrincipalName, Id, SignInActivity # 批量获取所有用户的许可证详情,按用户ID分组存为哈希表,方便快速匹配 $allUserLicenses = Get-MgUserLicenseDetail -All | Group-Object -Property UserId -AsHashTable -AsString # 使用List存储结果 $Report = [System.Collections.Generic.List[Object]]::new() foreach ($user in $AllUsers) { Write-host "Processing $($user.DisplayName)" -ForegroundColor Cyan # 从批量数据中匹配当前用户的许可证 $licenses = if ($allUserLicenses.ContainsKey($user.Id)) { $allUserLicenses[$user.Id].SkuPartNumber -join ", " } else { "无许可证" } $obj = [pscustomobject][ordered]@{ DisplayName = $user.DisplayName UserPrincipalName = $user.UserPrincipalName Licenses = $licenses LastInteractiveSignIn = $user.SignInActivity.LastSignInDateTime LastNonInteractiveSignin = $user.SignInActivity.LastNonInteractiveSignInDateTime } $Report.Add($obj) } $Report | Export-CSV -path C:\temp\Microsoft365_User_Activity-Report.csv -NoTypeInformation
额外优化建议
- 速率限制处理:Graph API有调用频率限制,可添加重试逻辑捕获429错误并等待后重试
- 使用最新模块:确保安装最新版Microsoft.Graph模块,新版本通常包含性能优化
- 提前过滤用户:若仅需处理特定用户(如活跃用户),可通过
Filter参数提前筛选,减少处理量 - 谨慎并行处理:针对超大规模用户,可尝试并行请求(控制并发数5-10),但需注意避免触发限流:
# 并行处理示例(需注意速率限制) $AllUsers | ForEach-Object -Parallel { $userId = $_.Id $signIn = Get-MgUser -UserId $userId -Select SignInActivity $licenses = (Get-MgUserLicenseDetail -UserId $userId).SkuPartNumber -join ", " [pscustomobject]@{ DisplayName = $_.DisplayName LastSignIn = $signIn.SignInActivity.LastSignInDateTime Licenses = $licenses } } -ThrottleLimit 5 | Export-Csv -Path "output.csv" -NoTypeInformation
内容的提问来源于stack exchange,提问作者Arbelac
相关产品推荐
相关产品推荐

