如何使用 Azure 函数使用托管标识连接 Azure SQL DW

时间:2021-05-27 15:53:17

标签: c# azure azure-functions azure-sqldw azure-managed-identity

我已经尝试使用 Azure 函数代码连接到 SQL 服务器并成功连接。但是,当我尝试对 SQL DW(专用 SQL 池)使用相同的代码时,出现以下错误。

谁能帮我解决这个问题?我无法弄清楚这里的问题是什么。

message": "Exception while executing function: Synapse_Connectivity -> A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 25 - Connection string is not valid) -> The parameter is incorrect",
    "errorDetails": "Microsoft.Azure.WebJobs.Host.FunctionInvocationException : Exception while executing function: Synapse_Connectivity ---> System.Data.SqlClient.SqlException : A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 25 - Connection string is not valid) ---> System.ComponentModel.Win32Exception : The parameter is incorrect \r\n   End of inner exception\r\n   at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity,SqlConnectionString connectionOptions,SqlCredential credential,Object providerInfo,String newPassword,SecureString newSecurePassword,Boolean redirectedUserInstance,SqlConnectionString userConnectionOptions,SessionData reconnectSessionData,DbConnectionPool pool,String accessToken,Boolean applyTransientFaultHandling,SqlAuthenticationProviderManager sqlAuthProviderManager)\r\n   at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options,DbConnectionPoolKey poolKey,Object poolGroupProviderInfo,DbConnectionPool pool,DbConnection owningConnection,DbConnectionOptions userOptions)\r\n   at System.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool pool,DbConnection owningObject,DbConnectionOptions options,DbConnectionPoolKey poolKey,DbConnectionOptions userOptions)\r\n   at System.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject,DbConnectionOptions userOptions,DbConnectionInternal oldConnection)\r\n   at System.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject,DbConnectionOptions userOptions,DbConnectionInternal oldConnection)\r\n   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject,UInt32 waitForMultipleObjectsTimeout,Boolean allowCreate,Boolean onlyOneCheckConnection,DbConnectionOptions userOptions,DbConnectionInternal& connection)\r\n   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject,TaskCompletionSource`1 retry,DbConnectionOptions userOptions,DbConnectionInternal& connection)\r\n   at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection,TaskCompletionSource`1 retry,DbConnectionOptions userOptions,DbConnectionInternal oldConnection,DbConnectionInternal& connection)\r\n   at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection,DbConnectionFactory connectionFactory,TaskCompletionSource`1 retry,DbConnectionOptions userOptions)\r\n   at System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection,DbConnectionFactory connectionFactory,TaskCompletionSource`1 retry,DbConnectionOptions userOptions)\r\n   at System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource`1 retry)\r\n   at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)\r\n   at System.Data.SqlClient.SqlConnection.Open()\r\n   at async FunctionApp_WBA_QA.Synapse_Connectivity.Run(HttpRequestMessage req,TraceWriter log)\r\n   at async Microsoft.Azure.WebJobs.Host.Executors.FunctionInvoker`2.InvokeAsync[TReflected,TReturnValue](Object instance,Object[] arguments)\r\n   at async Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.InvokeAsync(IFunctionInvoker invoker,ParameterHelper parameterHelper,CancellationTokenSource timeoutTokenSource,CancellationTokenSource functionCancellationTokenSource,Boolean throwOnTimeout,TimeSpan timerInterval,IFunctionInstance instance)\r\n   at async Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.ExecuteWithWatchersAsync(IFunctionInstance instance,ParameterHelper parameterHelper,TraceWriter traceWriter,CancellationTokenSource functionCancellationTokenSource)\r\n   at async Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.ExecuteWithLoggingAsync(??)\r\n   at async Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.ExecuteWithLoggingAsync(??) \r\n   End of inner exception\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at async Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.ExecuteWithLoggingAsync(??)\r\n   at async Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.TryExecuteAsync(IFunctionInstance functionInstance,CancellationToken cancellationToken)\r\n   at Microsoft.Azure.WebJobs.Host.Executors.ExceptionDispatchInfoDelayedException.Throw()\r\n   at async Microsoft.Azure.WebJobs.JobHost.CallAsync(??)\r\n   at async Microsoft.Azure.WebJobs.Script.ScriptHost.CallAsync(String method,Dictionary`2 arguments,CancellationToken cancellationToken)\r\n   at async Microsoft.Azure.WebJobs.Script.WebHost.WebScriptHostManager.HandleRequestAsync(FunctionDescriptor function,HttpRequestMessage request,CancellationToken cancellationToken)\r\n   at async Microsoft.Azure.WebJobs.Script.Host.FunctionRequestInvoker.ProcessRequestAsync(HttpRequestMessage request,CancellationToken cancellationToken,WebScriptHostManager scriptHostManager,WebHookReceiverManager webHookReceiverManager)\r\n   at async Microsoft.Azure.WebJobs.Script.WebHost.Controllers.FunctionsController.<>c__DisplayClass3_0.<ExecuteAsync>b__0(??)\r\n   at async Microsoft.Azure.WebJobs.Extensions.Http.HttpRequestManager.ProcessRequestAsync(HttpRequestMessage request,Func`3 processRequestHandler,CancellationToken cancellationToken)\r\n   at async Microsoft.Azure.WebJobs.Script.WebHost.Controllers.FunctionsController.ExecuteAsync(HttpControllerContext controllerContext,CancellationToken cancellationToken)\r\n   at async System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsync(HttpRequestMessage request,CancellationToken cancellationToken)\r\n   at async System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsync(HttpRequestMessage request,CancellationToken cancellationToken)\r\n   at async Microsoft.Azure.WebJobs.Script.WebHost.Handlers.WebScriptHostHandler.SendAsync(HttpRequestMessage request,CancellationToken cancellationToken)\r\n   at async Microsoft.Azure.WebJobs.Script.WebHost.Handlers.SystemTraceHandler.SendAsync(HttpRequestMessage request,CancellationToken cancellationToken)\r\n   at async System.Web.Http.HttpServer.SendAsync(HttpRequestMessage request,CancellationToken cancellationToken)"
}

