如何通过PowerShell调用MS Graph API获取合作伙伴租户用户与设备
跨合作伙伴租户批量查询用户、设备数据的实现方案
问题根因
之前的脚本无法跨租户访问的核心原因有两个:
- 令牌获取逻辑错误:脚本中传入自有合作伙伴租户ID获取的令牌,仅具备访问自身租户资源的权限,无法直接访问委托管理的客户租户资源
- 权限配置链路不完整:仅在自有Azure AD中给应用注册配置Graph权限不够,必须将应用关联到合作伙伴中心的CSP/委派管理体系,完成跨租户权限的全局同意,才可以覆盖所有关联客户租户。
前置配置要求
- 应用注册需要关联到你的合作伙伴中心账号,不能仅在自有Azure AD中独立创建
- 为应用授予以下Microsoft Graph应用程序权限(不要选委派权限),并在合作伙伴中心为所有关联GDAP关系的租户完成管理员同意,无需单独登录每个客户租户做授权:
Directory.Read.All:用于拉取所有关联客户租户列表User.Read.All:用于读取客户租户用户数据Device.Read.All:用于读取客户租户设备数据
- 确保应用被加入到对应GDAP关系配置的授权安全组中,否则会出现部分租户无权限访问的问题。
可直接运行的PowerShell脚本
# 首次运行先安装依赖模块,已安装可跳过 Install-Module MSAL.PS, Microsoft.Graph.Authentication -Scope CurrentUser -Force Import-Module MSAL.PS # 替换为你自己的配置参数 $ClientId = "应用注册的客户端ID" $PartnerTenantId = "你的合作伙伴租户ID" $ClientSecret = "应用注册的客户端密钥" | ConvertTo-SecureString -AsPlainText -Force $GraphScope = "https://graph.microsoft.com/.default" $OutputPath = "C:\Temp\PartnerTenantDataExport.csv" # 结果导出路径 # 1. 获取合作伙伴租户令牌,拉取所有关联客户租户列表 $partnerToken = Get-MsalToken -ClientId $ClientId ` -ClientSecret $ClientSecret ` -TenantId $PartnerTenantId ` -Scope $GraphScope $customerList = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/contracts?`$select=customerId,displayName,defaultDomainName" ` -Headers @{Authorization = "Bearer $($partnerToken.AccessToken)"} ` -Method Get $result = @() # 2. 遍历所有客户租户,拉取对应用户、设备数据 foreach ($customer in $customerList.value) { Write-Host "正在处理租户: $($customer.displayName) | ID: $($customer.customerId)" # 针对当前客户租户申请访问令牌,无需客户租户侧单独授权 $customerToken = Get-MsalToken -ClientId $ClientId ` -ClientSecret $ClientSecret ` -TenantId $customer.customerId ` -Scope $GraphScope $authHeader = @{Authorization = "Bearer $($customerToken.AccessToken)"} # 拉取用户数据(可自行调整需要返回的字段) $users = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/users?`$select=id,displayName,userPrincipalName,accountEnabled,lastSignInDateTime" ` -Headers $authHeader ` -Method Get # 拉取设备数据(可自行调整需要返回的字段) $devices = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/devices?`$select=id,displayName,deviceId,operatingSystem,accountEnabled,lastLogonDateTime" ` -Headers $authHeader ` -Method Get $result += [PSCustomObject]@{ TenantName = $customer.displayName TenantId = $customer.customerId DefaultDomain = $customer.defaultDomainName TotalUserCount = $users.value.Count TotalDeviceCount = $devices.value.Count UserList = $users.value | ConvertTo-Json -Compress DeviceList = $devices.value | ConvertTo-Json -Compress } } # 导出结果到CSV $result | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8 Write-Host "查询完成,结果已导出到: $OutputPath"
注意事项
- 不建议使用
Connect-MgGraph做跨租户切换,模块本身的上下文缓存容易出现权限错乱,直接携带对应租户的令牌调用Graph接口稳定性更高 - 如果调用contracts接口返回403,优先检查应用是否已经关联到合作伙伴中心、是否授予了
Directory.Read.All应用权限并完成管理员同意 - 如果单个客户租户访问返回403,优先检查对应租户的GDAP关系是否处于活跃状态、应用是否在GDAP授权的安全组范围内
- 客户租户数量较多时,可以在循环中加200-500ms的请求间隔,避免触发Graph接口限流
内容的提问来源于stack exchange,提问作者Mason
相关产品推荐
相关产品推荐

