如何使用在部署管道中运行的控制台应用程序连接到具有托管标识的 Azure SQL 数据库?

问题描述 投票:0回答:1

我正在尝试使用托管标识以无密码方式连接到 Azure SQL 数据库。连接我的 .NET Core 网站很顺利,但现在我尝试对部署期间在 Azure DevOps 管道中运行的控制台应用程序执行相同的操作,该管道的工作是运行迁移和数据种子。

目前,我的想法是尝试使用我的 DevOps Service Connection 创建的帐户连接到数据库。要从我的控制台应用程序访问该帐户,我想使用 Azure Powershell 任务来运行该应用程序,并使用 Active Directory 默认身份验证(这对于这种情况看起来是一个不错的选择)登录服务器。

我所做的是:

  • 创建服务连接(AzureRM 服务连接,配置有工作负载身份联合(自动))
  • 在经典发布管道中,添加一个 Azure Powershell 任务,该任务使用服务连接标识运行,并执行控制台应用程序
  • 创建数据库,为服务连接创建的帐户添加登录名,并为其赋予
    datareader
    datawriter
    ddladmin
    角色。打开防火墙以允许其他 Azure 资源访问。
  • 在控制台应用程序中,使用以下连接字符串连接到 Azure SQL:
    Data Source=tcp:redacted.database.windows.net,1433;Initial Catalog=redacteddb; Authentication=Active Directory Default; Encrypt=True;

但这不起作用。使用我在此处描述的过程,控制台应用程序崩溃并显示以下错误消息:

Microsoft.Data.SqlClient.SqlException (0x80131904): Connection Timeout Expired.  The timeout period elapsed during the post-login phase.  The connection could have timed out while waiting for server to complete the login process and respond; Or it could have timed out while attempting to create multiple active connections.  The duration spent while attempting to connect to this server was - [Pre-Login] initialization=314; handshake=165; [Login] initialization=2; authentication=57; [Post-Login] complete=14003; 

我做错了什么吗?或者还有其他方法来完成该任务吗?


编辑:添加了更多上下文信息以供参考:

控制台应用程序的源代码

Console.WriteLine($"Connection string: '{connString}'");

Console.WriteLine("Connecting...");

try
{
    using var conn = new SqlConnection(connString);
    conn.Open();

    Console.WriteLine("Connected");

    Console.WriteLine("Creating a table...");

    string query = "DROP TABLE IF EXISTS Hello";

    using (var cmd = new SqlCommand(query, conn))
    {
        cmd.ExecuteNonQuery();
    }

    query = "CREATE TABLE Hello";
    using (var cmd = new SqlCommand(query, conn))
    {
        int actionCount = cmd.ExecuteNonQuery();
        Console.WriteLine($"{actionCount} affected rows");
    }

    Console.WriteLine("Done without errors");
}
catch (Exception ex)
{
    Console.Error.WriteLine(ex.ToString());
}

部署管道如下所示

最后,今天的 powershell 任务的输出

