Redgate - Create Database Release

Octopus.Script exported 2015-07-17 by support@red-gate.com belongs to ‘Redgate’ category.

Creates the resources (including the SQL update script) to deploy database changes using Redgate’s SQL Change Automation, and exports them as Octopus artifacts so you can review the changes before deploying.

Requires SQL Change Automation version 3.0.2 or later.

Version date: 2020-12-21

Parameters

When steps based on the template are included in a project’s deployment process, the parameters below can be set.

Export path

DLMAutomationDeploymentResourcesPath

The path that the database deployment resources will be exported to.

This path is used in the “Redgate - Deploy from Database Release” step, and must be accessible to all tentacles used in database deployment steps.

Delete files in export folder

DLMAutomationDeleteExistingFiles = True

If the folder that the deployment resources are exported to isn’t empty, this step will fail. Select this option to delete any files in the folder.

Database package step

DLMAutomationNuGetDbPackageDownloadStepName

Select the step in this project which downloads the database package.

Target SQL Server instance

DLMAutomationDatabaseServer

The fully qualified SQL Server instance name for the target database.

Target database name

DLMAutomationDatabaseName

The name of the database that the source schema (the database package) will be compared with to generate the deployment resources. This must be an existing database.

Username (optional)

DLMAutomationDatabaseUsername

The SQL Server username used to connect to the database. If you leave this field and ‘Password’ blank, Windows authentication will be used to connect instead, using the account that runs the Tentacle service.

Password (optional)

DLMAutomationDatabasePassword

You must enter a password if you entered a username.

Encrypt

DLMAutomationEncrypt = false

Specify whether SSL encryption is used by SQL Server when a certificate is installed.

Trust Server Certificate

DLMAutomationTrustServerCertificate = false

Specify whether to force SQL Server to skip certificate validation.

Filter path (optional)

DLMAutomationFilterPath

Specify the location of a SQL Compare filter file (.scpf), which defines objects to include/exclude in the schema comparison. Filter files are generated by SQL Source Control.

For more help see Using SQL Compare filters in SQL Change Automation.

SQL Compare options (optional)

DLMAutomationCompareOptions

Enter SQL Compare options to apply when generating the update script. Use a comma-separated list to enter multiple values. For more help see Using SQL Compare options in SQL Change Automation.

SQL Data Compare options (optional)

DLMAutomationDataCompareOptions

Enter SQL Data Compare options to apply when generating the update script. Use a comma-separated list to enter multiple values. For more help see Using SQL Data Compare options in SQL Change Automation.

Transaction isolation level (optional)

DLMAutomationTransactionIsolationLevel = Serializable

Select the transaction isolation level to be used in deployment scripts.

Ignore static data

DLMAutomationIgnoreStaticData

Exclude changes to static data when generating the deployment resources.

Include identical objects in the change report

DLMAutomationIncludeIdenticalsInReport = False

By default, the change report only includes added, modified and removed objects. Choose this option to also include identical objects.

SQL Change Automation version (optional)

SpecificModuleVersion

If you wish to use a specific version of SQL Change Automation rather than the latest, enter the version number here.

Only use a version of SQL Change Automation that is already installed

UseInstalledModuleVersion = False

This prevents attempting to access PowerShell Gallery, which can be helpful when the build agent does not have access to the internet

Module Installation Folder (optional)

DLMAutomationModuleInstallationFolder

By default, module folders do not persist between steps. Setting this field to a specific folder will ensure that modules persist, and do not have to be downloaded again.

Proxy URL (optional)

DLMAutomationProxyUrl

By default, no proxy is used when connecting to Powershell Gallery. Alternatively, a proxy URL can be specified here that can be used for Powershell Gallery.

Script body

Steps based on this template will execute the following PowerShell script.

function GetModuleInstallationFolder
{
    if (ModuleInstallationFolderIsValid)
    {
        return [System.IO.Path]::GetFullPath($DLMAutomationModuleInstallationFolder)
    }

    return "$PSScriptRoot\Modules"
}

function ModuleInstallationFolderIsValid
{
    if ([string]::IsNullOrWhiteSpace($DLMAutomationModuleInstallationFolder))
    {
        return $false
    }

    return (Test-Path $DLMAutomationModuleInstallationFolder -IsValid) -eq $true;
}

$DlmAutomationModuleName = "DLMAutomation"
$SqlChangeAutomationModuleName = "SqlChangeAutomation"
$ModulesFolder = GetModuleInstallationFolder
$LocalModules = (New-Item "$ModulesFolder" -ItemType Directory -Force).FullName
$env:PSModulePath = "$LocalModules;$env:PSModulePath"

function IsScaAvailable
{
    if ((Get-Module $SqlChangeAutomationModuleName) -ne $null) {
        return $true
    }

    return $false
}

function InstallCorrectSqlChangeAutomation
{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $false)]
        [Version]$requiredVersion,
        [Parameter(Mandatory = $false)]
        [bool]$useInstalledVersion
    )

    $moduleName = $SqlChangeAutomationModuleName

    # this will be null if $requiredVersion is not specified - which is exactly what we want
    $maximumVersion = $requiredVersion

    if ($requiredVersion) {
        if ($requiredVersion.Revision -eq -1) {
            #If provided with a 3 part version number (the 4th part, revision, == -1), we should allow any value for the revision
            $maximumVersion = [Version]"$requiredVersion.$([System.Int32]::MaxValue)"
        }

        if ($requiredVersion.Major -lt 3) {
            # If the specified version is below V3 then the user is requesting a version of DLMA. We should look for that module name instead
            $moduleName = $DlmAutomationModuleName
        }
    }

    if ($useInstalledVersion) {
        Write-Verbose "Option to use installed version is selected. Skipping update/install using PowerShellGet."
    }
    else {
        $installedModule = GetHighestInstalledModule $moduleName -minimumVersion $requiredVersion -maximumVersion $maximumVersion

        if (!$installedModule) {
            #Either SCA isn't installed at all or $requiredVersion is specified but that version of SCA isn't installed
            Write-Verbose "$moduleName $requiredVersion not available - attempting to download from gallery"
            InstallLocalModule -moduleName $moduleName -minimumVersion $requiredVersion -maximumVersion $maximumVersion
        }
        elseif (!$requiredVersion) {
            #We've got a version of SCA installed, but $requiredVersion isn't specified so we might be able to upgrade
            $newest = GetHighestInstallableModule $moduleName
            if ($newest -and ($installedModule.Version -lt $newest.Version)) {
                Write-Verbose "Updating $moduleName to version $($newest.Version)"
                InstallLocalModule -moduleName $moduleName -minimumVersion $newest.Version
            }
        }
    }

    # Now we're done with install/upgrade, try to import the highest available module that matches our version requirements

    # We can't just use -minimumVersion and -maximumVersion arguments on Import-Module because PowerShell 3 doesn't have them,
    # so we have to find the precise matching installed version using our code, then import that specifically. Note that
    # $requiredVersion and $maximumVersion might be null when there's no specific version we need.
    $installedModule = GetHighestInstalledModule $moduleName -minimumVersion $requiredVersion -maximumVersion $maximumVersion

    if (!$installedModule -and !$requiredVersion) {
        #Did not find SCA, and we don't have a required version so we might be able to use an installed DLMA instead.
        Write-Verbose "$moduleName is not installed - trying to fall back to $DlmAutomationModuleName"
        $installedModule = GetHighestInstalledModule $DlmAutomationModuleName
    }

    if ($installedModule) {
        Write-Verbose "Importing installed $($installedModule.Name) version $($installedModule.Version)"
        Import-Module $installedModule -Force
    }
    else {
        throw "$moduleName $requiredVersion is not installed, and could not be downloaded from the PowerShell gallery"
    }
}

