使用PowerShell Invoke-Sqlcmd连接Azure SQL Database失败求助
问题:PowerShell Invoke-Sqlcmd连接Azure SQL Database失败(服务主体认证)
背景
- 本机WSL Ubuntu中的sqlcmd(版本18.2.0001.1)可通过服务主体访问令牌成功连接Azure SQL Database
- 同一机器上的PowerShell Invoke-Sqlcmd(22.2版本)连接失败,同时使用
System.Data.SqlClient.SqlConnection方式也报错
用户操作与错误信息
1. 获取服务主体令牌的命令(WSL中执行)
az account get-access-token --resource https://database.windows.net --output tsv | cut -f 1 | tr -d '\n' | iconv -f ascii -t UTF-16LE > TOKEN_LOCATION
2. 可正常运行的sqlcmd命令
sqlcmd -S myserver.database.windows.net -d mydatabase -G -P TOKEN_LOCATION -C -Q "print 'hello'"
3. 失败的Invoke-Sqlcmd命令
Invoke-Sqlcmd -ServerInstance myserver.database.windows.net -Database mydatabase -AccessToken (TOKEN_LOCATION or $TOKEN) -query "print 'hello'" -TrustServerCertificate -Verbose
错误信息:
Invoke-Sqlcmd: Login failed for user '<token-identified principal>'. Invoke-Sqlcmd: Incorrect syntax was encountered while parsing ''.
4. 失败的SqlConnection代码
$SQLConnection = New-Object System.Data.SqlClient.SqlConnection($ConnectionString) $SQLConnection.AccessToken = $token $SQLConnection.Open()
错误信息:
Exception calling "Open" with "0" argument(s): "A connection was successfully established with the server, but then an error occurred during the login process. (provider: TCP Provider, error : 0 - An existing connection was forcibly closed by the remote host.)"
原因分析
- 令牌格式不匹配:sqlcmd的
-P参数要求令牌是UTF-16LE编码的文件,所以你用iconv转码并写入文件;但Invoke-Sqlcmd的-AccessToken参数需要的是原始UTF-8格式的令牌字符串,转码后的内容会被识别为无效令牌。 - 令牌传递错误:你可能直接传入了文件路径或者转码后的字节内容,而非原始的令牌字符串。
- 旧版驱动兼容性:
System.Data.SqlClient是旧版SQL驱动,对Azure SQL的服务主体令牌认证支持存在兼容性问题,建议改用新版Microsoft.Data.SqlClient。
解决办法
方法1:修正Invoke-Sqlcmd的令牌获取与传递
先在PowerShell中直接获取原始UTF-8格式的令牌字符串,无需转码和写入文件:
# 获取原始令牌字符串(自动去除多余换行) $token = (az account get-access-token --resource https://database.windows.net --output tsv).Trim() # 正确调用Invoke-Sqlcmd Invoke-Sqlcmd -ServerInstance "myserver.database.windows.net" -Database "mydatabase" -AccessToken $token -Query "print 'hello'" -TrustServerCertificate -Verbose
方法2:改用Microsoft.Data.SqlClient连接
System.Data.SqlClient已被微软标记为维护模式,推荐使用Microsoft.Data.SqlClient:
# 先安装Microsoft.Data.SqlClient模块(首次运行) Install-Package -Name Microsoft.Data.SqlClient -ProviderName NuGet -Scope CurrentUser -Force # 获取令牌 $token = (az account get-access-token --resource https://database.windows.net --output tsv).Trim() # 构建连接字符串并打开连接 $connectionString = "Server=tcp:myserver.database.windows.net,1433;Database=mydatabase;TrustServerCertificate=True;" $SQLConnection = New-Object Microsoft.Data.SqlClient.SqlConnection($connectionString) $SQLConnection.AccessToken = $token $SQLConnection.Open() # 执行查询示例 $command = $SQLConnection.CreateCommand() $command.CommandText = "print 'hello'" $command.ExecuteNonQuery() # 关闭连接 $SQLConnection.Close()
诊断思路
- 验证令牌有效性:执行
az account get-access-token --resource https://database.windows.net,复制令牌到JWT解码工具(如本地的jwt.ms)检查:aud字段是否为https://database.windows.netexp字段是否未过期
- 确认服务主体权限:检查服务主体是否是Azure SQL Server的Microsoft Entra管理员,或已被授予目标数据库的访问角色(如
db_owner) - 测试网络连通性:在PowerShell中执行
Test-NetConnection myserver.database.windows.net -Port 1433,确认1433端口可正常访问 - 升级Invoke-Sqlcmd版本:执行
Update-Module -Name SqlServer升级到最新版,旧版本可能存在令牌认证的bug - 对比令牌内容:将sqlcmd使用的文件内容转成UTF-8字符串,和PowerShell获取的
$token对比,确保两者一致
内容的提问来源于stack exchange,提问作者Shimii
相关产品推荐
相关产品推荐

