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

Azure函数用用户分配托管身份连接SQL托管实例登录失败求助

Azure函数应用使用用户分配托管身份连接SQL托管实例登录失败排查

问题描述

我有一个Azure函数应用,尝试使用名为MI_ODBC的用户分配托管身份,通过azure.identity Python库的DefaultAzureCredential(无凭据认证)将[1234, 'Hello']插入到名为test_table_do_not_use的SQL表中。此前连接Azure SQL DB时可成功执行,但切换到Azure SQL托管实例后,出现登录失败错误。

函数应用代码

import os
import pyodbc, struct
from azure.identity import DefaultAzureCredential
import azure.functions as func
import logging


sql_insert_data = [1234, 'Hello']

def insert_all(data):
    sql = "INSERT INTO database.dbo.test_table_do_not_use(column1, column2) VALUES (?, ?)"

    cnxn = get_conn()
    crsr = cnxn.cursor()

    try:
        crsr.execute(sql, data)
    except Exception as e:
        crsr.rollback()
        print(e)
        print('Transaction rollback')
    else:
        cnxn.commit()
        crsr.close()
        cnxn.close()


# Managed Identity Auth
def get_conn():
    credential = DefaultAzureCredential(exclude_interactive_browser_credential=False)
    token_bytes = credential.get_token("https://database.windows.net/.default").token.encode("UTF-16-LE")
    token_struct = struct.pack(f'<I{len(token_bytes)}s', len(token_bytes), token_bytes)
    SQL_COPT_SS_ACCESS_TOKEN = 1256  # This connection option is defined by microsoft in msodbcsql.h
    connection_string = os.environ["SQLAZURECONNSTR_connectionstringname"]
    conn = pyodbc.connect(connection_string, attrs_before={SQL_COPT_SS_ACCESS_TOKEN: token_struct})
    return conn

app = func.FunctionApp()

@app.schedule(schedule="0 */1 * * * *", arg_name="myTimer", run_on_startup=True,
              use_monitor=False) 
def timer_trigger(myTimer: func.TimerRequest) -> None:
    if myTimer.past_due:
        logging.info('The timer is past due!')
    logging.info('Python timer trigger function executed.')
    return insert_all(data=sql_insert_data)

托管身份认证连接字符串

Driver={ODBC Driver 18 for SQL Server};Server=tcp:<database-server-name>.database.windows.net,1433;Database=<database-name>;Encrypt=yes;TrustServerCertificate=no;Connection Timeout=30

错误信息

Result: Failure Exception: InterfaceError: ('28000', "[28000] [Microsoft][ODBC Driver 18 for SQL Server][SQL Server]Login failed for user '<token-identified principal>'. (18456) (SQLDriverConnect)") Stack: File "/azure-functions-host/workers/python/3.11/LINUX/X64/azure_functions_worker/dispatcher.py", line 505, in _handle__invocation_request call_result = await self._loop.run_in_executor( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/concurrent/futures/thread.py", line 58, in run result = self.fn(*self.args, **self.kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/azure-functions-host/workers/python/3.11/LINUX/X64/azure_functions_worker/dispatcher.py", line 778, in _run_sync_func return ExtensionManager.get_sync_invocation_wrapper(context, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/azure-functions-host/workers/python/3.11/LINUX/X64/azure_functions_worker/extension.py", line 215, in _raw_invocation_wrapper result = function(**args) ^^^^^^^^^^^^^^^^ File "/home/site/wwwroot/function_app.py", line 73, in timer_trigger return insert_all(data=sql_insert_data) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/site/wwwroot/function_app.py", line 30, in insert_all cnxn = get_conn() ^^^^^^^^^^ File "/home/site/wwwroot/function_app.py", line 62, in get_conn conn = pyodbc.connect(connection_string, attrs_before={SQL_COPT_SS_ACCESS_TOKEN: token_struct})

SQL认证正常工作的代码

使用凭据存储在函数应用配置的连接字符串时,函数可正常工作,对应代码:

def get_conn():
    connection_string = os.environ["SQLAZURECONNSTR_sqlauthstringname"]
    conn = pyodbc.connect(connection_string)
    return conn

已完成的配置操作

  • 在函数应用的“Identity”部分添加了该用户分配托管身份;
  • 在Azure SQL托管实例的数据库中执行以下SQL创建该身份的用户并分配角色:
    CREATE USER [user-assigned-identity-name] FROM EXTERNAL PROVIDER;
    ALTER ROLE db_datareader ADD MEMBER [user-assigned-identity-name];
    ALTER ROLE db_datawriter ADD MEMBER [user-assigned-identity-name];
    ALTER ROLE db_ddladmin ADD MEMBER [user-assigned-identity-name];
    
  • 为该用户分配托管身份添加了SQL Managed Instance Contributor角色(作用域为托管实例所在资源组);
  • 在托管实例的网络安全组入站规则中添加了“AzureCloud”(优先级400)以白名单所有函数应用。

排查建议

  • 确认服务器地址格式:SQL托管实例公共端点的格式应为<mi-name>.public.<region>.database.windows.net,3342,而非SQL DB的1433端口。连接字符串中如果使用了错误的端口或FQDN,会导致身份验证失败。
  • 强制指定用户分配托管身份:DefaultAzureCredential可能会自动选择其他凭据来源,需在初始化时指定目标托管身份的客户端ID,确保使用正确的身份:
    credential = DefaultAzureCredential(
        managed_identity_client_id="<MI_ODBC的客户端ID>",
        exclude_interactive_browser_credential=True
    )
    
  • 检查托管身份权限范围:将SQL Managed Instance Contributor角色的作用域从资源组调整为托管实例本身,资源组级别的权限可能无法满足托管实例的身份验证要求。
  • 验证Azure AD身份验证配置:确认SQL托管实例已启用Azure AD身份验证,并且设置了有效的Azure AD管理员。未启用AD auth的实例无法使用托管身份登录。
  • 核对SQL用户名称:创建SQL用户时使用的[user-assigned-identity-name]必须与托管身份的显示名称完全一致,包括大小写和特殊字符,避免拼写错误。
  • 确认用户创建的数据库范围:确保是在连接字符串指定的<database-name>中创建了托管身份用户,而非master数据库。
  • 检查网络连通性:除了NSG规则,确认函数应用的出站IP能访问托管实例的对应端口(公共端点3342),私有端点场景需确保VNet对等或路由配置正确。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 15:03:13