能否使用服务主体或Azure应用管理员同意外部多租户应用注册?
我们管理多个租户,目前创建多租户应用注册后,需要逐个在浏览器中加载管理员同意URL,以管理员身份登录每个租户操作,过程十分繁琐。此前我可通过PowerShell及密码数据库执行Login-AzureRmAccount获取刷新令牌,调用端点https://main.iam.ad.ext.azure.com/api/RegisteredApplications/$azureAppId/Consent?onBehalfOfAll=true批准各租户应用。但如今受MFA及安全加固政策影响,已无法以管理员身份程序化登录完成批准。
现提问:
- 能否使用在每个租户创建的服务主体程序化登录,批准该租户的应用注册?
- 还是仅允许使用用户名密码的普通管理员账户执行批准?
- 是否有该流程的可用示例代码?
已执行测试
我创建了拥有Directory.ReadWrite.All权限的应用注册,测试了类似如下代码及多种变体但均失败,根据输入资源不同出现不同错误:
错误1:
Invoke-RestMethod : {"error":"invalid_resource","error_description":"AADSTS500011: The resource principal named <a href="https://graph.microsoft.com/.default" rel="nofollow noreferrer">https://graph.microsoft.com/.default was not found in the tenant name
错误2:
Invoke-WebRequest : The remote server returned an error: (401) Unauthorized.
测试代码:
$body = @{ grant_type = "client_credentials" client_id = $clientId client_secret = $clientSecret resource = $resourceAppIdURI } $oauth = Invoke-RestMethod -Method Post -Uri https://login.windows.net/$tenantId/oauth2/token?api-version=1.0 -Body $body $header = @{ 'Authorization' = 'Bearer ' + $apiToken.access_token 'X-Requested-With'= 'XMLHttpRequest' 'x-ms-client-request-id'= [guid]::NewGuid() 'x-ms-correlation-id' = [guid]::NewGuid() } $url = "https://main.iam.ad.ext.azure.com/api/RegisteredApplications/$azureAppId/Consent?onBehalfOfAll=true"
- 能否使用服务主体完成租户应用批准?
可以,但需满足两个核心条件:
- 目标租户中的服务主体必须拥有**
Application.ReadWrite.All或Directory.ReadWrite.All的应用权限**(注意是应用权限而非委派权限),且该权限已由目标租户管理员完成管理员同意。 - 不能继续使用
main.iam.ad.ext.azure.com端点,该端点属于Azure Portal内部API,不支持服务主体调用,需改用Microsoft Graph API完成操作。
- 是否仅允许用户名密码的管理员账户执行批准?
不是。除受MFA限制的用户名密码流程外,还支持:
- 满足权限要求的服务主体调用Microsoft Graph API(完全自动化)。
- 支持MFA的交互式登录流程(如设备代码流),但仍需人工交互,不适用于批量自动化场景。
- 示例代码(PowerShell + Microsoft Graph API)
以下是使用服务主体完成租户级应用管理员同意的可运行示例:
# 配置参数 $tenantId = "目标租户ID" $servicePrincipalClientId = "服务主体的应用ID" $servicePrincipalSecret = "服务主体的客户端密钥" $targetMultiTenantAppId = "需要被批准的多租户应用ID" $requiredScopes = "User.Read.All Directory.ReadWrite.All" # 替换为目标应用实际需要的权限范围 # 获取Microsoft Graph访问令牌 $tokenUri = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" $tokenBody = @{ grant_type = "client_credentials" client_id = $servicePrincipalClientId client_secret = $servicePrincipalSecret scope = "https://graph.microsoft.com/.default" } $tokenResponse = Invoke-RestMethod -Method Post -Uri $tokenUri -Body $tokenBody # 构建请求头 $headers = @{ "Authorization" = "Bearer $($tokenResponse.access_token)" "Content-Type" = "application/json" } # 查询目标应用在租户中的服务主体ID $spQueryUri = "https://graph.microsoft.com/v1.0/servicePrincipals?`$filter=appId eq '$targetMultiTenantAppId'" $targetSp = Invoke-RestMethod -Uri $spQueryUri -Headers $headers if (-not $targetSp.value) { Write-Host "目标应用未在当前租户中注册,请先让租户用户访问应用触发服务主体创建" exit } $targetSpId = $targetSp.value[0].id # 调用Graph API完成租户级管理员同意 $consentUri = "https://graph.microsoft.com/v1.0/oauth2PermissionGrants" $consentBody = @{ clientId = $targetMultiTenantAppId consentType = "AllPrincipals" resourceId = $targetSpId scope = $requiredScopes } | ConvertTo-Json try { $response = Invoke-RestMethod -Method Post -Uri $consentUri -Headers $headers -Body $consentBody Write-Host "应用同意已成功完成" } catch { Write-Host "同意失败:$($_.Exception.Message)" if ($_.ErrorDetails.Message) { Write-Host "详细错误:$($_.ErrorDetails.Message)" } }
关键注意事项
- 需先在目标租户中创建服务主体(可通过Azure AD门户或
New-AzADServicePrincipal命令),并为其分配Application.ReadWrite.All应用权限,由租户管理员完成权限同意。 - 目标多租户应用需先被租户用户访问过一次,确保其服务主体已在目标租户中创建,否则查询
servicePrincipals会返回空结果。 requiredScopes需严格匹配目标应用注册时定义的权限范围,不能随意添加未声明的权限。
内容的提问来源于stack exchange,提问作者baysidejr

