Azure SQL 数据库部署

Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019

每次成功生成后,都可以自动将数据库更新部署到 Azure SQL数据库。

DACPAC

部署数据库的最简单方法是创建数据层包或 DACPAC。 DACPAC 可用于打包和部署架构更改和数据。 可以在 Visual Studio 中使用 SQL 数据库项目创建 DACPAC。

要将 DACPAC 部署到 Azure SQL 数据库,请将以下代码片段添加到 azure-pipelines.yml 文件。

- task: SqlAzureDacpacDeployment@1
  displayName: Execute Azure SQL : DacpacTask
  inputs:
    azureSubscription: '<Azure service connection>'
    ServerName: '<Database server name>'
    DatabaseName: '<Database name>'
    SqlUsername: '<SQL user name>'
    SqlPassword: '<SQL user password>'
    DacpacFile: '<Location of Dacpac file in $(Build.SourcesDirectory) after compilation>'

另请参阅使用 Azure SQL 数据库部署任务时的身份验证信息

SQL 脚本

还可以使用 SQL 脚本(代替 DACPAC)来部署数据库。 下面是创建空数据库的 SQL 脚本的简单示例。

  USE [main]
  GO
  IF NOT EXISTS (SELECT name FROM main.sys.databases WHERE name = N'DatabaseExample')
  CREATE DATABASE [DatabaseExample]
  GO

要在管道中运行 SQL 脚本,需要 Azure PowerShell 脚本在 Azure 中创建和删除防火墙规则。 如果没有防火墙规则,Azure Pipelines 代理将无法与 Azure SQL 数据库通信。

以下 PowerShell 脚本创建两个规则。 可以将此脚本作为 SetAzureFirewallRule.ps1 签入存储库中。

ARM

[CmdletBinding(DefaultParameterSetName = 'None')]
param
(
  [String] [Parameter(Mandatory = $true)] $ServerName,
  [String] [Parameter(Mandatory = $true)] $ResourceGroupName,
  [String] $FirewallRuleName = "AzureWebAppFirewall"
)
$agentIP = (New-Object net.webclient).downloadstring("https://api.ipify.org")
New-AzSqlServerFirewallRule -ResourceGroupName $ResourceGroupName -ServerName $ServerName -FirewallRuleName $FirewallRuleName -StartIPAddress $agentIp -EndIPAddress $agentIP

经典

[CmdletBinding(DefaultParameterSetName = 'None')]
param
(
  [String] [Parameter(Mandatory = $true)] $ServerName,
  [String] [Parameter(Mandatory = $true)] $ResourceGroupName,
  [String] $FirewallRuleName = "AzureWebAppFirewall"
)

$ErrorActionPreference = 'Stop'

function New-AzureSQLServerFirewallRule {
  $agentIP = (New-Object net.webclient).downloadstring("https://api.ipify.org")
  New-AzureSqlDatabaseServerFirewallRule -StartIPAddress $agentIp -EndIPAddress $agentIp -RuleName $FirewallRuleName -ServerName $ServerName
}

function Update-AzureSQLServerFirewallRule{
  $agentIP= (New-Object net.webclient).downloadstring("https://api.ipify.org")
  Set-AzureSqlDatabaseServerFirewallRule -StartIPAddress $agentIp -EndIPAddress $agentIp -RuleName $FirewallRuleName -ServerName $ServerName
}

if ((Get-AzureSqlDatabaseServerFirewallRule -ServerName $ServerName -RuleName $FirewallRuleName -ErrorAction SilentlyContinue) -eq $null)
{
  New-AzureSQLServerFirewallRule
}
else
{
  Update-AzureSQLServerFirewallRule
}

以下 PowerShell 脚本将移除防火墙规则。 可以将此脚本作为 RemoveAzureFirewallRule.ps1 签入存储库中。

ARM

[CmdletBinding(DefaultParameterSetName = 'None')]
param
(
  [String] [Parameter(Mandatory = $true)] $ServerName,
  [String] [Parameter(Mandatory = $true)] $ResourceGroupName,
  [String] $FirewallRuleName = "AzureWebAppFirewall"
)
Remove-AzSqlServerFirewallRule -ServerName $ServerName -FirewallRuleName $FirewallRuleName -ResourceGroupName $ResourceGroupName

经典

[CmdletBinding(DefaultParameterSetName = 'None')]
param
(
  [String] [Parameter(Mandatory = $true)] $ServerName,
  [String] [Parameter(Mandatory = $true)] $ResourceGroupName,
  [String] $FirewallRuleName = "AzureWebAppFirewall"
)

$ErrorActionPreference = 'Stop'

if ((Get-AzureSqlDatabaseServerFirewallRule -ServerName $ServerName -RuleName $FirewallRuleName -ErrorAction SilentlyContinue))
{
  Remove-AzureSqlDatabaseServerFirewallRule -RuleName $FirewallRuleName -ServerName $ServerName
}