2023-11-15T08:24:36.4142211Z ##[section]Starting: Azure PowerShell script: InlineScript
2023-11-15T08:24:36.4311735Z ==============================================================================
2023-11-15T08:24:36.4312081Z Task         : Azure PowerShell
2023-11-15T08:24:36.4312267Z Description  : Run a PowerShell script within an Azure environment
2023-11-15T08:24:36.4312636Z Version      : 5.230.0
2023-11-15T08:24:36.4312752Z Author       : Microsoft Corporation
2023-11-15T08:24:36.4312942Z Help         : https://aka.ms/azurepowershelltroubleshooting
2023-11-15T08:24:36.4313249Z ==============================================================================
2023-11-15T08:24:38.1855588Z Generating script.
2023-11-15T08:24:38.2747050Z ========================== Starting Command Output ===========================
2023-11-15T08:24:38.3054027Z ##[command]"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Unrestricted -Command ". 'D:\a\_temp\a663ac37-9de0-4429-9678-aff5ce3ef8e6.ps1'"
2023-11-15T08:24:40.2425157Z Added TLS 1.2 in session.
2023-11-15T08:24:48.8484630Z ##[command]Import-Module -Name C:\Modules\az_9.3.0\Az.Accounts\2.13.1\Az.Accounts.psd1 -Global
2023-11-15T08:24:53.0323351Z WARNING: Both Az and AzureRM modules were detected on this machine. Az and AzureRM modules cannot be imported in the 
2023-11-15T08:24:53.0340865Z same session or used in the same script or runbook. If you are running PowerShell in an environment you control you can
2023-11-15T08:24:53.0347471Z  use the 'Uninstall-AzureRm' cmdlet to remove all AzureRm modules from your machine. If you are running in Azure 
2023-11-15T08:24:53.0360272Z Automation, take care that none of your runbooks import both Az and AzureRM modules. More information can be found 
2023-11-15T08:24:53.0363091Z here: https://aka.ms/azps-migration-guide
2023-11-15T08:25:19.9532103Z ##[command]Clear-AzContext -Scope CurrentUser -Force -ErrorAction SilentlyContinue
2023-11-15T08:25:20.6450837Z ##[command]Clear-AzContext -Scope Process
2023-11-15T08:25:20.6938006Z ##[command]Connect-AzAccount System.Collections.Hashtable
2023-11-15T08:25:23.7761139Z 
2023-11-15T08:25:23.8085637Z VERBOSE: Command [Connect-AzAccount] succeeded.
2023-11-15T08:25:23.8137560Z ##[command]Set-AzContext System.Collections.Hashtable
2023-11-15T08:25:24.0568462Z VERBOSE: Command [Set-AzContext] succeeded.
2023-11-15T08:25:27.7078802Z Connection string: 'Data Source=tcp:redacted.database.windows.net,1433;Initial Catalog=redacteddb; Authentication=Active Directory Default; Encrypt=True;'
2023-11-15T08:25:27.7091314Z Connexion...
2023-11-15T08:25:44.5334476Z ##[error]Microsoft.Data.SqlClient.SqlException (0x80131904): Connection Timeout Expired.  The timeout period elapsed during the post-login phase.  The connection could have timed out while waiting for server to complete the login process and respond; Or it could have timed out while attempting to create multiple active connections.  The duration spent while attempting to connect to this server was - [Pre-Login] initialization=254; handshake=172; [Login] initialization=3; authentication=17; [Post-Login] complete=14019; 
 ---> System.ComponentModel.Win32Exception (258): The wait operation timed out.
   at Microsoft.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at Microsoft.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at Microsoft.Data.SqlClient.TdsParserStateObject.ThrowExceptionAndWarning(Boolean callerHasConnectionLock, Boolean asyncClose)
   at Microsoft.Data.SqlClient.TdsParserStateObject.ReadSniError(TdsParserStateObject stateObj, UInt32 error)
   at Microsoft.Data.SqlClient.TdsParserStateObject.ReadSniSyncOverAsync()
   at Microsoft.Data.SqlClient.TdsParserStateObject.TryReadNetworkPacket()
   at Microsoft.Data.SqlClient.TdsParserStateObject.TryPrepareBuffer()
   at Microsoft.Data.SqlClient.TdsParserStateObject.TryReadByte(Byte& value)
   at Microsoft.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at Microsoft.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
   at Microsoft.Data.SqlClient.SqlInternalConnectionTds.CompleteLogin(Boolean enlistOK)
   at Microsoft.Data.SqlClient.SqlInternalConnectionTds.AttemptOneLogin(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean ignoreSniOpenTimeout, TimeoutTimer timeout, Boolean withFailover)
   at Microsoft.Data.SqlClient.SqlInternalConnectionTds.LoginNoFailover(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString connectionOptions, SqlCredential credential, TimeoutTimer timeout)
   at Microsoft.Data.SqlClient.SqlInternalConnectionTds.OpenLoginEnlist(TimeoutTimer timeout, SqlConnectionString connectionOptions, SqlCredential credential, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance)
   at Microsoft.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString userConnectionOptions, SessionData reconnectSessionData, Boolean applyTransientFaultHandling, String accessToken, DbConnectionPool pool)
   at Microsoft.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions)
   at Microsoft.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool pool, DbConnection owningObject, DbConnectionOptions options, DbConnectionPoolKey poolKey, DbConnectionOptions userOptions)
   at Microsoft.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
   at Microsoft.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
   at Microsoft.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at Microsoft.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at Microsoft.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
   at Microsoft.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at Microsoft.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at Microsoft.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry, SqlConnectionOverrides overrides)
   at Microsoft.Data.SqlClient.SqlConnection.Open(SqlConnectionOverrides overrides)
   at Microsoft.Data.SqlClient.SqlConnection.Open()
   at DbCheckService.StartAsync(CancellationToken cancellationToken) in D:\a\1\s\DbDeploy\DbCheckService.cs:line 37