function InstallPowerShellGet {
    [CmdletBinding()]
    Param()

    ConfigureProxyIfVariableSet
    [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12

    $psget = GetHighestInstalledModule PowerShellGet
    if (!$psget)
    {
        Write-Warning @"
Cannot access the PowerShell Gallery because PowerShellGet is not installed.
To install PowerShellGet, either upgrade to PowerShell 5 or install the PackageManagement MSI.
See https://docs.microsoft.com/en-us/powershell/gallery/installing-psget for more details.
"@
        throw "PowerShellGet is not available"
    }

    if ($psget.Version -lt [Version]'1.6') {
        #Bootstrap the NuGet package provider, which updates NuGet without requiring admin rights
        Write-Debug "Installing NuGet package provider"
        Get-PackageProvider NuGet -ForceBootstrap | Out-Null

        #Use the currently-installed version of PowerShellGet
        Import-PackageProvider PowerShellGet

        #Download the version of PowerShellGet that we actually need
        Write-Debug "Installing PowershellGet"
        Save-Module -Name PowerShellGet -Path $LocalModules -MinimumVersion 1.6 -Force -ErrorAction SilentlyContinue
    }

    Write-Debug "Importing PowershellGet"
    Import-Module PowerShellGet -MinimumVersion 1.6 -Force
    #Make sure we're actually using the package provider from the imported version of PowerShellGet
    Import-PackageProvider ((Get-Module PowerShellGet).Path) | Out-Null
}

function InstallLocalModule {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true)]
        [string]$moduleName,
        [Parameter(Mandatory = $false)]
        [Version]$minimumVersion,
        [Parameter(Mandatory = $false)]
        [Version]$maximumVersion
    )
    try {
        InstallPowerShellGet

        Write-Debug "Install $moduleName $requiredVersion"
        Save-Module -Name $moduleName -Path $LocalModules -Force -AcceptLicense -MinimumVersion $minimumVersion -MaximumVersion $maximumVersion -ErrorAction Stop
    }
    catch {
        Write-Warning "Could not install $moduleName $requiredVersion from any registered PSRepository"
    }
}

function GetHighestInstalledModule {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true, Position = 0)]
        [string] $moduleName,

        [Parameter(Mandatory = $false)]
        [Version]$minimumVersion,
        [Parameter(Mandatory = $false)]
        [Version]$maximumVersion
    )

    return Get-Module $moduleName -ListAvailable |
           Where {(!$minimumVersion -or ($_.Version -ge $minimumVersion)) -and (!$maximumVersion -or ($_.Version -le $maximumVersion))} |
           Sort -Property @{Expression = {[System.Version]($_.Version)}; Descending = $True} |
           Select -First 1
}

function GetHighestInstallableModule {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true, Position = 0)]
        [string] $moduleName
    )

    try {
        InstallPowerShellGet
        Find-Module SqlChangeAutomation -AllVersions |
            Sort -Property @{Expression = {[System.Version]($_.Version)}; Descending = $True} |
            Select -First 1
    }
    catch {
        Write-Warning "Could not find any suitable versions of $moduleName from any registered PSRepository"
    }
}

function GetInstalledSqlChangeAutomationVersion {
    $scaModule = (Get-Module $SqlChangeAutomationModuleName)

    if ($scaModule -ne $null) {
        return $scaModule.Version
    }

    $dlmaModule = (Get-Module $DlmAutomationModuleName)

    if ($dlmaModule -ne $null) {
        return $dlmaModule.Version
    }

    return $null
}

function ConfigureProxyIfVariableSet
{
    if ([string]::IsNullOrWhiteSpace($DLMAutomationProxyUrl) -eq $false)
    {
        Write-Debug "Setting DefaultWebProxy to $proxyUrl"

        [System.Net.WebRequest]::DefaultWebProxy = New-Object System.Net.WebProxy($DLMAutomationProxyUrl)
        [System.Net.WebRequest]::DefaultWebProxy.credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
        [System.Net.WebRequest]::DefaultWebProxy.BypassProxyOnLocal = $True
    }
}


$ErrorActionPreference = 'Stop'
$VerbosePreference = 'Continue'

# Set process level FUR environment
$env:REDGATE_FUR_ENVIRONMENT = "Octopus Step Templates"