将以下内容添加到 azure-pipelines.yml 文件以运行 SQL 脚本。

variables:
  AzureSubscription: '<SERVICE_CONNECTION_NAME>'
  ResourceGroupName: '<RESOURCE_GROUP_NAME>'
  ServerName: '<DATABASE_SERVER_NAME>'
  ServerFqdn: '<DATABASE_FQDN>'
  DatabaseName: '<DATABASE_NAME>'
  AdminUser: '<DATABASE_USERNAME>'
  AdminPassword: '<DATABASE_PASSWORD>'
  SQLFile: '<LOCATION_OF_SQL_FILE_IN_$(Build.SourcesDirectory)>'

steps:
- task: AzurePowerShell@5
  displayName: 'Azure PowerShell script'
  inputs:
    azureSubscription: '$(AzureSubscription)'
    ScriptType: filePath
    ScriptPath: '$(Build.SourcesDirectory)\scripts\SetAzureFirewallRule.ps1'
    ScriptArguments: '-ServerName $(ServerName) -ResourceGroupName $(ResourceGroupName)'
    azurePowerShellVersion: LatestVersion

- task: CmdLine@2
  displayName: Run Sqlcmd
  inputs:
    filename: sqlcmd
    arguments: '-S $(ServerFqdn) -U $(AdminUser) -P $(AdminPassword) -d $(DatabaseName) -i $(SQLFile)'

- task: AzurePowerShell@5
  displayName: 'Azure PowerShell script'
  inputs:
    azureSubscription: '$(AzureSubscription)'
    ScriptType: filePath
    ScriptPath: '$(Build.SourcesDirectory)\scripts\RemoveAzureFirewallRule.ps1'
    ScriptArguments: '-ServerName $(ServerName) -ResourceGroupName $(ResourceGroupName)'
    azurePowerShellVersion: LatestVersion

Azure 服务连接

Azure SQL 数据库部署任务是将数据库部署到 Azure 的主要机制。 与其他内置 Azure 任务一样,此任务需要 Azure 服务连接作为输入。 Azure 服务连接将存储用于从 Azure Pipelines 连接到 Azure 的凭据。

若要开始使用此任务,最简单的方法是以同时拥有 Azure DevOps 组织和 Azure 订阅的用户的身份登录。 在这种情况下,不必手动创建服务连接。 否则,若要了解如何创建 Azure 服务连接,请参阅创建 Azure 服务连接

若要了解如何创建 Azure 服务连接,请参阅创建 Azure 服务连接

按条件部署

可以选择仅将某些生成部署到 Azure 数据库。

若要在 YAML 中执行此操作,可使用以下方法之一:

  • 将部署步骤隔离到单独的作业中,并向该作业添加条件。
  • 向步骤添加条件。

以下示例演示如何使用步骤条件来仅部署源自主分支的生成。

- task: SqlAzureDacpacDeployment@1
  condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
  inputs:
    azureSubscription: '<Azure service connection>'
    ServerName: '<Database server name>'
    DatabaseName: '<Database name>'
    SqlUsername: '<SQL user name>'
    SqlPassword: '<SQL user password>'
    DacpacFile: '<Location of Dacpac file in $(Build.SourcesDirectory) after compilation>'

若要详细了解条件,请参阅指定条件

更多 SQL 操作

SQL Azure Dacpac 部署不一定能够支持你要执行的所有 SQL Server 操作。 在这些情况下,只需使用 PowerShell 或命令行脚本来运行所需的命令。 本部分介绍调用 SqlPackage.exe 工具的一些常见用例。 作为运行此工具的先决条件,必须使用自承载代理并在代理上安装该工具。

注意

如果从安装 SQLPackage 的文件夹执行 SQLPackage,则必须在路径前面加上 & 前缀,并将其用双引号括起来。

基本语法

<Path of SQLPackage.exe> <Arguments to SQLPackage.exe>

可以使用以下任一 SQL 脚本,具体取决于要执行的操作

提取

从活动的 SQL Server 或 Microsoft Azure SQL 数据库创建数据库快照 (.dacpac) 文件。

命令语法:

SqlPackage.exe /TargetFile:"<Target location of dacpac file>" /Action:Extract
/SourceServerName:"<ServerName>.database.windows.net"
/SourceDatabaseName:"<DatabaseName>" /SourceUser:"<Username>" /SourcePassword:"<Password>"

SqlPackage.exe /action:Extract /tf:"<Target location of dacpac file>"
/SourceConnectionString:"Data Source=ServerName;Initial Catalog=DatabaseName;Integrated Security=SSPI;Persist Security Info=False;"

示例:

SqlPackage.exe /TargetFile:"C:\temp\test.dacpac" /Action:Extract /SourceServerName:"DemoSqlServer.database.windows.net"
 /SourceDatabaseName:"Testdb" /SourceUser:"ajay" /SourcePassword:"SQLPassword"

帮助:

sqlpackage.exe /Action:Extract /?

