You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何加速基于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

额外优化建议

  1. 速率限制处理:Graph API有调用频率限制,可添加重试逻辑捕获429错误并等待后重试
  2. 使用最新模块:确保安装最新版Microsoft.Graph模块,新版本通常包含性能优化
  3. 提前过滤用户:若仅需处理特定用户(如活跃用户),可通过Filter参数提前筛选,减少处理量
  4. 谨慎并行处理:针对超大规模用户,可尝试并行请求(控制并发数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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 07:10:44