You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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

如果配置没问题,输出里会显示服务主体的objectIddisplayName等信息。

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

火山引擎 最新活动