如何使用Graph API导出Azure应用注册的委托与应用权限
问题原因
原脚本无法导出全量权限数据,核心问题如下:
- 未处理Graph API分页逻辑:默认单页最多返回100条条目,会遗漏大量应用注册、服务主体数据
Where-Object筛选语法错误:匹配条件未按要求使用脚本块包裹,导致权限匹配逻辑失效- 逻辑覆盖不全:仅实现了委托权限的部分匹配逻辑,未覆盖应用权限匹配、资源名称/权限名称关联、结果收集、CSV导出的完整流程
- 匹配逻辑不合理:使用模糊匹配
-match做权限ID、资源ID的匹配,容易出现匹配错误,ID为固定Guid值应使用精确匹配-eq
修正后完整脚本
# 依赖:提前安装MSAL.ps模块,安装命令:Install-Module MSAL.PS -Scope CurrentUser # 前置要求:脚本使用的Azure AD应用注册需授予Application.Read.All、Directory.Read.All应用权限,并完成管理员同意 $connectiondetails = @{ 'clientid' = "" # 填入你的应用程序(客户端)ID 'tenantid' = "" # 填入你的租户ID 'clientSecret' = "" | ConvertTo-SecureString -AsPlainText -Force # 填入你的客户端密钥 } $token = Get-MsalToken @connectiondetails $accessToken = $token.AccessToken # 配置CSV导出路径 $delegatedPermsExportPath = "C:\temp\delegatedpermissions.csv" $applicationPermsExportPath = "C:\temp\applicationpermissions.csv" # 通用方法:拉取Graph API全量分页数据 function Get-GraphAllPages { param( [string]$Uri, [string]$AccessToken ) $headers = @{ Authorization = "bearer $AccessToken" } $allResults = @() $nextLink = $Uri while (-not [string]::IsNullOrEmpty($nextLink)) { $response = Invoke-RestMethod -Method Get -Uri $nextLink -ContentType "application/json" -Headers $headers $allResults += $response.value $nextLink = $response.'@odata.nextLink' } return $allResults } # 1. 拉取全量应用注册数据 $allApps = Get-GraphAllPages -Uri "https://graph.microsoft.com/v1.0/applications" -AccessToken $accessToken # 2. 拉取全量服务主体数据,构建权限映射表 $allServicePrincipals = Get-GraphAllPages -Uri "https://graph.microsoft.com/v1.0/serviceprincipals" -AccessToken $accessToken # 资源AppId映射:Key=资源AppId,Value=资源显示名称 $resourceMap = @{} # 委托权限映射:Key=资源AppId|权限Id,Value=权限值 $delegatedPermMap = @{} # 应用权限映射:Key=资源AppId|权限Id,Value=权限值 $appPermMap = @{} foreach ($sp in $allServicePrincipals) { $resourceMap[$sp.appId] = $sp.displayName # 处理委托权限(oauth2PermissionScopes) foreach ($scope in $sp.oauth2PermissionScopes) { $key = "{0}|{1}" -f $sp.appId, $scope.id $delegatedPermMap[$key] = $scope.value } # 处理应用权限(appRoles) foreach ($role in $sp.appRoles) { $key = "{0}|{1}" -f $sp.appId, $role.id $appPermMap[$key] = $role.value } } # 3. 遍历所有应用注册,关联权限信息 $finalDelegatedPerms = @() $finalAppPerms = @() foreach ($app in $allApps) { foreach ($resourceAccess in $app.requiredResourceAccess) { $resourceAppId = $resourceAccess.resourceAppId $resourceName = if ($resourceMap.ContainsKey($resourceAppId)) { $resourceMap[$resourceAppId] } else { "未知资源($resourceAppId)" } foreach ($permEntry in $resourceAccess.resourceAccess) { $permKey = "{0}|{1}" -f $resourceAppId, $permEntry.id # 委托权限 if ($permEntry.type -eq "Scope") { $permName = if ($delegatedPermMap.ContainsKey($permKey)) { $delegatedPermMap[$permKey] } else { "未知权限($($permEntry.id))" } $finalDelegatedPerms += [PSCustomObject]@{ 应用显示名称 = $app.displayName 应用客户端ID = $app.appId 资源显示名称 = $resourceName 权限类型 = "委托权限" 权限名称 = $permName 权限ID = $permEntry.id } } # 应用权限 if ($permEntry.type -eq "Role") { $permName = if ($appPermMap.ContainsKey($permKey)) { $appPermMap[$permKey] } else { "未知权限($($permEntry.id))" } $finalAppPerms += [PSCustomObject]@{ 应用显示名称 = $app.displayName 应用客户端ID = $app.appId 资源显示名称 = $resourceName 权限类型 = "应用权限" 权限名称 = $permName 权限ID = $permEntry.id } } } } } # 4. 导出为CSV文件,使用UTF8编码避免乱码 $finalDelegatedPerms | Export-Csv -Path $delegatedPermsExportPath -NoTypeInformation -Encoding UTF8 $finalAppPerms | Export-Csv -Path $applicationPermsExportPath -NoTypeInformation -Encoding UTF8 Write-Host "导出完成,委托权限文件:$delegatedPermsExportPath,应用权限文件:$applicationPermsExportPath"
使用说明
- 运行前先安装
MSAL.PS模块,执行命令Install-Module MSAL.PS -Scope CurrentUser - 在脚本配置段填入你自己的客户端ID、租户ID、客户端密钥
- 确保使用的应用注册已经授予
Application.Read.All、Directory.Read.All的应用权限,并完成管理员同意 - 提前创建
C:\temp目录,或自行修改导出路径为你本地存在的目录 - 脚本会自动处理分页拉取全量数据,对于租户中已删除资源对应的残留权限ID,会标记为「未知资源」「未知权限」方便排查
内容的提问来源于stack exchange,提问作者Bernietechy
相关产品推荐
相关产品推荐

