如何在PowerShell中使用AAD认证调用Azure CosmosDB REST API
问题:AAD认证调用Cosmos DB REST API返回401未授权
已配置Cosmos DB仅允许AAD认证,且为用户组分配了角色,但调用REST API查询集合时返回401错误,脚本及错误信息如下:
原脚本
Param( [string] $AccountName, [string] $DatabaseName, [string] $ResourceGroupName ) $azContext = Get-AzContext $azProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile $profileClient = New-Object -TypeName Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient -ArgumentList ($azProfile) $token = $profileClient.AcquireAccessToken($azContext.Subscription.TenantId) $dateTime = [DateTime]::UtcNow.ToString("r") $keyType="aad" $tokenVersion="1.0" $authHeader=[System.Web.HttpUtility]::UrlEncode("type=$keyType&ver=$tokenVersion&sig=$($token.AccessToken)") $header = @{authorization=$authHeader;"x-ms-version"="2018-12-31";"x-ms-documentdb-isquery"="True";"x-ms-date"=$dateTime} $contentType= "application/query+json" $collectionName="CapabilityManagement.Capability" $restUri="https://$AccountName.documents.azure.com/dbs/$DatabaseName/colls/$collectionName/docs" $query=@" { "query": "SELECT * FROM contacts c WHERE c.id = @id", "parameters": [ { "name": "@id", "value": "57128516-26ff-475d-95bc-6d54c4b91b89" } ] } "@ [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $result = Invoke-RestMethod -Method Post -ContentType $contentType -Uri $restUri -Headers $header -Body $query
错误信息
Invoke-RestMethod : The remote server returned an error: (401) Unauthorized. At C:\Users\Ksp\Documents\test.ps1:49 char:15 + ... $result = Invoke-RestMethod -Method Post -ContentType $contentType ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
解决方案
1. 修正AAD令牌的获取逻辑
当前获取的令牌受众是Azure资源管理器(ARM),而Cosmos DB要求令牌受众必须是https://documents.azure.com/。修改令牌获取代码:
# 替换原令牌获取行,指定资源为Cosmos DB的受众 $token = $profileClient.AcquireAccessToken("https://documents.azure.com/")
2. 移除认证头的URL编码
认证头不需要整体URL编码,直接拼接字符串即可:
# 去掉UrlEncode,直接拼接认证头 $authHeader="type=$keyType&ver=$tokenVersion&sig=$($token.AccessToken)"
3. 验证角色分配的有效性
- 确保角色分配的范围是Cosmos DB账户、目标数据库或集合,而非资源组。
- 确认分配的角色包含所需权限:比如查询操作需要
Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/read权限,可使用内置角色Cosmos DB Built-in Data Reader。
4. 更新API版本(可选)
建议使用较新的API版本,避免旧版本的兼容性问题:
# 修改x-ms-version为较新版本 $header = @{authorization=$authHeader;"x-ms-version"="2021-05-15";"x-ms-documentdb-isquery"="True";"x-ms-date"=$dateTime}
修改后的完整脚本
Param( [string] $AccountName, [string] $DatabaseName, [string] $ResourceGroupName ) $azContext = Get-AzContext $azProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile $profileClient = New-Object -TypeName Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient -ArgumentList ($azProfile) # 获取Cosmos DB专用的AAD令牌 $token = $profileClient.AcquireAccessToken("https://documents.azure.com/") $dateTime = [DateTime]::UtcNow.ToString("r") $keyType="aad" $tokenVersion="1.0" # 无需URL编码,直接拼接认证头 $authHeader="type=$keyType&ver=$tokenVersion&sig=$($token.AccessToken)" # 使用较新的API版本 $header = @{authorization=$authHeader;"x-ms-version"="2021-05-15";"x-ms-documentdb-isquery"="True";"x-ms-date"=$dateTime} $contentType= "application/query+json" $collectionName="CapabilityManagement.Capability" $restUri="https://$AccountName.documents.azure.com/dbs/$DatabaseName/colls/$collectionName/docs" $query=@" { "query": "SELECT * FROM contacts c WHERE c.id = @id", "parameters": [ { "name": "@id", "value": "57128516-26ff-475d-95bc-6d54c4b91b89" } ] } "@ [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $result = Invoke-RestMethod -Method Post -ContentType $contentType -Uri $restUri -Headers $header -Body $query
内容的提问来源于stack exchange,提问作者KSP
相关产品推荐
相关产品推荐

