You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Azure托管标识函数连接PostgreSQL弹性服务器时密码认证失败

问题:Azure函数托管身份连接PostgreSQL弹性服务器认证失败

我有配置了系统托管标识的Azure Function,以及已配置Azure AD身份的Azure Database for PostgreSQL弹性服务器(测试时允许公网访问):

  • 本地通过DefaultAzureCredential(az login)连接时,在NpgsqlConnectionStringBuilder指定邮箱作为Username可以正常连接;不指定的话会用本地机器用户名,无法使用Azure凭据。
  • 部署到函数后,用ManagedIdentityCredential尝试连接时,能获取有效访问令牌,但始终因用户名问题认证失败。

相关代码

[Function("TestOpenConnection")]
public async Task<IActionResult> RunTestOpenConnectionMisc([HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req, CancellationToken cancellationToken)
{
    _logger.LogInformation(3009, "C# HTTP trigger TestOpenConnection function processed a request.");
    
    string connectionString = new NpgsqlConnectionStringBuilder
    {
        Host = "my-xyz-pgsql.postgres.database.azure.com",
        Port = 5432,
        Database = "my_test_db",
        SslMode = SslMode.Require,
        // ?? Username
    }.ToString();

    NpgsqlDataSourceBuilder dataSourceBuilder = new(connectionString);

    TimeSpan successRefreshInterval = TimeSpan.FromMinutes(55);
    TimeSpan failureRefreshInterval = TimeSpan.FromSeconds(30);

    dataSourceBuilder.UsePeriodicPasswordProvider(async (_, ct)
        => await GetAccessTokenAsync(ct), successRefreshInterval, failureRefreshInterval);

    await using var dataSource = dataSourceBuilder.Build();

    try
    {
        await dataSource.OpenConnectionAsync(cancellationToken);
    }
    catch (Exception e)
    {
        _logger.LogError(2004, e, "FAILED TO OPEN DATABASE CONNECTION");
        return new BadRequestObjectResult(e);
    }
    
    _logger.LogInformation(3011, "Connection opened successfully");
    return new OkObjectResult("Connection opened successfully");
}

private async Task<string> GetAccessTokenAsync(CancellationToken cancellationToken)
{
    ManagedIdentityCredential credential = new();
    string resource = "https://ossrdbms-aad.database.windows.net";
    TokenRequestContext tokenRequestContext = new([$"{resource}/.default"]);
    AccessToken fullToken = await credential.GetTokenAsync(tokenRequestContext, cancellationToken).ConfigureAwait(false);
    return fullToken.Token;
}

错误信息

[Error]   FAILED TO OPEN DATABASE CONNECTION
password authentication failed for user "funct-test-postrgre-xyz"

尝试过函数名称、服务主体ObjectId、ApplicationId作为用户名,均报错28P01: password authentication failed for user "xxx"。已执行以下SQL配置:

-- Create a Role for the Enterprise Application:
CREATE ROLE "00000000-0000-0000-0000-000000000000" WITH LOGIN;

-- Grant Privileges to the Role:
GRANT CONNECT ON DATABASE my_test_db TO "00000000-0000-0000-0000-000000000000";
GRANT USAGE ON SCHEMA public TO "00000000-0000-0000-0000-000000000000";
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO "00000000-0000-0000-0000-000000000000";

-- Configure Default Privileges (Optional):
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO "00000000-0000-0000-0000-000000000000";

提问:是否需要在PostgreSQL端进行额外配置?应使用服务主体的ApplicationId还是ObjectId?如何验证PostgreSQL配置是否正确?


解决方案

1. 正确的用户名选择

必须使用托管标识的ObjectId作为PostgreSQL的用户名:

  • 创建角色时的ObjectId必须和函数托管标识的ObjectId完全一致,且必须用双引号包裹(PostgreSQL会将无引号的UUID转为小写,导致与Azure AD传递的ObjectId不匹配)
  • 函数名称、ApplicationId均不被Azure AD认证PostgreSQL时认可,请勿使用

2. 修正连接字符串配置

必须在NpgsqlConnectionStringBuilder中明确指定Username为托管标识的ObjectId,否则Npgsql会默认使用函数名称作为用户名,这就是报错的直接原因:

string connectionString = new NpgsqlConnectionStringBuilder
{
    Host = "my-xyz-pgsql.postgres.database.azure.com",
    Port = 5432,
    Database = "my_test_db",
    SslMode = SslMode.Require,
    Username = "你的托管标识ObjectId" // 必须添加该行,替换为实际的ObjectId
}.ToString();

3. 验证PostgreSQL配置正确性

执行以下SQL命令检查配置:

-- 检查角色是否存在
SELECT rolname FROM pg_roles WHERE rolname = '你的托管标识ObjectId';

-- 检查数据库连接权限
SELECT has_database_privilege('你的托管标识ObjectId', 'my_test_db', 'CONNECT');

-- 检查schema使用权限
SELECT has_schema_privilege('你的托管标识ObjectId', 'public', 'USAGE');

-- 检查表权限
SELECT tablename, 
       has_table_privilege('你的托管标识ObjectId', tablename, 'SELECT') AS select_perm,
       has_table_privilege('你的托管标识ObjectId', tablename, 'INSERT') AS insert_perm,
       has_table_privilege('你的托管标识ObjectId', tablename, 'UPDATE') AS update_perm,
       has_table_privilege('你的托管标识ObjectId', tablename, 'DELETE') AS delete_perm
FROM pg_tables WHERE schemaname = 'public';

所有查询返回true则说明权限配置正确。

4. 额外检查项

  • 确认PostgreSQL服务器已启用Azure AD认证:在Azure门户的服务器「身份验证」选项卡中,已设置「Azure Active Directory管理员」(用户或服务主体均可)
  • 无需给函数托管标识分配Azure层面的PostgreSQL权限,仅需PostgreSQL内部角色配置正确即可
  • 你的GetAccessTokenAsync中使用的令牌资源https://ossrdbms-aad.database.windows.net是正确的,无需修改

内容的提问来源于stack exchange,提问作者tridy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 14:14:52