Azure SQL Server中AD管理员服务主体无法访问master数据库的解决办法
解决Azure SQL Server服务主体AD管理员无法访问master数据库的问题
我之前踩过完全一样的坑:服务主体明明已经配置成了Azure SQL Server的AD管理员,访问用户数据库时SELECT 1跑得好好的,但一碰master库就弹出SqlError 18456登录失败,而且确认访问令牌是有效的。折腾了半天终于搞明白,核心原因是服务主体作为服务器级的AD管理员,默认不会自动在master数据库中创建对应的用户对象——哪怕它拥有服务器级权限,也得手动在master库完成用户映射才能正常访问。
解决方案步骤
1. 先确认服务主体的AD管理员配置是否生效
首先用Azure CLI验证你的服务主体确实已经被正确设置为SQL Server的AD管理员:
az sql server ad-admin list --resource-group <你的资源组名称> --server-name MyAzureSqlServer
如果配置没问题,输出里会显示服务主体的objectId、displayName等信息。
2. 在master数据库中创建服务主体对应的用户
用拥有服务器级权限的账号(比如另一个AD管理员或者SQL登录管理员)连接到master数据库,执行以下T-SQL:
-- 用服务主体的显示名称或应用ID创建外部用户 CREATE USER [MyServicePrincipal] FROM EXTERNAL PROVIDER; -- 给用户分配合适的权限,比如db_owner或者只读权限 ALTER ROLE db_datareader ADD MEMBER [MyServicePrincipal];
注意:这里的
MyServicePrincipal要和你服务主体的显示名称或者应用程序ID完全一致,否则会创建失败。
3. 调整PowerShell连接逻辑(确保正确指定master库)
确保你的PowerShell代码在连接时明确指定数据库为master,并且正确传递访问令牌。示例代码如下:
$tenantId = "你的Azure AD租户ID" $clientId = "服务主体的应用程序ID" $clientSecret = "服务主体的客户端密钥" $serverName = "MyAzureSqlServer.database.windows.net" $databaseName = "master" # 获取SQL数据库的访问令牌 $tokenEndpoint = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" $tokenParams = @{ client_id = $clientId client_secret = $clientSecret scope = "https://database.windows.net/.default" grant_type = "client_credentials" } $tokenResponse = Invoke-RestMethod -Uri $tokenEndpoint -Method Post -Body $tokenParams $accessToken = $tokenResponse.access_token # 连接master库并执行查询 $connectionString = "Server=tcp:$serverName,1433;Database=$databaseName;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;" $connection = New-Object System.Data.SqlClient.SqlConnection($connectionString) $connection.AccessToken = $accessToken try { $connection.Open() $command = $connection.CreateCommand() $command.CommandText = "SELECT 1" $result = $command.ExecuteScalar() Write-Host "查询成功,结果:$result" } catch { Write-Error "连接失败:$_" } finally { $connection.Close() }
附加:配置服务主体为SQL Server AD管理员的两种合法方式
因为Az PowerShell模块的Set-AzSqlServerActiveDirectoryAdministrator不支持服务主体作为管理员,所以只能通过以下两种方式配置:
方法1:使用Azure CLI命令
# 先获取服务主体的Object ID $spObjectId = az ad sp show --id <服务主体应用ID> --query id -o tsv # 设置为SQL Server AD管理员 az sql server ad-admin create ` --resource-group <你的资源组名称> ` --server-name MyAzureSqlServer ` --display-name "MyServicePrincipal" ` --object-id $spObjectId
方法2:自定义PowerShell函数调用Azure REST API
function Set-SqlServerServicePrincipalAdmin { param( [Parameter(Mandatory=$true)] [string]$ResourceGroupName, [Parameter(Mandatory=$true)] [string]$ServerName, [Parameter(Mandatory=$true)] [string]$ServicePrincipalObjectId, [Parameter(Mandatory=$true)] [string]$ServicePrincipalDisplayName, [Parameter(Mandatory=$true)] [string]$TenantId, [Parameter(Mandatory=$true)] [string]$SubscriptionId ) # 获取Azure管理API的访问令牌(确保已通过Connect-AzAccount登录) $accessToken = (Get-AzAccessToken -ResourceUrl "https://management.azure.com/").Token $authHeader = @{ "Authorization" = "Bearer $accessToken" "Content-Type" = "application/json" } # 构建请求体 $body = @{ properties = @{ administratorType = "ServicePrincipal" login = $ServicePrincipalDisplayName sid = $ServicePrincipalObjectId tenantId = $TenantId } } | ConvertTo-Json # 调用REST API设置AD管理员 $apiUrl = "https://management.azure.com/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.Sql/servers/$ServerName/administrators/ActiveDirectory?api-version=2021-11-01" Invoke-RestMethod -Uri $apiUrl -Method Put -Headers $authHeader -Body $body } # 使用示例 Set-SqlServerServicePrincipalAdmin ` -ResourceGroupName "你的资源组名称" ` -ServerName "MyAzureSqlServer" ` -ServicePrincipalObjectId "服务主体的Object ID" ` -ServicePrincipalDisplayName "MyServicePrincipal" ` -TenantId "你的租户ID" ` -SubscriptionId "你的订阅ID"
内容的提问来源于stack exchange,提问作者mark