ClientConnectionId:[redacted]
Error Number:-2,State:0,Class:11
2023-11-15T08:25:44.5382166Z info: Microsoft.Hosting.Lifetime[0]
2023-11-15T08:25:44.5399882Z       Application is shutting down...
2023-11-15T08:25:44.5401124Z info: Microsoft.Hosting.Lifetime[0]
2023-11-15T08:25:44.5402216Z       Application started. Press Ctrl+C to shut down.
2023-11-15T08:25:44.5403087Z info: Microsoft.Hosting.Lifetime[0]
2023-11-15T08:25:44.5407826Z       Hosting environment: Production
2023-11-15T08:25:44.5420762Z info: Microsoft.Hosting.Lifetime[0]
2023-11-15T08:25:44.5426218Z       Content root path: D:\a\r1\a\dbdeploy\bin
2023-11-15T08:25:44.5439858Z Account                              SubscriptionName TenantId                             Environment
2023-11-15T08:25:44.5446942Z -------                              ---------------- --------                             -----------
2023-11-15T08:25:44.5462367Z *** [redacted]    [redacted] AzureCloud 
2023-11-15T08:25:44.5463374Z 
2023-11-15T08:25:44.5476230Z Name               : [redacted] ([redacted]) - [redacted] - 
2023-11-15T08:25:44.5482252Z                      ***
2023-11-15T08:25:44.5495682Z Account            : ***
2023-11-15T08:25:44.5502212Z Environment        : AzureCloud
2023-11-15T08:25:44.5515078Z Subscription       : [redacted]
2023-11-15T08:25:44.5521024Z Tenant             : [redacted]
2023-11-15T08:25:44.5528995Z TokenCache         : 
2023-11-15T08:25:44.5538222Z VersionProfile     : 
2023-11-15T08:25:44.5546931Z ExtendedProperties : {}
2023-11-15T08:25:44.5552281Z 
2023-11-15T08:25:44.5557895Z 
2023-11-15T08:25:44.5563558Z 
2023-11-15T08:25:44.8553919Z Added TLS 1.2 in session.
2023-11-15T08:25:45.2851397Z ##[section]Finishing: Azure PowerShell script: InlineScript
azure-devops azure-pipelines azure-sql-database
1个回答
0
投票

Azure Active Directory 交互式比 Active Directory 默认更安全。因此,您可以使用以下连接字符串:

Server=tcp:<serverNmae>.database.windows.net,1433;Initial Catalog=<databaseName>;Persist Security Info=False;User ID=<ADId>;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Authentication=\"Active Directory Interactive\";

完整代码如下:

using System;
using System.Data;
using Microsoft.Data.SqlClient;

string connectionString = "Server=tcp:<serverName>.database.windows.net,1433;Initial Catalog=<databaseName>;Persist Security Info=False;User ID=<ADId>;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Authentication=\"Active Directory Interactive\";";

using (SqlConnection connection = new SqlConnection(connectionString))
{
    connection.Open();

    string query = "SELECT * FROM <tableName>";
    using (SqlCommand command = new SqlCommand(query, connection))
    {
        using (SqlDataReader reader = command.ExecuteReader())
        {
            while (reader.Read())
            {
                int studentId = reader.GetInt32(reader.GetOrdinal("Id"));
                string Name = reader.GetString(reader.GetOrdinal("Name"));
                Console.WriteLine($"StudentID: {studentId}, Name: {Name}");
                     
                }
        }
    }

}

首次打开浏览器进行身份验证。之后连接后会自动连接。数据库连接成功如下图:

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.