#Helper functions for paramter handling
function Required() {
    Param(
        [Parameter(Mandatory = $false)][string]$Parameter,
        [Parameter(Mandatory = $true)][string]$Name
    )
    if ([string]::IsNullOrWhiteSpace($Parameter)) { throw "You must enter a value for '$Name'" }
}
function Optional() {
    #Default is untyped here - if we specify [string] powershell will convert nulls into empty string
    Param(
        [Parameter(Mandatory = $false)][string]$Parameter,
        [Parameter(Mandatory = $false)]$Default
    )
    if ([string]::IsNullOrWhiteSpace($Parameter)) {
        $Default
    } else {
        $Parameter
    }
}
function RequireBool() {
    Param(
        [Parameter(Mandatory = $false)][string]$Parameter,
        [Parameter(Mandatory = $true)][string]$Name
    )
    $Result = $False
    if (![bool]::TryParse($Parameter , [ref]$Result )) { throw "'$Name' must be a boolean value." }
    $Result
}
function RequirePositiveNumber() {
    Param(
        [Parameter(Mandatory = $false)][string]$Parameter,
        [Parameter(Mandatory = $true)][string]$Name
    )
    $Result = 0
    if (![int32]::TryParse($Parameter , [ref]$Result )) { throw "'$Name' must be a numerical value." }
    if ($Result -lt 0) { throw "'$Name' must be >= 0." }
    $Result
}

$SpecificModuleVersion = Optional -Parameter $SpecificModuleVersion
$UseInstalledModuleVersion = Optional -Parameter $UseInstalledModuleVersion -Default 'False'
$UseInstalledVersionSwitch = [bool]::Parse($UseInstalledModuleVersion)
InstallCorrectSqlChangeAutomation -requiredVersion $SpecificModuleVersion -useInstalledVersion $UseInstalledVersionSwitch

# Check if SQL Change Automation is installed.
$powershellModule = Get-Module -Name SqlChangeAutomation
if ($powershellModule -eq $null) {
    throw "Cannot find SQL Change Automation on your Octopus Tentacle. If SQL Change Automation is installed, try restarting the Tentacle service for it to be detected."
}

$currentVersion = $powershellModule.Version
$minimumRequiredVersion = [version] '3.0.3'
if ($currentVersion -lt $minimumRequiredVersion) {
    throw "This step requires SQL Change Automation version $minimumRequiredVersion or later. The current version is $currentVersion. The latest version can be found at http://www.red-gate.com/sca/productpage"
}

$minimumRequiredVersionDataCompareOptions = [version] '3.3.0'
$minimumRequiredVersionTrustServerCertificate = [version]'4.3.20267'

function AreConnectionOptionsHandled($encryptConnection, $trustServerCertificate)
{
    if ([string]::IsNullOrWhiteSpace($currentVersion) -or $currentVersion -ge $minimumRequiredVersionTrustServerCertificate)
    {
        return $true
    }
    elseif($encryptConnection -or $trustServerCertificate)
    {
        Write-Warning "Encrypt and TrustServerCertificate options require SQL Change Automation version $minimumRequiredVersionTrustServerCertificate or later. The current version is $currentVersion."
        return $false
    }
}

# Check the parameters.
Required -Parameter $DLMAutomationDeploymentResourcesPath -Name 'Export Path'
Required -Parameter $DLMAutomationDeleteExistingFiles -Name 'Delete files in export folder'
Required -Parameter $DLMAutomationNuGetDbPackageDownloadStepName -Name 'Database package step'
Required -Parameter $DLMAutomationDatabaseServer -Name 'Target SQL Server instance'
Required -Parameter $DLMAutomationDatabaseName -Name 'Target database name'
$DLMAutomationDatabaseUsername = Optional -Parameter $DLMAutomationDatabaseUsername
$DLMAutomationDatabasePassword = Optional -Parameter $DLMAutomationDatabasePassword
$DLMAutomationEncrypt = Optional -Parameter $DLMAutomationEncrypt
$DLMAutomationTrustServerCertificate = Optional -Parameter $DLMAutomationTrustServerCertificate
$DLMAutomationFilterPath = Optional -Parameter $DLMAutomationFilterPath
$DLMAutomationCompareOptions = Optional -Parameter $DLMAutomationCompareOptions
$DLMAutomationDataCompareOptions = Optional -Parameter $DLMAutomationDataCompareOptions
$DLMAutomationTransactionIsolationLevel = Optional -Parameter $DLMAutomationTransactionIsolationLevel -Default 'Serializable'
$DLMAutomationIgnoreStaticData = Optional -Parameter $DLMAutomationIgnoreStaticData -Default 'False'
$DLMAutomationIncludeIdenticalsInReport = Optional -Parameter $DLMAutomationIncludeIdenticalsInReport -Default 'False'
$DLMAutomationModuleInstallationFolder = Optional -Parameter $DLMAutomationModuleInstallationFolder
$DLMAutomationProxyUrl = Optional -Parameter $DLMAutomationProxyUrl

# Get the NuGet package installation directory path.
$packagePath = $OctopusParameters["Octopus.Action[$DLMAutomationNuGetDbPackageDownloadStepName].Output.Package.InstallationDirectoryPath"]
if($packagePath -eq $null) {
    throw "The 'Database package download step' is not a 'Deploy a NuGet package' step: '$DLMAutomationNuGetDbPackageDownloadStepName'"
}

# Constructing the unique export path.
$projectId = $OctopusParameters["Octopus.Project.Id"]
$releaseNumber = $OctopusParameters["Octopus.Release.Number"]
$nugetPackageId = $OctopusParameters["Octopus.Action[$DLMAutomationNuGetDbPackageDownloadStepName].Package.NuGetPackageId"]
$exportPath = Join-Path (Join-Path (Join-Path $DLMAutomationDeploymentResourcesPath $projectId) $releaseNumber) $nugetPackageId

# Make sure the directory we're about to create doesn't already exist, and delete any files if requested.
if ((Test-Path $exportPath) -AND ((Get-ChildItem $exportPath | Measure-Object).Count -ne 0)) {
    if ($DLMAutomationDeleteExistingFiles -eq 'True') {
        Write-Host "Deleting all files in $exportPath"
        rmdir $exportPath -Recurse -Force
    } else {
        throw "The export path is not empty: $exportPath.  Select the 'Delete files in export folder' option to overwrite the existing folder contents."
    }
}

$connectionOptions = @{ }

if(AreConnectionOptionsHandled([bool]::Parse($DLMAutomationEncrypt), [bool]::Parse($DLMAutomationTrustServerCertificate))) {
    $connectionOptions += @{ 'Encrypt' = [bool]::Parse($DLMAutomationEncrypt) }
    $connectionOptions += @{ 'TrustServerCertificate' = [bool]::Parse($DLMAutomationTrustServerCertificate) }
}