我的配置是:

"CFG_SQLDB_server": "XXXX.database.windows.net",
"CFG_SQLDB_database": "XXXXqadb",
"SYNAPSE_SQL_server": "XXXXsqlsrv.database.windows.net",
"SYNAPSE_SQL_database": "XXXXqa-sqldw"

using System;
using System.Data.SqlClient;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Azure.Services.AppAuthentication;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Newtonsoft.Json;

namespace FunctionApp_SQL
{
    public static class Synapse_Connectivity
    {
        [FunctionName("Synapse_Connectivity")]
        public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]HttpRequestMessage req, TraceWriter log)
        {
            log.Info("C# HTTP trigger function processed a request.");

            string synapse_db_server = GetEnvironmentVariable("SYNAPSE_SQLDB_server");
            string synapse_db_name = GetEnvironmentVariable("SYNAPSE_SQLDB_database");

            string ResourceId = "https://database.windows.net/";

            string sqlConnectionString = String.Format("Data Source=tcp:{0},1433;Initial Catalog={1};Persist Security Info=False;Connect Timeout=30;Encrypt=True;TrustServerCertificate=False;MultipleActiveResultSets=true", synapse_db_server, synapse_db_name);

            log.Info($"sqlConnectionString : {sqlConnectionString}");

            /*Gettting the Token FOR SQL*/
            var tokenProvider = new AzureServiceTokenProvider();
            string accessToken = await tokenProvider.GetAccessTokenAsync(ResourceId);
            log.Info($"AccessToken : {accessToken}");

            log.Info("Connecting to database.");

            var myObj = "";

            using (SqlConnection conn = new SqlConnection(sqlConnectionString))
            {
                conn.AccessToken = accessToken;
                conn.Open();

                var statement = $"select * from ext.test for json path";
                log.Info($"{statement}");

                using (SqlCommand cmd = new SqlCommand(statement, conn))
                {
                    using (SqlDataReader reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            log.Info($"{reader.GetString(0)}");
                            myObj = reader.GetString(0);

                        }
                    }
                }
            }

            var jsonToReturn = JsonConvert.SerializeObject(myObj);

            return new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StringContent(jsonToReturn, Encoding.UTF8, "application/json")
            };

        }

        public static string GetEnvironmentVariable(string name)
        {
            return System.Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.Process);
        }
    }
}

1 个答案:

答案 0 :(得分:0)

根据您的帖子,我假设这是一个“专用 SQL 池(以前称为 SQL DW)”而不是 Synapse 专用 SQL 池,后者会有一些不同的答案。

我不保证其中任何一项都会奏效,但我想到了一些事项。

  1. 确保 SQL Server 防火墙配置为允许 Azure 服务:

enter image description here

  1. 在 SQL Server IAM 中,您可能需要将 Reader RBAC 角色授予 Azure 函数的托管标识:

enter image description here

[注意:这个例子有 SQL 安全管理器,你的角色很可能是读者]

  1. 我认为您需要向 Azure 函数的数据库添加一个 USER ... FROM EXTERNAL PROVIDER 并授予其 db_datareader 权限:

    从外部提供商创建用户 [your_azure_function]; EXEC sp_addrolemember 'db-datareader' [your_azure_function];

This article 用于连接不同的服务,但我认为一般规则可能适用。