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
相关产品推荐
相关产品推荐