# Determine whether or not to include identical objects in the report.
$targetDB = New-DatabaseConnection @connectionOptions `
                                   -ServerInstance $DLMAutomationDatabaseServer `
                                   -Database $DLMAutomationDatabaseName `
                                   -Username $DLMAutomationDatabaseUsername `
                                   -Password $DLMAutomationDatabasePassword | Test-DatabaseConnection

$importedBuildArtifact = Import-DatabaseBuildArtifact -Path $packagePath

# Only allow sqlcmd variables that don't have special characters like spaces, colon or dashes
$regex = '^[a-zA-Z_][a-zA-Z0-9_]+$'
$sqlCmdVariables = @{}
$OctopusParameters.Keys | Where { $_ -match $regex } | ForEach {
	$sqlCmdVariables[$_] = $OctopusParameters[$_]
}

# Create the deployment resources from the database to the NuGet package
$releaseParams = @{
    Target = $targetDB
    Source = $importedBuildArtifact
    TransactionIsolationLevel = $DLMAutomationTransactionIsolationLevel
    IgnoreStaticData = [bool]::Parse($DLMAutomationIgnoreStaticData)
    FilterPath = $DLMAutomationFilterPath
    SQLCompareOptions = $DLMAutomationCompareOptions
    IncludeIdenticalsInReport = [bool]::Parse($DLMAutomationIncludeIdenticalsInReport)
    SqlCmdVariables = $sqlCmdVariables
}

if($currentVersion -ge $minimumRequiredVersionDataCompareOptions) {
    $releaseParams.SQLDataCompareOptions = $DLMAutomationDataCompareOptions
} elseif(-not [string]::IsNullOrWhiteSpace($DLMAutomationDataCompareOptions)) {
    Write-Warning "SQL Data Compare options requires SQL Change Automation version $minimumRequiredVersionDataCompareOptions or later. The current version is $currentVersion."
}

$release = New-DatabaseReleaseArtifact @releaseParams

# Export the deployment resources to disk
$release | Export-DatabaseReleaseArtifact -Path $exportPath

# Import the changes summary, deployment warnings, and update script as Octopus artifacts, so you can review them.
function UploadIfExists() {
    Param(
        [Parameter(Mandatory = $true)]
        [string]$ArtifactPath,
        [Parameter(Mandatory = $true)]
        [string]$Name
    )
    if (Test-Path $ArtifactPath) {
        New-OctopusArtifact $ArtifactPath -Name $Name
    }
}

UploadIfExists -ArtifactPath "$exportPath\Reports\Changes.html" -Name "Changes-$DLMAutomationDatabaseName.html"
UploadIfExists -ArtifactPath "$exportPath\Reports\Drift.html" -Name "Drift-$DLMAutomationDatabaseName.html"
UploadIfExists -ArtifactPath "$exportPath\Reports\Warnings.xml" -Name "Warnings-$DLMAutomationDatabaseName.xml"
UploadIfExists -ArtifactPath "$exportPath\Update.sql" -Name "Update-$DLMAutomationDatabaseName.sql"
UploadIfExists -ArtifactPath "$exportPath\TargetedDeploymentScript.sql" -Name "TargetedDeploymentScript-$DLMAutomationDatabaseName.sql"
UploadIfExists -ArtifactPath "$exportPath\DriftRevertScript.sql" -Name "DriftRevertScript-$DLMAutomationDatabaseName.sql"

# Sets a variable if there are changes to deploy. Useful if you want to have steps run only when this is set
if ($release.UpdateSQL -notlike '*This script is empty because the Target and Source schemas are equivalent*')
{
  Set-OctopusVariable -name "ChangesToDeploy" -value "True"
}

Provided under the Apache License version 2.0.

Report an issue

To use this template in Octopus Deploy, copy the JSON below and paste it into the Library → Step templates → Import dialog.