发布

增量更新数据库架构以便匹配源 .dacpac 文件的架构。 如果数据库不存在于服务器上,则发布操作将创建该数据库。 否则,将更新现有数据库。

命令语法:

SqlPackage.exe /SourceFile:"<Dacpac file location>" /Action:Publish /TargetServerName:"<ServerName>.database.windows.net"
/TargetDatabaseName:"<DatabaseName>" /TargetUser:"<Username>" /TargetPassword:"<Password> "

示例:

SqlPackage.exe /SourceFile:"E:\dacpac\ajyadb.dacpac" /Action:Publish /TargetServerName:"DemoSqlServer.database.windows.net"
/TargetDatabaseName:"Testdb4" /TargetUser:"ajay" /TargetPassword:"SQLPassword"

帮助:

sqlpackage.exe /Action:Publish /?

导出

将活动数据库(包括数据库架构和用户数据)从 SQL Server 或 Microsoft Azure SQL 数据库导出到 BACPAC 包(.bacpac 文件)。

命令语法:

SqlPackage.exe /TargetFile:"<Target location for bacpac file>" /Action:Export /SourceServerName:"<ServerName>.database.windows.net"
/SourceDatabaseName:"<DatabaseName>" /SourceUser:"<Username>" /SourcePassword:"<Password>"

示例:

SqlPackage.exe /TargetFile:"C:\temp\test.bacpac" /Action:Export /SourceServerName:"DemoSqlServer.database.windows.net"
/SourceDatabaseName:"Testdb" /SourceUser:"ajay" /SourcePassword:"SQLPassword"

帮助:

sqlpackage.exe /Action:Export /?

导入

将架构和表数据从 BACPAC 包导入到 SQL Server 或 Microsoft Azure SQL 数据库的实例中的新用户数据库中。

命令语法:

SqlPackage.exe /SourceFile:"<Bacpac file location>" /Action:Import /TargetServerName:"<ServerName>.database.windows.net"
/TargetDatabaseName:"<DatabaseName>" /TargetUser:"<Username>" /TargetPassword:"<Password>"

示例:

SqlPackage.exe /SourceFile:"C:\temp\test.bacpac" /Action:Import /TargetServerName:"DemoSqlServer.database.windows.net"
/TargetDatabaseName:"Testdb" /TargetUser:"ajay" /TargetPassword:"SQLPassword"

帮助:

sqlpackage.exe /Action:Import /?

DeployReport

创建将由发布操作完成的更改的 XML 报表。

命令语法:

SqlPackage.exe /SourceFile:"<Dacpac file location>" /Action:DeployReport /TargetServerName:"<ServerName>.database.windows.net"
/TargetDatabaseName:"<DatabaseName>" /TargetUser:"<Username>" /TargetPassword:"<Password>" /OutputPath:"<Output XML file path for deploy report>"

示例:

SqlPackage.exe /SourceFile:"E: \dacpac\ajyadb.dacpac" /Action:DeployReport /TargetServerName:"DemoSqlServer.database.windows.net"
/TargetDatabaseName:"Testdb" /TargetUser:"ajay" /TargetPassword:"SQLPassword" /OutputPath:"C:\temp\deployReport.xml" 

帮助:

sqlpackage.exe /Action:DeployReport /?

DriftReport

创建自注册数据库注册以来已对其做出的更改的 XML 报表。

命令语法:

SqlPackage.exe /Action:DriftReport /TargetServerName:"<ServerName>.database.windows.net" /TargetDatabaseName:"<DatabaseName>"
/TargetUser:"<Username>" /TargetPassword:"<Password>" /OutputPath:"<Output XML file path for drift report>"

示例:

SqlPackage.exe /Action:DriftReport /TargetServerName:"DemoSqlServer.database.windows.net" /TargetDatabaseName:"Testdb"
/TargetUser:"ajay" /TargetPassword:"SQLPassword" /OutputPath:"C:\temp\driftReport.xml"

帮助:

sqlpackage.exe /Action:DriftReport /?

脚本

创建 Transact-SQL 增量更新脚本,该脚本可更新目标的架构以匹配源的架构。

命令语法:

SqlPackage.exe /SourceFile:"<Dacpac file location>" /Action:Script /TargetServerName:"<ServerName>.database.windows.net"
/TargetDatabaseName:"<DatabaseName>" /TargetUser:"<Username>" /TargetPassword:"<Password>" /OutputPath:"<Output SQL script file path>"

示例:

SqlPackage.exe /Action:Script /SourceFile:"E:\dacpac\ajyadb.dacpac" /TargetServerName:"DemoSqlServer.database.windows.net"
/TargetDatabaseName:"Testdb" /TargetUser:"ajay" /TargetPassword:"SQLPassword" /OutputPath:"C:\temp\test.sql"
/Variables:StagingDatabase="Staging DB Variable value"

帮助:

sqlpackage.exe /Action:Script /?