{
  "Id": "c20b70dc-69aa-42a1-85db-6d37341b63e3",
  "Name": "Redgate - Create Database Release",
  "Description": "Creates the resources (including the SQL update script) to deploy database changes using Redgate's [SQL Change Automation](http://www.red-gate.com/sca/productpage), and exports them as Octopus artifacts so you can review the changes before deploying.\r\n\r\nRequires SQL Change Automation version 3.0.2 or later.\r\n\r\n*Version date: 2020-12-21*",
  "Version": 22,
  "ExportedAt": "2015-07-17T11:04:21.348Z",
  "ActionType": "Octopus.Script",
  "Author": "support@red-gate.com",
  "Parameters": [
    {
      "Name": "DLMAutomationDeploymentResourcesPath",
      "Label": "Export path",
      "HelpText": "The path that the database deployment resources will be exported to.\n\nThis path is used in the \"Redgate - Deploy from Database Release\" step, and must be accessible to all tentacles used in database deployment steps.",
      "DefaultValue": null,
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Name": "DLMAutomationDeleteExistingFiles",
      "Label": "Delete files in export folder",
      "HelpText": "If the folder that the deployment resources are exported to isn't empty, this step will fail. Select this option to delete any files in the folder.",
      "DefaultValue": "True",
      "DisplaySettings": {
        "Octopus.ControlType": "Checkbox"
      }
    },
    {
      "Name": "DLMAutomationNuGetDbPackageDownloadStepName",
      "Label": "Database package step",
      "HelpText": "Select the step in this project which downloads the database package.",
      "DefaultValue": null,
      "DisplaySettings": {
        "Octopus.ControlType": "StepName"
      }
    },
    {
      "Name": "DLMAutomationDatabaseServer",
      "Label": "Target SQL Server instance",
      "HelpText": "The fully qualified SQL Server instance name for the target database.",
      "DefaultValue": null,
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Name": "DLMAutomationDatabaseName",
      "Label": "Target database name",
      "HelpText": "The name of the database that the source schema (the database package) will be compared with to generate the deployment resources. This must be an existing database.",
      "DefaultValue": null,
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Name": "DLMAutomationDatabaseUsername",
      "Label": "Username (optional)",
      "HelpText": "The SQL Server username used to connect to the database. If you leave this field and 'Password' blank, Windows authentication will be used to connect instead, using the account that runs the Tentacle service.",
      "DefaultValue": null,
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Name": "DLMAutomationDatabasePassword",
      "Label": "Password (optional)",
      "HelpText": "You must enter a password if you entered a username.",
      "DefaultValue": null,
      "DisplaySettings": {
        "Octopus.ControlType": "Sensitive"
      }
    },
    {
      "Name": "DLMAutomationEncrypt",
      "Label": "Encrypt",
      "HelpText": "Specify whether SSL encryption is used by SQL Server when a certificate is installed.",
      "DefaultValue": "false",
      "DisplaySettings": {
        "Octopus.ControlType": "Checkbox"
      }
    },
    {
      "Name": "DLMAutomationTrustServerCertificate",
      "Label": "Trust Server Certificate",
      "HelpText": "Specify whether to force SQL Server to skip certificate validation.",
      "DefaultValue": "false",
      "DisplaySettings": {
        "Octopus.ControlType": "Checkbox"
      }
    },
    {
      "Name": "DLMAutomationFilterPath",
      "Label": "Filter path (optional)",
      "HelpText": "Specify the location of a SQL Compare filter file (.scpf), which defines objects to include/exclude in the schema comparison. Filter files are generated by SQL Source Control.\n\nFor more help see [Using SQL Compare filters in SQL Change Automation](http://www.red-gate.com/sca/ps/help/filters).",
      "DefaultValue": null,
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Name": "DLMAutomationCompareOptions",
      "Label": "SQL Compare options (optional)",
      "HelpText": "Enter SQL Compare options to apply when generating the update script. Use a comma-separated list to enter multiple values. For more help see [Using SQL Compare options in SQL Change Automation](http://www.red-gate.com/sca/add-ons/compare-options).",
      "DefaultValue": null,
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Name": "DLMAutomationDataCompareOptions",
      "Label": "SQL Data Compare options (optional)",
      "HelpText": "Enter SQL Data Compare options to apply when generating the update script. Use a comma-separated list to enter multiple values. For more help see [Using SQL Data Compare options in SQL Change Automation](http://www.red-gate.com/sca/ps/help/datacompareoptions).",
      "DefaultValue": null,
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Name": "DLMAutomationTransactionIsolationLevel",
      "Label": "Transaction isolation level (optional)",
      "HelpText": "Select the transaction isolation level to be used in deployment scripts.",
      "DefaultValue": "Serializable",
      "DisplaySettings": {
        "Octopus.ControlType": "Select",
        "Octopus.SelectOptions": "Serializable\nSnapshot\nRepeatableRead\nReadCommitted\nReadUncommitted"
      }
    },
    {
      "Name": "DLMAutomationIgnoreStaticData",
      "Label": "Ignore static data",
      "HelpText": "Exclude changes to static data when generating the deployment resources.",
      "DefaultValue": null,
      "DisplaySettings": {
        "Octopus.ControlType": "Checkbox"
      }
    },
    {
      "Name": "DLMAutomationIncludeIdenticalsInReport",
      "Label": "Include identical objects in the change report",
      "HelpText": "By default, the change report only includes added, modified and removed objects. Choose this option to also include identical objects.",
      "DefaultValue": "False",
      "DisplaySettings": {
        "Octopus.ControlType": "Checkbox"
      }
    },
    {
      "Name": "SpecificModuleVersion",
      "Label": "SQL Change Automation version (optional)",
      "HelpText": "If you wish to use a specific version of SQL Change Automation rather than the latest, enter the version number here.",
      "DefaultValue": null,
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Name": "UseInstalledModuleVersion",
      "Label": "Only use a version of SQL Change Automation that is already installed",
      "HelpText": "This prevents attempting to access PowerShell Gallery, which can be helpful when the build agent does not have access to the internet",
      "DefaultValue": "False",
      "DisplaySettings": {
        "Octopus.ControlType": "Checkbox"
      }
    },
    {
      "Name": "DLMAutomationModuleInstallationFolder",
      "Label": "Module Installation Folder (optional)",
      "HelpText": "By default, module folders do not persist between steps. Setting this field to a specific folder will ensure that modules persist, and do not have to be downloaded again.",
      "DefaultValue": null,
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Name": "DLMAutomationProxyUrl",
      "Label": "Proxy URL (optional)",
      "HelpText": "By default, no proxy is used when connecting to Powershell Gallery. Alternatively, a proxy URL can be specified here that can be used for Powershell Gallery.",
      "DefaultValue": null,
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    }
  ],
  "Properties": {
    "Octopus.Action.Script.ScriptBody": "function GetModuleInstallationFolder\n{\n    if (ModuleInstallationFolderIsValid)\n    {\n        return [System.IO.Path]::GetFullPath($DLMAutomationModuleInstallationFolder)\n    }\n\n    return \"$PSScriptRoot\\Modules\"\n}\n\nfunction ModuleInstallationFolderIsValid\n{\n    if ([string]::IsNullOrWhiteSpace($DLMAutomationModuleInstallationFolder))\n    {\n        return $false\n    }\n\n    return (Test-Path $DLMAutomationModuleInstallationFolder -IsValid) -eq $true;\n}\n\n$DlmAutomationModuleName = \"DLMAutomation\"\n$SqlChangeAutomationModuleName = \"SqlChangeAutomation\"\n$ModulesFolder = GetModuleInstallationFolder\n$LocalModules = (New-Item \"$ModulesFolder\" -ItemType Directory -Force).FullName\n$env:PSModulePath = \"$LocalModules;$env:PSModulePath\"\n\nfunction IsScaAvailable\n{\n    if ((Get-Module $SqlChangeAutomationModuleName) -ne $null) {\n        return $true\n    }\n\n    return $false\n}\n\nfunction InstallCorrectSqlChangeAutomation\n{\n    [CmdletBinding()]\n    Param(\n        [Parameter(Mandatory = $false)]\n        [Version]$requiredVersion,\n        [Parameter(Mandatory = $false)]\n        [bool]$useInstalledVersion\n    )\n\n    $moduleName = $SqlChangeAutomationModuleName\n\n    # this will be null if $requiredVersion is not specified - which is exactly what we want\n    $maximumVersion = $requiredVersion\n\n    if ($requiredVersion) {\n        if ($requiredVersion.Revision -eq -1) {\n            #If provided with a 3 part version number (the 4th part, revision, == -1), we should allow any value for the revision\n            $maximumVersion = [Version]\"$requiredVersion.$([System.Int32]::MaxValue)\"\n        }\n\n        if ($requiredVersion.Major -lt 3) {\n            # If the specified version is below V3 then the user is requesting a version of DLMA. We should look for that module name instead\n            $moduleName = $DlmAutomationModuleName\n        }\n    }\n\n    if ($useInstalledVersion) {\n        Write-Verbose \"Option to use installed version is selected. Skipping update/install using PowerShellGet.\"\n    }\n    else {\n        $installedModule = GetHighestInstalledModule $moduleName -minimumVersion $requiredVersion -maximumVersion $maximumVersion\n\n        if (!$installedModule) {\n            #Either SCA isn't installed at all or $requiredVersion is specified but that version of SCA isn't installed\n            Write-Verbose \"$moduleName $requiredVersion not available - attempting to download from gallery\"\n            InstallLocalModule -moduleName $moduleName -minimumVersion $requiredVersion -maximumVersion $maximumVersion\n        }\n        elseif (!$requiredVersion) {\n            #We've got a version of SCA installed, but $requiredVersion isn't specified so we might be able to upgrade\n            $newest = GetHighestInstallableModule $moduleName\n            if ($newest -and ($installedModule.Version -lt $newest.Version)) {\n                Write-Verbose \"Updating $moduleName to version $($newest.Version)\"\n                InstallLocalModule -moduleName $moduleName -minimumVersion $newest.Version\n            }\n        }\n    }\n\n    # Now we're done with install/upgrade, try to import the highest available module that matches our version requirements\n\n    # We can't just use -minimumVersion and -maximumVersion arguments on Import-Module because PowerShell 3 doesn't have them,\n    # so we have to find the precise matching installed version using our code, then import that specifically. Note that\n    # $requiredVersion and $maximumVersion might be null when there's no specific version we need.\n    $installedModule = GetHighestInstalledModule $moduleName -minimumVersion $requiredVersion -maximumVersion $maximumVersion\n\n    if (!$installedModule -and !$requiredVersion) {\n        #Did not find SCA, and we don't have a required version so we might be able to use an installed DLMA instead.\n        Write-Verbose \"$moduleName is not installed - trying to fall back to $DlmAutomationModuleName\"\n        $installedModule = GetHighestInstalledModule $DlmAutomationModuleName\n    }\n\n    if ($installedModule) {\n        Write-Verbose \"Importing installed $($installedModule.Name) version $($installedModule.Version)\"\n        Import-Module $installedModule -Force\n    }\n    else {\n        throw \"$moduleName $requiredVersion is not installed, and could not be downloaded from the PowerShell gallery\"\n    }\n}\n\nfunction InstallPowerShellGet {\n    [CmdletBinding()]\n    Param()\n\n    ConfigureProxyIfVariableSet\n    [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12\n\n    $psget = GetHighestInstalledModule PowerShellGet\n    if (!$psget)\n    {\n        Write-Warning @\"\nCannot access the PowerShell Gallery because PowerShellGet is not installed.\nTo install PowerShellGet, either upgrade to PowerShell 5 or install the PackageManagement MSI.\nSee https://docs.microsoft.com/en-us/powershell/gallery/installing-psget for more details.\n\"@\n        throw \"PowerShellGet is not available\"\n    }\n\n    if ($psget.Version -lt [Version]'1.6') {\n        #Bootstrap the NuGet package provider, which updates NuGet without requiring admin rights\n        Write-Debug \"Installing NuGet package provider\"\n        Get-PackageProvider NuGet -ForceBootstrap | Out-Null\n\n        #Use the currently-installed version of PowerShellGet\n        Import-PackageProvider PowerShellGet\n\n        #Download the version of PowerShellGet that we actually need\n        Write-Debug \"Installing PowershellGet\"\n        Save-Module -Name PowerShellGet -Path $LocalModules -MinimumVersion 1.6 -Force -ErrorAction SilentlyContinue\n    }\n\n    Write-Debug \"Importing PowershellGet\"\n    Import-Module PowerShellGet -MinimumVersion 1.6 -Force\n    #Make sure we're actually using the package provider from the imported version of PowerShellGet\n    Import-PackageProvider ((Get-Module PowerShellGet).Path) | Out-Null\n}\n\nfunction InstallLocalModule {\n    [CmdletBinding()]\n    Param(\n        [Parameter(Mandatory = $true)]\n        [string]$moduleName,\n        [Parameter(Mandatory = $false)]\n        [Version]$minimumVersion,\n        [Parameter(Mandatory = $false)]\n        [Version]$maximumVersion\n    )\n    try {\n        InstallPowerShellGet\n\n        Write-Debug \"Install $moduleName $requiredVersion\"\n        Save-Module -Name $moduleName -Path $LocalModules -Force -AcceptLicense -MinimumVersion $minimumVersion -MaximumVersion $maximumVersion -ErrorAction Stop\n    }\n    catch {\n        Write-Warning \"Could not install $moduleName $requiredVersion from any registered PSRepository\"\n    }\n}\n\nfunction GetHighestInstalledModule {\n    [CmdletBinding()]\n    Param(\n        [Parameter(Mandatory = $true, Position = 0)]\n        [string] $moduleName,\n\n        [Parameter(Mandatory = $false)]\n        [Version]$minimumVersion,\n        [Parameter(Mandatory = $false)]\n        [Version]$maximumVersion\n    )\n\n    return Get-Module $moduleName -ListAvailable |\n           Where {(!$minimumVersion -or ($_.Version -ge $minimumVersion)) -and (!$maximumVersion -or ($_.Version -le $maximumVersion))} |\n           Sort -Property @{Expression = {[System.Version]($_.Version)}; Descending = $True} |\n           Select -First 1\n}\n\nfunction GetHighestInstallableModule {\n    [CmdletBinding()]\n    Param(\n        [Parameter(Mandatory = $true, Position = 0)]\n        [string] $moduleName\n    )\n\n    try {\n        InstallPowerShellGet\n        Find-Module SqlChangeAutomation -AllVersions |\n            Sort -Property @{Expression = {[System.Version]($_.Version)}; Descending = $True} |\n            Select -First 1\n    }\n    catch {\n        Write-Warning \"Could not find any suitable versions of $moduleName from any registered PSRepository\"\n    }\n}\n\nfunction GetInstalledSqlChangeAutomationVersion {\n    $scaModule = (Get-Module $SqlChangeAutomationModuleName)\n\n    if ($scaModule -ne $null) {\n        return $scaModule.Version\n    }\n\n    $dlmaModule = (Get-Module $DlmAutomationModuleName)\n\n    if ($dlmaModule -ne $null) {\n        return $dlmaModule.Version\n    }\n\n    return $null\n}\n\nfunction ConfigureProxyIfVariableSet\n{\n    if ([string]::IsNullOrWhiteSpace($DLMAutomationProxyUrl) -eq $false)\n    {\n        Write-Debug \"Setting DefaultWebProxy to $proxyUrl\"\n\n        [System.Net.WebRequest]::DefaultWebProxy = New-Object System.Net.WebProxy($DLMAutomationProxyUrl)\n        [System.Net.WebRequest]::DefaultWebProxy.credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials\n        [System.Net.WebRequest]::DefaultWebProxy.BypassProxyOnLocal = $True\n    }\n}\n\n\n$ErrorActionPreference = 'Stop'\n$VerbosePreference = 'Continue'\n\n# Set process level FUR environment\n$env:REDGATE_FUR_ENVIRONMENT = \"Octopus Step Templates\"\n\n#Helper functions for paramter handling\nfunction Required() {\n    Param(\n        [Parameter(Mandatory = $false)][string]$Parameter,\n        [Parameter(Mandatory = $true)][string]$Name\n    )\n    if ([string]::IsNullOrWhiteSpace($Parameter)) { throw \"You must enter a value for '$Name'\" }\n}\nfunction Optional() {\n    #Default is untyped here - if we specify [string] powershell will convert nulls into empty string\n    Param(\n        [Parameter(Mandatory = $false)][string]$Parameter,\n        [Parameter(Mandatory = $false)]$Default\n    )\n    if ([string]::IsNullOrWhiteSpace($Parameter)) {\n        $Default\n    } else {\n        $Parameter\n    }\n}\nfunction RequireBool() {\n    Param(\n        [Parameter(Mandatory = $false)][string]$Parameter,\n        [Parameter(Mandatory = $true)][string]$Name\n    )\n    $Result = $False\n    if (![bool]::TryParse($Parameter , [ref]$Result )) { throw \"'$Name' must be a boolean value.\" }\n    $Result\n}\nfunction RequirePositiveNumber() {\n    Param(\n        [Parameter(Mandatory = $false)][string]$Parameter,\n        [Parameter(Mandatory = $true)][string]$Name\n    )\n    $Result = 0\n    if (![int32]::TryParse($Parameter , [ref]$Result )) { throw \"'$Name' must be a numerical value.\" }\n    if ($Result -lt 0) { throw \"'$Name' must be >= 0.\" }\n    $Result\n}\n\n$SpecificModuleVersion = Optional -Parameter $SpecificModuleVersion\n$UseInstalledModuleVersion = Optional -Parameter $UseInstalledModuleVersion -Default 'False'\n$UseInstalledVersionSwitch = [bool]::Parse($UseInstalledModuleVersion)\nInstallCorrectSqlChangeAutomation -requiredVersion $SpecificModuleVersion -useInstalledVersion $UseInstalledVersionSwitch\n\n# Check if SQL Change Automation is installed.\n$powershellModule = Get-Module -Name SqlChangeAutomation\nif ($powershellModule -eq $null) {\n    throw \"Cannot find SQL Change Automation on your Octopus Tentacle. If SQL Change Automation is installed, try restarting the Tentacle service for it to be detected.\"\n}\n\n$currentVersion = $powershellModule.Version\n$minimumRequiredVersion = [version] '3.0.3'\nif ($currentVersion -lt $minimumRequiredVersion) {\n    throw \"This step requires SQL Change Automation version $minimumRequiredVersion or later. The current version is $currentVersion. The latest version can be found at http://www.red-gate.com/sca/productpage\"\n}\n\n$minimumRequiredVersionDataCompareOptions = [version] '3.3.0'\n$minimumRequiredVersionTrustServerCertificate = [version]'4.3.20267'\n\nfunction AreConnectionOptionsHandled($encryptConnection, $trustServerCertificate)\n{\n    if ([string]::IsNullOrWhiteSpace($currentVersion) -or $currentVersion -ge $minimumRequiredVersionTrustServerCertificate)\n    {\n        return $true\n    }\n    elseif($encryptConnection -or $trustServerCertificate)\n    {\n        Write-Warning \"Encrypt and TrustServerCertificate options require SQL Change Automation version $minimumRequiredVersionTrustServerCertificate or later. The current version is $currentVersion.\"\n        return $false\n    }\n}\n\n# Check the parameters.\nRequired -Parameter $DLMAutomationDeploymentResourcesPath -Name 'Export Path'\nRequired -Parameter $DLMAutomationDeleteExistingFiles -Name 'Delete files in export folder'\nRequired -Parameter $DLMAutomationNuGetDbPackageDownloadStepName -Name 'Database package step'\nRequired -Parameter $DLMAutomationDatabaseServer -Name 'Target SQL Server instance'\nRequired -Parameter $DLMAutomationDatabaseName -Name 'Target database name'\n$DLMAutomationDatabaseUsername = Optional -Parameter $DLMAutomationDatabaseUsername\n$DLMAutomationDatabasePassword = Optional -Parameter $DLMAutomationDatabasePassword\n$DLMAutomationEncrypt = Optional -Parameter $DLMAutomationEncrypt\n$DLMAutomationTrustServerCertificate = Optional -Parameter $DLMAutomationTrustServerCertificate\n$DLMAutomationFilterPath = Optional -Parameter $DLMAutomationFilterPath\n$DLMAutomationCompareOptions = Optional -Parameter $DLMAutomationCompareOptions\n$DLMAutomationDataCompareOptions = Optional -Parameter $DLMAutomationDataCompareOptions\n$DLMAutomationTransactionIsolationLevel = Optional -Parameter $DLMAutomationTransactionIsolationLevel -Default 'Serializable'\n$DLMAutomationIgnoreStaticData = Optional -Parameter $DLMAutomationIgnoreStaticData -Default 'False'\n$DLMAutomationIncludeIdenticalsInReport = Optional -Parameter $DLMAutomationIncludeIdenticalsInReport -Default 'False'\n$DLMAutomationModuleInstallationFolder = Optional -Parameter $DLMAutomationModuleInstallationFolder\n$DLMAutomationProxyUrl = Optional -Parameter $DLMAutomationProxyUrl\n\n# Get the NuGet package installation directory path.\n$packagePath = $OctopusParameters[\"Octopus.Action[$DLMAutomationNuGetDbPackageDownloadStepName].Output.Package.InstallationDirectoryPath\"]\nif($packagePath -eq $null) {\n    throw \"The 'Database package download step' is not a 'Deploy a NuGet package' step: '$DLMAutomationNuGetDbPackageDownloadStepName'\"\n}\n\n# Constructing the unique export path.\n$projectId = $OctopusParameters[\"Octopus.Project.Id\"]\n$releaseNumber = $OctopusParameters[\"Octopus.Release.Number\"]\n$nugetPackageId = $OctopusParameters[\"Octopus.Action[$DLMAutomationNuGetDbPackageDownloadStepName].Package.NuGetPackageId\"]\n$exportPath = Join-Path (Join-Path (Join-Path $DLMAutomationDeploymentResourcesPath $projectId) $releaseNumber) $nugetPackageId\n\n# Make sure the directory we're about to create doesn't already exist, and delete any files if requested.\nif ((Test-Path $exportPath) -AND ((Get-ChildItem $exportPath | Measure-Object).Count -ne 0)) {\n    if ($DLMAutomationDeleteExistingFiles -eq 'True') {\n        Write-Host \"Deleting all files in $exportPath\"\n        rmdir $exportPath -Recurse -Force\n    } else {\n        throw \"The export path is not empty: $exportPath.  Select the 'Delete files in export folder' option to overwrite the existing folder contents.\"\n    }\n}\n\n$connectionOptions = @{ }\n\nif(AreConnectionOptionsHandled([bool]::Parse($DLMAutomationEncrypt), [bool]::Parse($DLMAutomationTrustServerCertificate))) {\n    $connectionOptions += @{ 'Encrypt' = [bool]::Parse($DLMAutomationEncrypt) }\n    $connectionOptions += @{ 'TrustServerCertificate' = [bool]::Parse($DLMAutomationTrustServerCertificate) }\n}\n\n# Determine whether or not to include identical objects in the report.\n$targetDB = New-DatabaseConnection @connectionOptions `\n                                   -ServerInstance $DLMAutomationDatabaseServer `\n                                   -Database $DLMAutomationDatabaseName `\n                                   -Username $DLMAutomationDatabaseUsername `\n                                   -Password $DLMAutomationDatabasePassword | Test-DatabaseConnection\n\n$importedBuildArtifact = Import-DatabaseBuildArtifact -Path $packagePath\n\n# Only allow sqlcmd variables that don't have special characters like spaces, colon or dashes\n$regex = '^[a-zA-Z_][a-zA-Z0-9_]+$'\n$sqlCmdVariables = @{}\n$OctopusParameters.Keys | Where { $_ -match $regex } | ForEach {\n\t$sqlCmdVariables[$_] = $OctopusParameters[$_]\n}\n\n# Create the deployment resources from the database to the NuGet package\n$releaseParams = @{\n    Target = $targetDB\n    Source = $importedBuildArtifact\n    TransactionIsolationLevel = $DLMAutomationTransactionIsolationLevel\n    IgnoreStaticData = [bool]::Parse($DLMAutomationIgnoreStaticData)\n    FilterPath = $DLMAutomationFilterPath\n    SQLCompareOptions = $DLMAutomationCompareOptions\n    IncludeIdenticalsInReport = [bool]::Parse($DLMAutomationIncludeIdenticalsInReport)\n    SqlCmdVariables = $sqlCmdVariables\n}\n\nif($currentVersion -ge $minimumRequiredVersionDataCompareOptions) {\n    $releaseParams.SQLDataCompareOptions = $DLMAutomationDataCompareOptions\n} elseif(-not [string]::IsNullOrWhiteSpace($DLMAutomationDataCompareOptions)) {\n    Write-Warning \"SQL Data Compare options requires SQL Change Automation version $minimumRequiredVersionDataCompareOptions or later. The current version is $currentVersion.\"\n}\n\n$release = New-DatabaseReleaseArtifact @releaseParams\n\n# Export the deployment resources to disk\n$release | Export-DatabaseReleaseArtifact -Path $exportPath\n\n# Import the changes summary, deployment warnings, and update script as Octopus artifacts, so you can review them.\nfunction UploadIfExists() {\n    Param(\n        [Parameter(Mandatory = $true)]\n        [string]$ArtifactPath,\n        [Parameter(Mandatory = $true)]\n        [string]$Name\n    )\n    if (Test-Path $ArtifactPath) {\n        New-OctopusArtifact $ArtifactPath -Name $Name\n    }\n}\n\nUploadIfExists -ArtifactPath \"$exportPath\\Reports\\Changes.html\" -Name \"Changes-$DLMAutomationDatabaseName.html\"\nUploadIfExists -ArtifactPath \"$exportPath\\Reports\\Drift.html\" -Name \"Drift-$DLMAutomationDatabaseName.html\"\nUploadIfExists -ArtifactPath \"$exportPath\\Reports\\Warnings.xml\" -Name \"Warnings-$DLMAutomationDatabaseName.xml\"\nUploadIfExists -ArtifactPath \"$exportPath\\Update.sql\" -Name \"Update-$DLMAutomationDatabaseName.sql\"\nUploadIfExists -ArtifactPath \"$exportPath\\TargetedDeploymentScript.sql\" -Name \"TargetedDeploymentScript-$DLMAutomationDatabaseName.sql\"\nUploadIfExists -ArtifactPath \"$exportPath\\DriftRevertScript.sql\" -Name \"DriftRevertScript-$DLMAutomationDatabaseName.sql\"\n\n# Sets a variable if there are changes to deploy. Useful if you want to have steps run only when this is set\nif ($release.UpdateSQL -notlike '*This script is empty because the Target and Source schemas are equivalent*')\n{\n  Set-OctopusVariable -name \"ChangesToDeploy\" -value \"True\"\n}\n",
    "Octopus.Action.Script.Syntax": "PowerShell"
  },
  "Category": "Redgate",
  "HistoryUrl": "https://github.com/OctopusDeploy/Library/commits/master/step-templates//opt/buildagent/work/75443764cd38076d/step-templates/redgate-create-database-release.json",
  "Website": "/step-templates/c20b70dc-69aa-42a1-85db-6d37341b63e3",
  "Logo": "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",
  "$Meta": {
    "Type": "ActionTemplate"
  }
}

History

Page updated on Friday, July 17, 2015