Flyway Database Migrations

Octopus.Script exported 2024-03-05 by twerthi belongs to ‘Flyway’ category.

Step template to leverage Flyway to deploy migration scripts. This is the latest and greatest Flyway step template that leverages all the newest features of both Flyway and Octopus Deploy.

  • You can include the flyway executables in your package, if you include the flyway (Linux) or flyway.cmd (Windows) in the root of the package this step template will automatically find them.
  • You can use this with an execution container, negating the need to include Flyway in the package. If Flyway isn’t found in the package it will attempt to find /flyway/flyway (when using Linux containers) or flyway in the environment path and use that.
  • Support for all Flyway commands, including the undo command.
  • Support for flyway community, teams, enterprise, and pro editions.

Please note this requires Octopus Deploy 2019.10.0 or newer along with PowerShell Core installed on the machines running this step. AWS EC2 IAM Authentication requires the AWS CLI to be installed.

Parameters

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

Flyway Package

Flyway.Package.Value =

Required

The package containing the migration scripts you want Flyway to run. Please refer to documentation for core concepts and naming conventions.

Flyway Executable Path

Flyway.Executable.Path =

Optional

The path of the flyway executable. It can either be a relative path or an absolute path.

When not provided, this step template will test for the following. The step template places precedence on the version of the flyway included in the package. If Flyway is NOT found in the package, it will attempt to see if it is installed on the server by checking common paths.

Running on Linux:

  • .flyway: the package being deployed includes flyway and is running on Linux
  • /flyway/flyway: The default path for the Linux execution container.

Running on Windows:

  • \.flyway.cmd: the package being deployed includes flyway and is running on Windows
  • flyway: the package is in the path on the Windows VM or Windows Execution container.

Flyway Command

Flyway.Command.Value = migrate

Required

The flyway command you wish to run.

  • Migrate: Migrates the schema to the latest version
  • Migrate Dry Run: Does a migration dry run and saves the results to a file that is uploaded as an artifact. This works for the Teams version of Flyway only.
  • Check Changes: Produces a report indicating differences between applied migration scripts on your target database and pending migrations scripts (ie. the set of instructions you want to use to change your target database). This will report on the differences and perform a dry-run migration. Check Redgate’s documentation to ensure your server technology is supported. Available to Enterprise licenses only.
  • Check Drift: Produces a report indicating differences between the structure of your target database and the structure created by the migrations applied by Flyway. Available to Enterprise licenses only.
  • Check Dry Run: Produces a report listing out the migration scripts to run. Use this to perform a dry run check or if you have a Teams license.
  • Info: Prints the details and status information about all migrations.
  • Validate: Validates applied migrations against resolved ones (on the filesystem or classpath) to detect accidental changes that may prevent the schema(s) from being recreated precisely.
  • Undo: Undoes the most recently applied versioned migration. You must provide a Flyway Teams or Enterprise license key for this to work.
  • Repair: Repairs the Flyway schema history table by removing any failed migrations and realigning the checksums, descriptions and types of the applied migrations with the ones of the available migrations
  • Clean: It will effectively give you a fresh start, by wiping your configured schemas completely clean. All objects (tables, views, procedures, …) will be dropped. DO NOT USE AGAINST A PRODUCTION DB!!!
  • Baseline: Introducing Flyway to existing databases by baselining them at a specific version. This will cause Migrate to ignore all migrations up to and including the baseline version. Newer migrations will then be applied as usual.
  • Snapshot: Creates a file containing the schema of the specified database for subsequent use with the Check Drift option. Available to Enterprise licenses only.

License Key

Flyway.License.Key =

Optional

The Flyway Teams or Enterprise license key will enable undo functionality and the ability to dry run a migration.

-Url

Flyway.Target.Url =

Required

The URL parameter used in Flyway. This is the URL of the database to run the migration scripts on in the format specified in the default flyway.conf file.

Examples:

  • SQL Server: jdbc:sqlserver://host:port;databaseName=database
  • Oracle: jdbc:oracle:thin:@//host:port/service or jdbc:oracle:thin:@tns_entry
  • MySQL: jdbc:mysql://host:port/database
  • PostgreSQL: jdbc:postgresql://host:port/database
  • SQLite: jdbc:sqlite:database

Please refer to documentation for further examples.

Authentication Method

Flyway.Authentication.Method = usernamepassword

Method used to authenticate to the database server.

-User

Flyway.Database.User =

Optional

The user used to connect to the database.

-Password

Flyway.Database.User.Password =

Optional

The password used to connect to the database.

-Schemas

Flyway.Command.Schemas =

Optional

Comma-separated case-sensitive list of schemas managed by Flyway.

Example: schema1,schema2

Flyway will attempt to create these schemas if they do not already exist and will clean them in the order of this list. If Flyway created them, then the schemas themselves will be dropped when cleaning.

The first schema in the list will act as the default schema.

-Target

Flyway.Command.Target = latest

Optional for migrate, info, validate, check, and undo. Ignored for all other commands

The target version up to which Flyway should consider migrations. If set to a value other than current or latest, this must be a valid migration version (e.g. 2.1).

When migrating forwards, Flyway will apply all migrations up to and including the target version. Migrations with a higher version number will be ignored. If the target is current, then no versioned migrations will be applied but repeatable migrations will be, together with any callbacks.

When undoing migrations, Flyway will apply all undo scripts up to and including the target version. Undo scripts with a lower version number will be ignored. Specifying a target version should be done with care, as undo scripts typically destroy database objects.

Special Values:

  • current: designates the current version of the schema
  • latest: the latest version of the schema, as defined by the migration with the highest version

Default is: latest. When running the undo command, this will switch to current if latest is supplied.

-CherryPick

Flyway.Command.CherryPick =

Optional for migrate, info, check, and validate. Ignored for all other commands.

A Comma separated list of migrations that Flyway should consider when migrating, undoing, or repairing. Migrations are considered in the order that they are supplied, overriding the default ordering. Leave blank to consider all discovered migrations.

Each item in the list must either be a valid migration version (e.g 2.1) or a valid migration description (e.g. create_table).

See documentation for more details.

The default is an empty string, meaning this command-line argument will be skipped.

-OutOfOrder

Flyway.Command.OutOfOrder = false

Optional for migrate, info, check, and validate. Ignored for all other commands.

Allows migrations to be run “out of order”.

If you already have versions 1.0 and 3.0 applied, and now a version 2.0 is found, it will be applied too instead of being ignored.

See documentation for more details.

The default is False.

-SkipExecutingMigrations

Flyway.Command.SkipExecutingMigrations = false

Optional for migrate. Ignored for all other commands.

Whether Flyway should skip migration execution. The remainder of the operation will run as normal - including updating the schema history table, callbacks, and so on.

skipExecutingMigrations essentially allows you to mimic a migration being executed, because the schema history table is still updated as normal.

skipExecutingMigrations can be used to bring an out-of-process change into Flyway’s change control process. For instance, a script run against the database outside of Flyway (like a hotfix) can be turned into a migration. The hotfix migration can be deployed with Flyway with skipExecutingMigrations=true. The schema history table will be updated with the new migration, but the script itself won’t be executed again.

skipExecutingMigrations can be used with cherryPick to skip specific migrations.

See documentation for more details.

The default value is False.

-PlaceHolders

Flyway.Command.PlaceHolders =

Optional for migrate, info, validate, undo, check, and repair. Ignored for all other commands.

Placeholders to replace in SQL migrations.

Each new line represents a new placeholder. This will only work with string variable types, text and sensitive values.

Imagine this SQL Script

INSERT INTO ${Key1} (name) VALUES ('Mr. T')
GRANT SELECT ON SCHEMA ${flyway:defaultSchema} TO ${Key2}

Use the format Name::Value for example:

Key1::My Super Awesome Value

Key2::Other Super Awesome Value

This will replace ${Key1} with My Super Awesome Value and ${Key2} with Other Super Awesome Value.

-InfoSinceDate

Flyway.Command.InfoSinceDate =

Optional with the info command and Flyway Teams and Flyway Enterprise editions. Ignored for all other commands.

Limits info to show only migrations applied after this date, and any unapplied migrations. Must be in the format dd/MM/yyyy HH:mm (e.g. 01/12/2020 13:00)

-InfoSinceVersion

Flyway.Command.InfoSinceVersion =

Optional with the info command and Flyway Teams and Flyway Enterprise editions. Ignored for all other commands.

Limits info to show only migrations greater than or equal to this version, and any repeatable migrations. (e.g 1.1)

-baselineOnMigrate

Flyway.Command.BaseLineOnMigrate = false

Optional when using migrate. Ignored for all other commands.

Whether to automatically call baseline when migrate is executed against a non-empty schema with no metadata table. This schema will then be baselined with the baselineVersion before executing the migrations. Only migrations above baselineVersion will then be applied.

This is useful for initial Flyway production deployments on projects with an existing DB.

Be careful when enabling this as it removes the safety net that ensures Flyway does not migrate the wrong database in case of a configuration mistake!

-baselineVersion

Flyway.Command.BaselineVersion =

Required when using Baseline or when BaselineOnMigrate is set to True. Ignored for all other commands.

The version to tag an existing schema with when executing baseline.

-baseLineDescription

Flyway.Command.BaselineDescription =

Required when using Baseline. Ignored for all other commands.

The Description to tag an existing schema with when executing baseline.

-Locations

Flyway.Command.Locations =

Optional with the info, migrate, repair, undo, check, and validate commands.

Specifies the location of the script files to execute. If left blank, the Extracted Path of the package will be used.

Example: filesystem:#{Octopus.Action.Package[Flyway.Package.Value].ExtractedPath}/MySubFolder

-check.BuildUrl

Flyway.Command.CheckBuildUrl =

Required when using Check Changes or Check Drift commands and -snapshot.FileName is empty.

Please Note: When -snapshot.FileName is not empty, this parameter is ignored.

Flyway uses the URL to an existing database as a temporary database to perform the check logic against. Flyway will clean this database, so if you specify a full database, you must ensure it is okay for Flyway to erase its schema.

Examples:

  • SQL Server: jdbc:sqlserver://host:port;databaseName=database
  • Oracle: jdbc:oracle:thin:@//host:port/service or jdbc:oracle:thin:@tns_entry
  • MySQL: jdbc:mysql://host:port/database
  • PostgreSQL: jdbc:postgresql://host:port/database
  • SQLite: jdbc:sqlite:database

Please refer to documentation for further examples.

-check.BuildUser

Flyway.Database.Check.User =

Optional

The username of the user for the build database. Use this if the build database needs a separate username and password from the authentication information supplied above.

Please Note: When -snapshot.FileName is not empty, this parameter is ignored.

-check.BuildPassword

Flyway.Database.Check.User.Password =

Optional

The password of the user for the build database. Use this if the build database needs a separate username and password from the authentication information supplied above.

Please Note: When -snapshot.FileName is not empty, this parameter is ignored.

-check.failOnDrift

Flyway.Command.FailOnDrift = true

Required when using Check Drift. Ignored for all other commands.

Indicates if Flyway will terminate with a non-zero return code if drift is detected. When using Check Drift with a snapshot and you want to see the changes, set this to false

-snapshot.FileName

Flyway.Command.Snapshot.FileName =

Required when using Snapshot. Optional when using the Check Drift command. Ignored for all other commands.

The name, including the path, of the snapshot file to create or use.

If this parameter is populated the -check.BuildUser, check.BuildPassword, and -check.BuildUrl parameters are all ignored.

Hint: If executing this step in an execution container, consider ../../../ as the path prefix as the script will execute in /etc/octopus/default/Work/[Random Folder Name]/Flyway.Package.Value. /etc/octopus/default is mounted as a volume. Or leverage a NAS or other file share.

Important: You will need this file for future steps. It must be stored in a non-temporary location. By default, Octopus runs this script in a temporary location on a worker. That location is deleted automatically once this step is complete. If you do not supply a path, the file will be deleted.

Additional arguments

Flyway.Additional.Arguments =

Any additional arguments that need to be passed (ie `-table=“MyTable”)

Script body

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

$VerboseActionPreference="Continue"

function Get-FlywayExecutablePath
{
	param (
    	$providedPath
    )
    
    if ([string]::IsNullOrWhiteSpace($providedPath) -eq $false)
    {
    	Write-Host "The executable path was provided, testing to see if it is absolute or relative"
		if ([IO.Path]::IsPathRooted($providedPath))
        {
        	Write-Host "The provided path is absolute, using that"
            
        	return $providedPath
        }
        
        Write-Host "The provided path was relative, combining $(Get-Location) with $providedPath"
        return Join-Path $(Get-Location) $providedPath
    }
    
    Write-Host "Checking to see if we are currently running on Linux"
    if ($IsLinux)    
    {
    	Write-Host "Currently running on Linux"
    	Write-Host "Checking to see if flyway was included with the package"
    	if (Test-Path "./flyway")
        {
        	Write-Host "It was, using that version of flyway"
        	return "flyway"
        }
        
        Write-Host "Testing to see if we are on an execution container with /flyway/flyway as the path"
    	if (Test-Path "/flyway/flyway")
        {
        	Write-Host "We are, using /flyway/flyway"
        	return "/flyway/flyway"
        }               
    }
    
    Write-Host "Currently running on Windows"
    
    Write-Host "Testing to see if flyway.cmd was included with the package"
    if (Test-Path ".\flyway.cmd")
    {
    	Write-Host "It was, using that version."
    	return ".\flyway.cmd"
    }
    
    Write-Host "Testing to see if flyway can be found in the env path"
    $flywayExecutable = (Get-Command "flyway" -ErrorAction SilentlyContinue)
    if ($null -ne $flywayExecutable)
    {
    	Write-Host "The flyway folder is part of the environment path"
        return $flywayExecutable.Source
    }
    
    Fail-Step "Unable to find flyway executable.  Please include it as part of the package, or provide the path to it."
}

function Test-AddParameterToCommandline
{
	param (
    	$acceptedCommands,
        $selectedCommand,
        $parameterValue,
        $defaultValue,
        $parameterName
    )
    
    if ([string]::IsNullOrWhiteSpace($parameterValue) -eq $true)
    {    	
    	Write-Verbose "$parameterName is empty, returning false"
    	return $false
    }
    
    if ([string]::IsNullOrWhiteSpace($defaultValue) -eq $false -and $parameterValue.ToLower().Trim() -eq $defaultValue.ToLower().Trim())
    {
    	Write-Verbose "$parameterName is matches the default value, returning false"
    	return $false
    }
    
    if ([string]::IsNullOrWhiteSpace($acceptedCommands) -eq $true -or $acceptedCommands -eq "any")
    {
    	Write-Verbose "$parameterName has a value and this is for any command, returning true"
    	return $true
    }
    
    $acceptedCommandArray = $acceptedCommands -split ","
    foreach ($command in $acceptedCommandArray)
    {
    	if ($command.ToLower().Trim() -eq $selectedCommand.ToLower().Trim())
        {
        	Write-Verbose "$parameterName has a value and the current command $selectedCommand matches the accepted command $command, returning true"
        	return $true
        }
    }
    
    Write-Verbose "$parameterName has a value but is not accepted in the current command, returning false"
    return $false
}

function Get-ParsedUrl
{
	# Define parameters
    param (
    	$ConnectionUrl
    )
    
    # Remove the 'jdbc:' portion from the $ConnectionUrl parameter
    $ConnectionUrl = $ConnectionUrl.ToLower().Replace("jdbc:", "")
    
    # Parse and return the url
    return [System.Uri]$ConnectionUrl
}

# Declaring the path to the NuGet package
$flywayPackagePath = $OctopusParameters["Octopus.Action.Package[Flyway.Package.Value].ExtractedPath"]
$flywayUrl = $OctopusParameters["Flyway.Target.Url"]
$flywayUser = $OctopusParameters["Flyway.Database.User"]
$flywayUserPassword = $OctopusParameters["Flyway.Database.User.Password"]
$flywayCommand = $OctopusParameters["Flyway.Command.Value"]
$flywayLicenseKey = $OctopusParameters["Flyway.License.Key"]
$flywayExecutablePath = $OctopusParameters["Flyway.Executable.Path"]
$flywaySchemas = $OctopusParameters["Flyway.Command.Schemas"]
$flywayTarget = $OctopusParameters["Flyway.Command.Target"]
$flywayInfoSinceDate = $OctopusParameters["Flyway.Command.InfoSinceDate"]
$flywayInfoSinceVersion = $OctopusParameters["Flyway.Command.InfoSinceVersion"]
$flywayLicensedEdition = $OctopusParameters["Flyway.License.Version"]
$flywayCherryPick = $OctopusParameters["Flyway.Command.CherryPick"]
$flywayOutOfOrder = $OctopusParameters["Flyway.Command.OutOfOrder"]
$flywaySkipExecutingMigrations = $OctopusParameters["Flyway.Command.SkipExecutingMigrations"]
$flywayPlaceHolders = $OctopusParameters["Flyway.Command.PlaceHolders"]
$flywayBaseLineVersion = $OctopusParameters["Flyway.Command.BaselineVersion"]
$flywayBaselineDescription = $OctopusParameters["Flyway.Command.BaselineDescription"]
$flywayAuthenticationMethod = $OctopusParameters["Flyway.Authentication.Method"]
$flywayLocations = $OctopusParameters["Flyway.Command.Locations"]
$flywayAdditionalArguments = $OctopusParameters["Flyway.Additional.Arguments"]
$flywayStepName = $OctopusParameters["Octopus.Action.StepName"]
$flywayEnvironment = $OctopusParameters["Octopus.Environment.Name"]
$flywayCheckBuildUrl = $OctopusParameters["Flyway.Command.CheckBuildUrl"]
$flywayCheckBuildUsername = $OctopusParameters["Flyway.Database.Check.User"]
$flywayCheckBuildPassword = $OctopusParameters["Flyway.Database.Check.User.Password"]
$flywayBaselineOnMigrate = $OctopusParameters["Flyway.Command.BaseLineOnMigrate"]
$flywaySnapshotFileName = $OctopusParameters["Flyway.Command.Snapshot.FileName"]
$flywayCheckFailOnDrift = $OctopusParameters["Flyway.Command.FailOnDrift"]

if ([string]::IsNullOrWhitespace($flywayLocations))
{
	$flywayLocations = "filesystem:$flywayPackagePath"
}


# Logging for troubleshooting
Write-Host "*******************************************"
Write-Host "Logging variables:"
Write-Host " - - - - - - - - - - - - - - - - - - - - -"
Write-Host "PackagePath: $flywayPackagePath"
Write-Host "Flyway Executable Path: $flywayExecutablePath"
Write-Host "Flyway Command: $flywayCommand"
Write-Host "-url: $flywayUrl"
Write-Host "-user: $flywayUser"
Write-Host "-schemas: $flywaySchemas"
Write-Host "-target: $flywayTarget"
Write-Host "-cherryPick: $flywayCherryPick"
Write-Host "-outOfOrder: $flywayOutOfOrder"
Write-Host "-skipExecutingMigrations: $flywaySkipExecutingMigrations"
Write-Host "-infoSinceDate: $flywayInfoSinceDate"
Write-Host "-infoSinceVersion: $flywayInfoSinceVersion"
Write-Host "-baselineOnMigrate: $flywayBaselineOnMigrate"
Write-Host "-baselineVersion: $flywayBaselineVersion"
Write-Host "-baselineDescription: $flywayBaselineDescription"
Write-Host "-locations: $flywayLocations"
Write-Host "-check.BuildUrl: $flywayCheckBuildUrl"
Write-Host "-check.failOnDrift: $flywayCheckFailOnDrift"
Write-Host "-snapshot.FileName OR check.DeployedSnapshot: $flywaySnapshotFileName"
Write-Host "Additional Arguments: $flywayAdditionalArguments"
Write-Host "placeHolders: $flywayPlaceHolders"
Write-Host "*******************************************"

if ($null -eq $IsWindows) {
    Write-Host "Determining Operating System..."
    $IsWindows = ([System.Environment]::OSVersion.Platform -eq "Win32NT")
    $IsLinux = ([System.Environment]::OSVersion.Platform -eq "Unix")
}

Write-Host "Setting execution location to: $flywayPackagePath"
Set-Location $flywayPackagePath

$flywayCmd = Get-FlywayExecutablePath -providedPath $flywayExecutablePath

$commandToUse = $flywayCommand
if ($flywayCommand -eq "migrate dry run")
{
	$commandToUse = "migrate"
}

if ($flywayCommand -eq "check dry run" -or $flywayCommand -eq "check changes" -or $flywayCommand -eq "check drift")
{
	$commandToUse = "check"
}

$arguments = @(
	$commandToUse    
)

if ($flywayCommand -eq "check dry run")
{
	$arguments += "-dryrun"
}

if ($flywayCommand -eq "check changes")
{
	$arguments += "-changes"
    $arguments += "-dryrun"
}

if ($flywayCommand -eq "check drift")
{
	$arguments += "-drift"
}

# Deteremine authentication method
switch ($flywayAuthenticationMethod)
{
	"awsiam"
    {
		# Check to see if OS is Windows and running in a container
        if ($IsWindows -and $env:DOTNET_RUNNING_IN_CONTAINER)
        {
        	throw "IAM Role authentication is not supported in a Windows container."
        }

		# Get parsed connection string url
        $parsedUrl = Get-ParsedUrl -ConnectionUrl $flywayUrl
        
        # Region is part of the RDS endpoint, extract
        $region = ($parsedUrl.Host.Split("."))[2]

		Write-Host "Generating AWS IAM token ..."
		$flywayUserPassword = (aws rds generate-db-auth-token --hostname $parsedUrl.Host --region $region --port $parsedUrl.Port --username $flywayUser)

		$arguments += "-user=`"$flywayUser`""
    	$arguments += "-password=`"$flywayUserPassword`""

		break
    }
	"azuremanagedidentity"
    {
		# Check to see if OS is Windows and running in a container
        if ($IsWindows -and $env:DOTNET_RUNNING_IN_CONTAINER)
        {
        	throw "Azure Managed Identity is not supported in a Windows container."
        }
        
        # SQL Server driver doesn't assign password
        if (!$flywayUrl.ToLower().Contains("jdbc:sqlserver:"))
        {        
          # Get login token
          Write-Host "Generating Azure Managed Identity token ..."
          $token = Invoke-RestMethod -Method GET -Uri "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://ossrdbms-aad.database.windows.net" -Headers @{"MetaData" = "true"} -UseBasicParsing

          $flywayUserPassword = $token.access_token
          $arguments += "-password=`"$flywayUserPassword`""
          $arguments += "-user=`"$flywayUser`""
        }
        else
        {
            
			# Check to see if the querstring parameter for Azure Managed Identity is present
            if (!$flywayUrl.ToLower().Contains("authentication=activedirectorymsi"))
            {
                # Add the authentication piece to the jdbc url
                if (!$flywayUrl.EndsWith(";"))
                {
                	# Add the separator
                    $flywayUrl += ";"
                }
                
                # Add authentication piece
                $flywayUrl += "Authentication=ActiveDirectoryMSI"
            }
        }
        
        break
    }
    "gcpserviceaccount"
    {
		# Check to see if OS is Windows and running in a container
        if ($IsWindows -and $env:DOTNET_RUNNING_IN_CONTAINER)
        {
        	throw "GCP Service Account authentication is not supported in a Windows container."
        }
    
        # Define header
        $header = @{ "Metadata-Flavor" = "Google"}

        # Retrieve service accounts
        $serviceAccounts = Invoke-RestMethod -Method Get -Uri "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/" -Headers $header -UseBasicParsing

        # Results returned in plain text format, get into array and remove empty entries
        $serviceAccounts = $serviceAccounts.Split([Environment]::NewLine, [StringSplitOptions]::RemoveEmptyEntries)

        # Retreive the specific service account assigned to the VM
        $serviceAccount = $serviceAccounts | Where-Object {$_.ToLower().Contains("iam.gserviceaccount.com") }

		Write-Host "Generating GCP IAM token ..."
        # Retrieve token for account
        $token = Invoke-RestMethod -Method Get -Uri "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/$serviceAccount/token" -Headers $header -UseBasicParsing
        
        $flywayUserPassword = $token.access_token
        
        $arguments += "-user=`"$flywayUser`""
        $arguments += "-password=`"$flywayUserPassword`""
        #$env:FLYWAY_PASSWORD = $flywayUserPassword
        
        break
    }   
    "usernamepassword"
    {
    	# Add password
        Write-Host "Testing for parameters that can be applied to any command"
        if (Test-AddParameterToCommandline -parameterValue $flywayUser -acceptedCommands "any" -selectedCommand $flywayCommand -parameterName "-user")
        {
            Write-Host "User provided, adding user and password command line argument"
            $arguments += "-user=`"$flywayUser`""
            $arguments += "-password=`"$flywayUserPassword`""
        }
        
        break
    }
    "windowsauthentication"
    {
    	# Display to the user they've selected windows authentication.  Though this is dictated by the jdbc url, this is added to make sure the user knows that's what is
        # being used
        Write-Host "Using Windows Authentication"
        
        # Check for integratedauthentication=true in url
        if (!$flywayUrl.ToLower().Contains("integratedsecurity=true"))
        {
        	# Check to see if the connection url ends with a ;
            if (!$flywayUrl.EndsWith(";"))
            {
            	# Add the ;
                $flywayUrl += ";"
            }
            
            $flywayUrl += "integratedSecurity=true;"
        }
        break
    }
}

$arguments += "-url=`"$flywayUrl`""
$arguments += "-locations=`"$flywayLocations`""

if (Test-AddParameterToCommandline -parameterValue $flywaySchemas -acceptedCommands "any" -selectedCommand $flywayCommand -parameterName "-schemas")
{
	Write-Host "Schemas provided, adding schemas command line argument"
	$arguments += "-schemas=`"$flywaySchemas`""    
}

if (Test-AddParameterToCommandline -parameterValue $flywayLicenseKey -acceptedCommands "any" -selectedCommand $flywayCommand -parameterName "-licenseKey")
{
	Write-Host "License key provided, adding -licenseKey command line argument"
	$arguments += "-licenseKey=`"$flywayLicenseKey`""                  
}
Write-Host "Finished testing for parameters that can be applied to any command, moving onto command specific parameters"

if (Test-AddParameterToCommandline -parameterValue $flywayCherryPick -acceptedCommands "migrate,info,validate,check" -selectedCommand $flywayCommand -parameterName "-cherryPick")
{
	Write-Host "Cherry pick provided, adding cherry pick command line argument"
	$arguments += "-cherryPick=`"$flywayCherryPick`""    
}

if (Test-AddParameterToCommandline -parameterValue $flywayOutOfOrder -defaultValue "false" -acceptedCommands "migrate,info,validate,check" -selectedCommand $commandToUse -parameterName "-outOfOrder")
{
	Write-Host "Out of order is not false, adding out of order command line argument"
	$arguments += "-outOfOrder=`"$flywayOutOfOrder`""    
}

if (Test-AddParameterToCommandline -parameterValue $flywayPlaceHolders -acceptedCommands "migrate,info,validate,undo,repair,check" -selectedCommand $commandToUse -parameterName "-placeHolders")
{
	Write-Host "Placeholder parameter provided, adding them to the command line arguments"
    
    $placeHolderValueList = @(($flywayPlaceHolders -Split "`n").Trim())
    foreach ($placeHolder in $placeHolderValueList)
    {
    	$placeHolderSplit = $placeHolder -Split "::"
        $placeHolderKey = $placeHolderSplit[0]
        $placeHolderValue = $placeHolderSplit[1]
        Write-Host "Adding -placeHolders.$placeHolderKey = $placeHolderValue to the argument list"
        
        $arguments += "-placeholders.$placeHolderKey=`"$placeHolderValue`""    
    }   	
}

if (Test-AddParameterToCommandline -parameterValue $flywayTarget -acceptedCommands "migrate,info,validate,undo,check" -selectedCommand $commandToUse -parameterName "-target")
{
	Write-Host "Target provided, adding target command line argument"

	if ($flywayTarget.ToLower().Trim() -eq "latest" -and $flywayCommand -eq "undo")
	{
		Write-Host "The current target is latest, but the command is undo, changing the target to be current"
		$flywayTarget = "current"
	}

	$arguments += "-target=`"$flywayTarget`""    
}

if (Test-AddParameterToCommandline -parameterValue $flywaySkipExecutingMigrations -defaultValue "false" -acceptedCommands "migrate" -selectedCommand $flywayCommand -parameterName "-skipExecutingMigrations")
{
	Write-Host "Skip executing migrations is not false, adding skip executing migrations command line argument"
	$arguments += "-skipExecutingMigrations=`"$flywaySkipExecutingMigrations`""    
}

if (Test-AddParameterToCommandline -parameterValue $flywayBaselineOnMigrate -defaultValue "false" -acceptedCommands "migrate" -selectedCommand $flywayCommand -parameterName "-baselineOnMigrate")
{
	Write-Host "Baseline on migrate is not false, adding the baseline on migrate argument"
	$arguments += "-baselineOnMigrate=`"$flywayBaselineOnMigrate`""    
    
    if (Test-AddParameterToCommandline -parameterValue $flywayBaselineVersion -acceptedCommands "migrate" -selectedCommand $flywayCommand -parameterName "-baselineVersion")
    {
    	Write-Host "Baseline version has been specified, adding baseline version argument"
		$arguments += "-baselineVersion=`"$flywayBaselineVersion`""  
    }
}

if (Test-AddParameterToCommandline -parameterValue $flywayBaselineVersion -acceptedCommands "baseline" -selectedCommand $flywayCommand -parameterName "-baselineVersion")
{
	Write-Host "Doing a baseline, adding baseline version and description"
	$arguments += "-baselineVersion=`"$flywayBaselineVersion`""    
    $arguments += "-baselineDescription=`"$flywayBaselineDescription`""    
}

if (Test-AddParameterToCommandline -parameterValue $flywayInfoSinceDate -acceptedCommands "info" -selectedCommand $flywayCommand -parameterName "-infoSinceDate")
{
	Write-Host "Info since date has been provided, adding that to the command line arguments"
	$arguments += "-infoSinceDate=`"$flywayInfoSinceDate`""
}

if (Test-AddParameterToCommandline -parameterValue $flywayInfoSinceVersion -acceptedCommands "info" -selectedCommand $flywayCommand -parameterName "-infoSinceVersion")
{
	Write-Host "Info since version has been provided, adding that to the command line arguments"
	$arguments += "-infoSinceVersion=`"$flywayInfoSinceVersion`""
} 

if (Test-AddParameterToCommandline -parameterValue $flywaySnapshotFileName -acceptedCommands "snapshot" -selectedCommand $commandToUse -parameterName "-snapshot.filename")
{
	Write-Host "Snapshot filename has been provided, adding that to the command line arguments"
    $folderName = Split-Path -Parent $flywaySnapshotFileName
    if ((test-path $folderName) -eq $false)
    {
    	New-Item $folderName -ItemType Directory
    }
    $arguments += "-snapshot.filename=`"$flywaySnapshotFileName`""
}

$snapshotFileNameforCheckProvided = $false
if (Test-AddParameterToCommandline -parameterValue $flywaySnapshotFileName -acceptedCommands "check" -selectedCommand $commandToUse -parameterName "-check.deployedSnapshot")
{
	Write-Host "Snapshot filename has been provided for the check command, adding that to the command line arguments"
    $folderName = Split-Path -Parent $flywaySnapshotFileName
    if ((test-path $folderName) -eq $false)
    {
    	New-Item $folderName -ItemType Directory
    }
    $arguments += "-check.deployedSnapshot=`"$flywaySnapshotFileName`""
    $snapshotFileNameforCheckProvided = $true
}

if ((Test-AddParameterToCommandline -parameterValue $flywayCheckBuildUrl -acceptedCommands "check" -selectedCommand $commandToUse -parameterName "-check.buildUrl") -eq $true -and $snapshotFileNameforCheckProvided -eq $false)
{
	Write-Host "Check build URL has been provided, adding that to the command line arguments"
	$arguments += "-check.buildUrl=`"$flywayCheckBuildUrl`""
}

Write-Host "Checking to see if the check username and password were supplied"
if ((Test-AddParameterToCommandline -parameterValue $flywayCheckBuildUsername -acceptedCommands "check" -selectedCommand $commandToUse -parameterName "-user")  -eq $true -and $snapshotFileNameforCheckProvided -eq $false)
{
	Write-Host "Check User provided, adding check user and check password command line argument"
	$arguments += "-check.buildUser=`"$flywayCheckBuildUsername`""
	$arguments += "-check.buildPassword=`"$flywayCheckBuildPassword`""
}

if (Test-AddParameterToCommandline -parameterValue $flywayCheckFailOnDrift -acceptedCommands "check drift" -selectedCommand $flywayCommand -parameterName "-check.failOnDrift")
{
	Write-Host "Doing a check drift command, adding the fail on drift"
	$arguments += "-check.failOnDrift=`"$flywayCheckFailOnDrift`""
}


Write-Host "Finished checking for command specific parameters, moving onto execution"
$dryRunOutputFile = ""

if ($flywayCommand -eq "migrate dry run")
{
	$dryRunOutputFile = Join-Path $(Get-Location) "dryRunOutput"
    Write-Host "Adding the argument dryRunOutput so Flyway will perform a dry run and not an actual migration."
    $arguments += "-dryRunOutput=`"$dryRunOutputFile`""
}

# Check to see if there's any additional arguments to add
if (![string]::IsNullOrWhitespace($flywayAdditionalArguments))
{
	# Split on space
    $flywayAdditionalArgumentsArray = ($flywayAdditionalArguments.Split(" ", [System.StringSplitOptions]::RemoveEmptyEntries))

    # Loop through array
    foreach ($newArgument in $flywayAdditionalArgumentsArray)
    {
    	# Add the arguments
    	$arguments += $newArgument
    }
}

# Display what's going to be run
if (![string]::IsNullOrWhitespace($flywayUserPassword))
{
    $flywayDisplayArguments = $arguments.PSObject.Copy()
    $arrayIndex = 0
    for ($i = 0; $i -lt $flywayDisplayArguments.Count; $i++)
    {
        if ($null -ne $flywayDisplayArguments[$i])
        {
            if ($flywayDisplayArguments[$i].Contains($flywayUserPassword))
            {
                $flywayDisplayArguments[$i] = $flywayDisplayArguments[$i].Replace($flywayUserPassword, "****")
            }
        }
    }

    Write-Host "Executing the following command: $flywayCmd $flywayDisplayArguments"
}
else
{
    Write-Host "Executing the following command: $flywayCmd $arguments"
}

# Attempt to find driver path for java
$driverPath = (Get-ChildItem -Path (Get-ChildItem -Path $flywayCmd).Directory -Recurse | Where-Object {$_.PSIsContainer -eq $true -and $_.Name -eq "drivers"})

# If found, add driver path to the PATH environment varaible
if ($null -ne $driverPath)
{
	$env:PATH += "$([IO.Path]::PathSeparator)$($driverPath.FullName)"
}

# Adjust call to flyway command based on OS
if ($IsLinux)
{
    & bash $flywayCmd $arguments
}
else
{
    & $flywayCmd $arguments
}

# Check exit code
if ($lastExitCode -ne 0)
{
	# Fail the step
    Write-Error "Execution of Flyway failed!"
}

$currentDate = Get-Date
$currentDateFormatted = $currentDate.ToString("yyyyMMdd_HHmmss")

# Check to see if the dry run variable has a value
if (![string]::IsNullOrWhitespace($dryRunOutputFile))
{     
    $sqlDryRunFile = "$($dryRunOutputFile).sql"
    $htmlDryRunFile = "$($dryRunOutputFile).html"
    
    if (Test-Path $sqlDryRunFile)
    {
    	New-OctopusArtifact -Path $sqlDryRunFile -Name "$($flywayStepName)_$($flywayEnvironment)_$($currentDateFormatted)_dryRunOutput.sql"
    }
    
    if (Test-Path $htmlDryRunFile)
    {
    	New-OctopusArtifact -Path $htmlDryRunFile -Name "$($flywayStepName)_$($flywayEnvironment)_$($currentDateFormatted)_dryRunOutput.html"
    }
}

$reportFile = Join-Path $(Get-Location) "report.html"
    
if (Test-Path $reportFile)
{
  	New-OctopusArtifact -Path $reportFile -Name "$($flywayStepName)_$($flywayEnvironment)_$($currentDateFormatted)_report.html"
}

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": "ccebac39-79a8-4ab4-b55f-19ea570d9ebc",
  "Name": "Flyway Database Migrations",
  "Description": "Step template to leverage Flyway to deploy migration scripts.  This is the latest and greatest Flyway step template that leverages all the newest features of both Flyway and Octopus Deploy.\n\n- You can include the flyway executables in your package, if you include the `flyway` (Linux) or `flyway.cmd` (Windows) in the root of the package this step template will automatically find them.\n- You can use this with an execution container, negating the need to include Flyway in the package. If Flyway isn't found in the package it will attempt to find `/flyway/flyway` (when using Linux containers) or `flyway` in the environment path and use that.\n- Support for all Flyway commands, including the `undo` command.\n- Support for flyway community, teams, enterprise, and pro editions.  \n\nPlease note this requires Octopus Deploy **2019.10.0** or newer along with PowerShell Core installed on the machines running this step.\nAWS EC2 IAM Authentication requires the AWS CLI to be installed.",
  "Version": 14,
  "ExportedAt": "2024-03-05T19:05:40.264Z",
  "ActionType": "Octopus.Script",
  "Author": "twerthi",
  "Packages": [
    {
      "Name": "Flyway.Package.Value",
      "Id": "0c0d333c-d794-4a16-a3a2-4bbba4550763",
      "PackageId": null,
      "FeedId": null,
      "AcquisitionLocation": "Server",
      "Properties": {
        "Extract": "True",
        "SelectionMode": "deferred",
        "PackageParameterName": "Flyway.Package.Value"
      }
    }
  ],
  "Parameters": [
    {
      "Id": "a4ba9557-61d3-4d93-99d9-9937abaded9c",
      "Name": "Flyway.Package.Value",
      "Label": "Flyway Package",
      "HelpText": "**Required**\n\nThe package containing the migration scripts you want Flyway to run.  Please refer to [documentation](https://flywaydb.org/documentation/concepts/migrations) for core concepts and naming conventions.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "Package"
      }
    },
    {
      "Id": "02d50d4c-02f9-44c2-8ee0-3fc69a21a165",
      "Name": "Flyway.Executable.Path",
      "Label": "Flyway Executable Path",
      "HelpText": "**Optional**\n\nThe path of the flyway executable.  It can either be a relative path or an absolute path.\n\nWhen not provided, this step template will test for the following.  The step template places precedence on the version of the flyway included in the package. If Flyway is NOT found in the package, it will attempt to see if it is installed on the server by checking common paths.\n\nRunning on `Linux`:\n- `.flyway`: the package being deployed includes flyway and is running on Linux\n- `/flyway/flyway`: The default path for the Linux execution container.\n\nRunning on Windows:\n- `\\.flyway.cmd`: the package being deployed includes flyway and is running on Windows\n- `flyway`: the package is in the path on the Windows VM or Windows Execution container.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "6e6e98ae-7e57-4bf7-a280-7c4a6bc57760",
      "Name": "Flyway.Command.Value",
      "Label": "Flyway Command",
      "HelpText": "**Required**\n\nThe [flyway command](https://flywaydb.org/documentation/usage/commandline/) you wish to run.\n\n- `Migrate`: Migrates the schema to the latest version\n- `Migrate Dry Run`: Does a migration dry run and saves the results to a file that is uploaded as an artifact.  This works for the `Teams` version of Flyway only.\n- `Check Changes`: Produces a report indicating differences between applied migration scripts on your target database and pending migrations scripts (ie. the set of instructions you want to use to change your target database).  This will report on the differences and perform a dry-run migration.  Check Redgate's documentation to ensure your server technology is supported.  Available to `Enterprise` licenses only.\n- `Check Drift`: Produces a report indicating differences between the structure of your target database and the structure created by the migrations applied by Flyway. Available to `Enterprise` licenses only.\n- `Check Dry Run`: Produces a report listing out the migration scripts to run.  Use this to perform a dry run check or if you have a `Teams` license.\n- `Info`: Prints the details and status information about all migrations.  \n- `Validate`: Validates applied migrations against resolved ones (on the filesystem or classpath) to detect accidental changes that may prevent the schema(s) from being recreated precisely.\n- `Undo`: Undoes the most recently applied versioned migration.  You must provide a Flyway `Teams` or `Enterprise` license key for this to work.\n- `Repair`: Repairs the Flyway schema history table by removing any failed migrations and realigning the checksums, descriptions and types of the applied migrations with the ones of the available migrations\n- `Clean`: It will effectively give you a fresh start, by wiping your configured schemas completely clean. All objects (tables, views, procedures, …) will be dropped.  **DO NOT USE AGAINST A PRODUCTION DB!!!**\n- `Baseline`:  Introducing Flyway to existing databases by baselining them at a specific version. This will cause Migrate to ignore all migrations up to and including the baseline version. Newer migrations will then be applied as usual.\n- `Snapshot`: Creates a file containing the schema of the specified database for subsequent use with the `Check Drift` option.  Available to `Enterprise` licenses only.\n",
      "DefaultValue": "migrate",
      "DisplaySettings": {
        "Octopus.ControlType": "Select",
        "Octopus.SelectOptions": "migrate|Migrate\nmigrate dry run|Migrate Dry Run\ncheck changes|Check Changes\ncheck drift|Check Drift\ncheck dry run|Check Dry Run\ninfo|Info\nvalidate|Validate\nundo|Undo\nrepair|Repair\nclean|Clean\nbaseline|Baseline\nsnapshot|Snapshot"
      }
    },
    {
      "Id": "e648821f-221c-4b8b-85fb-654f0d7379c5",
      "Name": "Flyway.License.Key",
      "Label": "License Key",
      "HelpText": "**Optional**\n\nThe [Flyway Teams](https://flywaydb.org/download) or `Enterprise` license key will enable undo functionality and the ability to dry run a migration.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "Sensitive"
      }
    },
    {
      "Id": "bb9d99ac-96b4-4d46-ae8f-87e5a7214278",
      "Name": "Flyway.Target.Url",
      "Label": "-Url",
      "HelpText": "**Required**\n\nThe [URL](https://flywaydb.org/documentation/configuration/parameters/url) parameter used in Flyway.  This is the URL of the database to run the migration scripts on in the format specified in the default flyway.conf file.\n\nExamples:\n- SQL Server: `jdbc:sqlserver://host:port;databaseName=database`\n- Oracle: `jdbc:oracle:thin:@//host:port/service` or `jdbc:oracle:thin:@tns_entry`\n- MySQL: `jdbc:mysql://host:port/database`\n- PostgreSQL: `jdbc:postgresql://host:port/database`\n- SQLite: `jdbc:sqlite:database`\n\nPlease refer to [documentation](https://flywaydb.org/documentation/database/sqlserver) for further examples.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "e9dccda1-4045-4e8e-b93e-4fac8d66630a",
      "Name": "Flyway.Authentication.Method",
      "Label": "Authentication Method",
      "HelpText": "Method used to authenticate to the database server.",
      "DefaultValue": "usernamepassword",
      "DisplaySettings": {
        "Octopus.ControlType": "Select",
        "Octopus.SelectOptions": "awsiam|AWS EC2 IAM Role\nazuremanagedidentity|Azure Managed Identity\ngcpserviceaccount|GCP Service Account\nusernamepassword|Username\\Password\nwindowsauthentication|Windows Authentication"
      }
    },
    {
      "Id": "e1cb49a8-b965-4827-9f8e-01724d263541",
      "Name": "Flyway.Database.User",
      "Label": "-User",
      "HelpText": "**Optional**\n\nThe [user](https://flywaydb.org/documentation/configuration/parameters/user) used to connect to the database.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "51fab335-82b2-4e58-8d4a-e2640ba66296",
      "Name": "Flyway.Database.User.Password",
      "Label": "-Password",
      "HelpText": "**Optional**\n\nThe [password](https://flywaydb.org/documentation/configuration/parameters/password) used to connect to the database.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "Sensitive"
      }
    },
    {
      "Id": "f8787222-c404-47ca-9f0f-32fad6c656e7",
      "Name": "Flyway.Command.Schemas",
      "Label": "-Schemas",
      "HelpText": "**Optional**\n\nComma-separated case-sensitive list of [schemas](https://flywaydb.org/documentation/configuration/parameters/schemas) managed by Flyway. \n\nExample: `schema1,schema2`\n\nFlyway will attempt to create these schemas if they do not already exist and will clean them in the order of this list. If Flyway created them, then the schemas themselves will be dropped when cleaning.\n\nThe first schema in the list will act as the default schema.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "27a4fafe-8e8b-4d6d-b25a-0953535ef2c9",
      "Name": "Flyway.Command.Target",
      "Label": "-Target",
      "HelpText": "**Optional** for `migrate`, `info`, `validate`, `check`, and `undo`.  **Ignored** for all other commands\n\nThe [target version](https://flywaydb.org/documentation/configuration/parameters/target) up to which Flyway should consider migrations. If set to a value other than current or latest, this must be a valid migration version (e.g. `2.1`).\n\nWhen migrating forwards, Flyway will apply all migrations up to and including the target version. Migrations with a higher version number will be ignored. If the target is `current`, then no versioned migrations will be applied but repeatable migrations will be, together with any callbacks.\n\nWhen undoing migrations, Flyway will apply all undo scripts up to and including the target version. Undo scripts with a lower version number will be ignored. Specifying a target version should be done with care, as undo scripts typically destroy database objects.\n\nSpecial Values:\n- `current`: designates the current version of the schema\n- `latest`: the latest version of the schema, as defined by the migration with the highest version\n\nDefault is: `latest`.  When running the `undo` command, this will switch to `current` if `latest` is supplied.",
      "DefaultValue": "latest",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "1b0b700e-6686-4474-a630-8a89aea9b1d5",
      "Name": "Flyway.Command.CherryPick",
      "Label": "-CherryPick",
      "HelpText": "**Optional** for `migrate`, `info`, `check`, and `validate`.  **Ignored** for all other commands.\n\nA Comma separated list of migrations that Flyway should consider when migrating, undoing, or repairing. Migrations are considered in the order that they are supplied, overriding the default ordering. Leave blank to consider all discovered migrations.\n\nEach item in the list must either be a valid migration version (e.g `2.1`) or a valid migration description (e.g. `create_table`).\n\nSee [documentation](https://flywaydb.org/documentation/configuration/parameters/cherryPick) for more details.\n\nThe default is an empty string, meaning this command-line argument will be skipped.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "79a5ed66-eae8-4b0b-89fa-5da10850bb8d",
      "Name": "Flyway.Command.OutOfOrder",
      "Label": "-OutOfOrder",
      "HelpText": "**Optional** for `migrate`, `info`, `check`, and `validate`.  **Ignored** for all other commands.\n\nAllows migrations to be run “out of order”.\n\nIf you already have versions `1.0` and `3.0` applied, and now a version `2.0` is found, it will be applied too instead of being ignored.\n\nSee [documentation](https://flywaydb.org/documentation/configuration/parameters/outOfOrder) for more details.\n\nThe default is `False`.",
      "DefaultValue": "false",
      "DisplaySettings": {
        "Octopus.ControlType": "Select",
        "Octopus.SelectOptions": "true|True\nfalse|False"
      }
    },
    {
      "Id": "1d234d38-35d7-4b5e-a3fb-7ed81fa41d46",
      "Name": "Flyway.Command.SkipExecutingMigrations",
      "Label": "-SkipExecutingMigrations",
      "HelpText": "**Optional** for `migrate`.  **Ignored** for all other commands.\n\nWhether Flyway should skip migration execution. The remainder of the operation will run as normal - including updating the schema history table, callbacks, and so on.\n\n`skipExecutingMigrations` essentially allows you to mimic a migration being executed, because the schema history table is still updated as normal.\n\n`skipExecutingMigrations` can be used to bring an out-of-process change into Flyway’s change control process. For instance, a script run against the database outside of Flyway (like a hotfix) can be turned into a migration. The hotfix migration can be deployed with Flyway with `skipExecutingMigrations=true`. The schema history table will be updated with the new migration, but the script itself won’t be executed again.\n\n`skipExecutingMigrations` can be used with cherryPick to skip specific migrations.\n\nSee [documentation](https://flywaydb.org/documentation/configuration/parameters/skipExecutingMigrations) for more details.\n\nThe default value is `False`.",
      "DefaultValue": "false",
      "DisplaySettings": {
        "Octopus.ControlType": "Select",
        "Octopus.SelectOptions": "true|True\nfalse|False"
      }
    },
    {
      "Id": "7e00397a-e18e-44b8-bb4d-e89b7c70d760",
      "Name": "Flyway.Command.PlaceHolders",
      "Label": "-PlaceHolders",
      "HelpText": "**Optional** for `migrate`, `info`, `validate`, `undo`, `check`, and `repair`.  **Ignored** for all other commands.\n\n[Placeholders](https://flywaydb.org/documentation/configuration/placeholder) to replace in SQL migrations.\n\nEach new line represents a new placeholder.  This will only work with string variable types, text and sensitive values.    \n\nImagine this SQL Script\n\n```\nINSERT INTO ${Key1} (name) VALUES ('Mr. T')\nGRANT SELECT ON SCHEMA ${flyway:defaultSchema} TO ${Key2}\n```\n\nUse the format **Name::Value**  for example:\n\n```\nKey1::My Super Awesome Value\n\nKey2::Other Super Awesome Value\n```\n\nThis will replace `${Key1}` with `My Super Awesome Value` and `${Key2}` with `Other Super Awesome Value`.\n",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "MultiLineText"
      }
    },
    {
      "Id": "3703d1da-1750-4c4f-89ba-04f5297ba0b9",
      "Name": "Flyway.Command.InfoSinceDate",
      "Label": "-InfoSinceDate",
      "HelpText": "**Optional** with the `info` command and [Flyway Teams](https://flywaydb.org/documentation/usage/commandline/info#filtering-output) and Flyway Enterprise editions.  **Ignored** for all other commands.\n\nLimits info to show only migrations applied after this date, and any unapplied migrations. Must be in the format `dd/MM/yyyy HH:mm` (e.g. `01/12/2020 13:00`)",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "0bfecdcd-ad27-4908-949c-a54745aa85f0",
      "Name": "Flyway.Command.InfoSinceVersion",
      "Label": "-InfoSinceVersion",
      "HelpText": "**Optional** with the `info` command and [Flyway Teams](https://flywaydb.org/documentation/usage/commandline/info#filtering-output) and Flyway Enterprise editions.  **Ignored** for all other commands.\n\nLimits info to show only migrations greater than or equal to this version, and any repeatable migrations. (e.g `1.1`)",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "4d4c2fc6-f4a4-4c68-bb79-9ef4d28e2f53",
      "Name": "Flyway.Command.BaseLineOnMigrate",
      "Label": "-baselineOnMigrate",
      "HelpText": "**Optional** when using `migrate`.  **Ignored** for all other commands.\n\nWhether to automatically call baseline when migrate is executed against a non-empty schema with no metadata table. This schema will then be baselined with the baselineVersion before executing the migrations. Only migrations above baselineVersion will then be applied.\n\nThis is useful for initial Flyway production deployments on projects with an existing DB.\n\nBe careful when enabling this as it removes the safety net that ensures Flyway does not migrate the wrong database in case of a configuration mistake!",
      "DefaultValue": "false",
      "DisplaySettings": {
        "Octopus.ControlType": "Select",
        "Octopus.SelectOptions": "true|True\nfalse|False"
      }
    },
    {
      "Id": "f7e23a32-73a8-4118-8996-354d48f176cb",
      "Name": "Flyway.Command.BaselineVersion",
      "Label": "-baselineVersion",
      "HelpText": "**Required** when using `Baseline` or when `BaselineOnMigrate` is set to `True`.  **Ignored** for all other commands.\n\nThe version to tag an existing schema with when executing [baseline](https://flywaydb.org/documentation/command/baseline).",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "c8a118ef-bdc4-4e2f-966b-596662321d1c",
      "Name": "Flyway.Command.BaselineDescription",
      "Label": "-baseLineDescription",
      "HelpText": "**Required** when using `Baseline`.  **Ignored** for all other commands.\n\nThe Description to tag an existing schema with when executing [baseline](https://flywaydb.org/documentation/command/baseline).",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "d3bbe139-2bd4-4242-be70-a6f05510042b",
      "Name": "Flyway.Command.Locations",
      "Label": "-Locations",
      "HelpText": "**Optional** with the `info`,  `migrate`, `repair`, `undo`, `check`, and `validate` commands. \n\nSpecifies the location of the script files to execute.  If left blank, the `Extracted Path` of the package will be used.\n\nExample: filesystem:`#{Octopus.Action.Package[Flyway.Package.Value].ExtractedPath}/MySubFolder`",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "57611c50-3f3f-4a9d-a6c4-02517d2f238e",
      "Name": "Flyway.Command.CheckBuildUrl",
      "Label": "-check.BuildUrl",
      "HelpText": "**Required** when using `Check Changes` or `Check Drift` commands and `-snapshot.FileName` is empty.  \n\n**Please Note:** When `-snapshot.FileName` is not empty, this parameter is ignored.\n\nFlyway uses the URL to an existing database as a temporary database to perform the check logic against.  Flyway will clean this database, so if you specify a full database, you must ensure it is okay for Flyway to erase its schema.\n\nExamples:\n- SQL Server: `jdbc:sqlserver://host:port;databaseName=database`\n- Oracle: `jdbc:oracle:thin:@//host:port/service` or `jdbc:oracle:thin:@tns_entry`\n- MySQL: `jdbc:mysql://host:port/database`\n- PostgreSQL: `jdbc:postgresql://host:port/database`\n- SQLite: `jdbc:sqlite:database`\n\nPlease refer to [documentation](https://flywaydb.org/documentation/database/sqlserver) for further examples.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "b52238f0-a7e6-4cfa-851f-f1f9fdb70c65",
      "Name": "Flyway.Database.Check.User",
      "Label": "-check.BuildUser",
      "HelpText": "**Optional**\n\nThe username of the user for the build database.  Use this if the build database needs a separate username and password from the authentication information supplied above.\n\n**Please Note:** When `-snapshot.FileName` is not empty, this parameter is ignored.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "14f7c856-6ca2-4319-84b3-e96a40d65c10",
      "Name": "Flyway.Database.Check.User.Password",
      "Label": "-check.BuildPassword",
      "HelpText": "**Optional**\n\nThe password of the user for the build database.  Use this if the build database needs a separate username and password from the authentication information supplied above.\n\n**Please Note:** When `-snapshot.FileName` is not empty, this parameter is ignored.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "Sensitive"
      }
    },
    {
      "Id": "4bcdced9-2397-4423-bdc2-776ff80f78f3",
      "Name": "Flyway.Command.FailOnDrift",
      "Label": "-check.failOnDrift",
      "HelpText": "**Required** when using `Check Drift`.  Ignored for all other commands.\n\nIndicates if Flyway will terminate with a non-zero return code if drift is detected.  When using `Check Drift` with a snapshot and you want to see the changes, set this to `false`",
      "DefaultValue": "true",
      "DisplaySettings": {
        "Octopus.ControlType": "Select",
        "Octopus.SelectOptions": "true|True\nfalse|False"
      }
    },
    {
      "Id": "22bb9c1b-1ce4-4f63-b618-d642c804d3ac",
      "Name": "Flyway.Command.Snapshot.FileName",
      "Label": "-snapshot.FileName",
      "HelpText": "**Required** when using `Snapshot`.  **Optional** when using the `Check Drift` command.  **Ignored** for all other commands.\n\nThe name, including the path, of the snapshot file to create or use.  \n\nIf this parameter is populated the `-check.BuildUser`, `check.BuildPassword`, and `-check.BuildUrl` parameters are all ignored.\n\n**Hint:** If executing this step in an execution container, consider `../../../` as the path prefix as the script will execute in `/etc/octopus/default/Work/[Random Folder Name]/Flyway.Package.Value`.  `/etc/octopus/default` is mounted as a volume.  Or leverage a NAS or other file share.\n\n**Important:** You will need this file for future steps. It must be stored in a non-temporary location.  By default, Octopus runs this script in a temporary location on a worker.  That location is deleted automatically once this step is complete.  If you do not supply a path, the file will be deleted.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "ff9a4e49-d3f0-45ff-891a-3e5f6b72806d",
      "Name": "Flyway.Additional.Arguments",
      "Label": "Additional arguments",
      "HelpText": "Any additional arguments that need to be passed (ie `-table=\"MyTable\")",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    }
  ],
  "Properties": {
    "Octopus.Action.Script.ScriptSource": "Inline",
    "Octopus.Action.Script.Syntax": "PowerShell",
    "Octopus.Action.Script.ScriptBody": "$VerboseActionPreference=\"Continue\"\n\nfunction Get-FlywayExecutablePath\n{\n\tparam (\n    \t$providedPath\n    )\n    \n    if ([string]::IsNullOrWhiteSpace($providedPath) -eq $false)\n    {\n    \tWrite-Host \"The executable path was provided, testing to see if it is absolute or relative\"\n\t\tif ([IO.Path]::IsPathRooted($providedPath))\n        {\n        \tWrite-Host \"The provided path is absolute, using that\"\n            \n        \treturn $providedPath\n        }\n        \n        Write-Host \"The provided path was relative, combining $(Get-Location) with $providedPath\"\n        return Join-Path $(Get-Location) $providedPath\n    }\n    \n    Write-Host \"Checking to see if we are currently running on Linux\"\n    if ($IsLinux)    \n    {\n    \tWrite-Host \"Currently running on Linux\"\n    \tWrite-Host \"Checking to see if flyway was included with the package\"\n    \tif (Test-Path \"./flyway\")\n        {\n        \tWrite-Host \"It was, using that version of flyway\"\n        \treturn \"flyway\"\n        }\n        \n        Write-Host \"Testing to see if we are on an execution container with /flyway/flyway as the path\"\n    \tif (Test-Path \"/flyway/flyway\")\n        {\n        \tWrite-Host \"We are, using /flyway/flyway\"\n        \treturn \"/flyway/flyway\"\n        }               \n    }\n    \n    Write-Host \"Currently running on Windows\"\n    \n    Write-Host \"Testing to see if flyway.cmd was included with the package\"\n    if (Test-Path \".\\flyway.cmd\")\n    {\n    \tWrite-Host \"It was, using that version.\"\n    \treturn \".\\flyway.cmd\"\n    }\n    \n    Write-Host \"Testing to see if flyway can be found in the env path\"\n    $flywayExecutable = (Get-Command \"flyway\" -ErrorAction SilentlyContinue)\n    if ($null -ne $flywayExecutable)\n    {\n    \tWrite-Host \"The flyway folder is part of the environment path\"\n        return $flywayExecutable.Source\n    }\n    \n    Fail-Step \"Unable to find flyway executable.  Please include it as part of the package, or provide the path to it.\"\n}\n\nfunction Test-AddParameterToCommandline\n{\n\tparam (\n    \t$acceptedCommands,\n        $selectedCommand,\n        $parameterValue,\n        $defaultValue,\n        $parameterName\n    )\n    \n    if ([string]::IsNullOrWhiteSpace($parameterValue) -eq $true)\n    {    \t\n    \tWrite-Verbose \"$parameterName is empty, returning false\"\n    \treturn $false\n    }\n    \n    if ([string]::IsNullOrWhiteSpace($defaultValue) -eq $false -and $parameterValue.ToLower().Trim() -eq $defaultValue.ToLower().Trim())\n    {\n    \tWrite-Verbose \"$parameterName is matches the default value, returning false\"\n    \treturn $false\n    }\n    \n    if ([string]::IsNullOrWhiteSpace($acceptedCommands) -eq $true -or $acceptedCommands -eq \"any\")\n    {\n    \tWrite-Verbose \"$parameterName has a value and this is for any command, returning true\"\n    \treturn $true\n    }\n    \n    $acceptedCommandArray = $acceptedCommands -split \",\"\n    foreach ($command in $acceptedCommandArray)\n    {\n    \tif ($command.ToLower().Trim() -eq $selectedCommand.ToLower().Trim())\n        {\n        \tWrite-Verbose \"$parameterName has a value and the current command $selectedCommand matches the accepted command $command, returning true\"\n        \treturn $true\n        }\n    }\n    \n    Write-Verbose \"$parameterName has a value but is not accepted in the current command, returning false\"\n    return $false\n}\n\nfunction Get-ParsedUrl\n{\n\t# Define parameters\n    param (\n    \t$ConnectionUrl\n    )\n    \n    # Remove the 'jdbc:' portion from the $ConnectionUrl parameter\n    $ConnectionUrl = $ConnectionUrl.ToLower().Replace(\"jdbc:\", \"\")\n    \n    # Parse and return the url\n    return [System.Uri]$ConnectionUrl\n}\n\n# Declaring the path to the NuGet package\n$flywayPackagePath = $OctopusParameters[\"Octopus.Action.Package[Flyway.Package.Value].ExtractedPath\"]\n$flywayUrl = $OctopusParameters[\"Flyway.Target.Url\"]\n$flywayUser = $OctopusParameters[\"Flyway.Database.User\"]\n$flywayUserPassword = $OctopusParameters[\"Flyway.Database.User.Password\"]\n$flywayCommand = $OctopusParameters[\"Flyway.Command.Value\"]\n$flywayLicenseKey = $OctopusParameters[\"Flyway.License.Key\"]\n$flywayExecutablePath = $OctopusParameters[\"Flyway.Executable.Path\"]\n$flywaySchemas = $OctopusParameters[\"Flyway.Command.Schemas\"]\n$flywayTarget = $OctopusParameters[\"Flyway.Command.Target\"]\n$flywayInfoSinceDate = $OctopusParameters[\"Flyway.Command.InfoSinceDate\"]\n$flywayInfoSinceVersion = $OctopusParameters[\"Flyway.Command.InfoSinceVersion\"]\n$flywayLicensedEdition = $OctopusParameters[\"Flyway.License.Version\"]\n$flywayCherryPick = $OctopusParameters[\"Flyway.Command.CherryPick\"]\n$flywayOutOfOrder = $OctopusParameters[\"Flyway.Command.OutOfOrder\"]\n$flywaySkipExecutingMigrations = $OctopusParameters[\"Flyway.Command.SkipExecutingMigrations\"]\n$flywayPlaceHolders = $OctopusParameters[\"Flyway.Command.PlaceHolders\"]\n$flywayBaseLineVersion = $OctopusParameters[\"Flyway.Command.BaselineVersion\"]\n$flywayBaselineDescription = $OctopusParameters[\"Flyway.Command.BaselineDescription\"]\n$flywayAuthenticationMethod = $OctopusParameters[\"Flyway.Authentication.Method\"]\n$flywayLocations = $OctopusParameters[\"Flyway.Command.Locations\"]\n$flywayAdditionalArguments = $OctopusParameters[\"Flyway.Additional.Arguments\"]\n$flywayStepName = $OctopusParameters[\"Octopus.Action.StepName\"]\n$flywayEnvironment = $OctopusParameters[\"Octopus.Environment.Name\"]\n$flywayCheckBuildUrl = $OctopusParameters[\"Flyway.Command.CheckBuildUrl\"]\n$flywayCheckBuildUsername = $OctopusParameters[\"Flyway.Database.Check.User\"]\n$flywayCheckBuildPassword = $OctopusParameters[\"Flyway.Database.Check.User.Password\"]\n$flywayBaselineOnMigrate = $OctopusParameters[\"Flyway.Command.BaseLineOnMigrate\"]\n$flywaySnapshotFileName = $OctopusParameters[\"Flyway.Command.Snapshot.FileName\"]\n$flywayCheckFailOnDrift = $OctopusParameters[\"Flyway.Command.FailOnDrift\"]\n\nif ([string]::IsNullOrWhitespace($flywayLocations))\n{\n\t$flywayLocations = \"filesystem:$flywayPackagePath\"\n}\n\n\n# Logging for troubleshooting\nWrite-Host \"*******************************************\"\nWrite-Host \"Logging variables:\"\nWrite-Host \" - - - - - - - - - - - - - - - - - - - - -\"\nWrite-Host \"PackagePath: $flywayPackagePath\"\nWrite-Host \"Flyway Executable Path: $flywayExecutablePath\"\nWrite-Host \"Flyway Command: $flywayCommand\"\nWrite-Host \"-url: $flywayUrl\"\nWrite-Host \"-user: $flywayUser\"\nWrite-Host \"-schemas: $flywaySchemas\"\nWrite-Host \"-target: $flywayTarget\"\nWrite-Host \"-cherryPick: $flywayCherryPick\"\nWrite-Host \"-outOfOrder: $flywayOutOfOrder\"\nWrite-Host \"-skipExecutingMigrations: $flywaySkipExecutingMigrations\"\nWrite-Host \"-infoSinceDate: $flywayInfoSinceDate\"\nWrite-Host \"-infoSinceVersion: $flywayInfoSinceVersion\"\nWrite-Host \"-baselineOnMigrate: $flywayBaselineOnMigrate\"\nWrite-Host \"-baselineVersion: $flywayBaselineVersion\"\nWrite-Host \"-baselineDescription: $flywayBaselineDescription\"\nWrite-Host \"-locations: $flywayLocations\"\nWrite-Host \"-check.BuildUrl: $flywayCheckBuildUrl\"\nWrite-Host \"-check.failOnDrift: $flywayCheckFailOnDrift\"\nWrite-Host \"-snapshot.FileName OR check.DeployedSnapshot: $flywaySnapshotFileName\"\nWrite-Host \"Additional Arguments: $flywayAdditionalArguments\"\nWrite-Host \"placeHolders: $flywayPlaceHolders\"\nWrite-Host \"*******************************************\"\n\nif ($null -eq $IsWindows) {\n    Write-Host \"Determining Operating System...\"\n    $IsWindows = ([System.Environment]::OSVersion.Platform -eq \"Win32NT\")\n    $IsLinux = ([System.Environment]::OSVersion.Platform -eq \"Unix\")\n}\n\nWrite-Host \"Setting execution location to: $flywayPackagePath\"\nSet-Location $flywayPackagePath\n\n$flywayCmd = Get-FlywayExecutablePath -providedPath $flywayExecutablePath\n\n$commandToUse = $flywayCommand\nif ($flywayCommand -eq \"migrate dry run\")\n{\n\t$commandToUse = \"migrate\"\n}\n\nif ($flywayCommand -eq \"check dry run\" -or $flywayCommand -eq \"check changes\" -or $flywayCommand -eq \"check drift\")\n{\n\t$commandToUse = \"check\"\n}\n\n$arguments = @(\n\t$commandToUse    \n)\n\nif ($flywayCommand -eq \"check dry run\")\n{\n\t$arguments += \"-dryrun\"\n}\n\nif ($flywayCommand -eq \"check changes\")\n{\n\t$arguments += \"-changes\"\n    $arguments += \"-dryrun\"\n}\n\nif ($flywayCommand -eq \"check drift\")\n{\n\t$arguments += \"-drift\"\n}\n\n# Deteremine authentication method\nswitch ($flywayAuthenticationMethod)\n{\n\t\"awsiam\"\n    {\n\t\t# Check to see if OS is Windows and running in a container\n        if ($IsWindows -and $env:DOTNET_RUNNING_IN_CONTAINER)\n        {\n        \tthrow \"IAM Role authentication is not supported in a Windows container.\"\n        }\n\n\t\t# Get parsed connection string url\n        $parsedUrl = Get-ParsedUrl -ConnectionUrl $flywayUrl\n        \n        # Region is part of the RDS endpoint, extract\n        $region = ($parsedUrl.Host.Split(\".\"))[2]\n\n\t\tWrite-Host \"Generating AWS IAM token ...\"\n\t\t$flywayUserPassword = (aws rds generate-db-auth-token --hostname $parsedUrl.Host --region $region --port $parsedUrl.Port --username $flywayUser)\n\n\t\t$arguments += \"-user=`\"$flywayUser`\"\"\n    \t$arguments += \"-password=`\"$flywayUserPassword`\"\"\n\n\t\tbreak\n    }\n\t\"azuremanagedidentity\"\n    {\n\t\t# Check to see if OS is Windows and running in a container\n        if ($IsWindows -and $env:DOTNET_RUNNING_IN_CONTAINER)\n        {\n        \tthrow \"Azure Managed Identity is not supported in a Windows container.\"\n        }\n        \n        # SQL Server driver doesn't assign password\n        if (!$flywayUrl.ToLower().Contains(\"jdbc:sqlserver:\"))\n        {        \n          # Get login token\n          Write-Host \"Generating Azure Managed Identity token ...\"\n          $token = Invoke-RestMethod -Method GET -Uri \"http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://ossrdbms-aad.database.windows.net\" -Headers @{\"MetaData\" = \"true\"} -UseBasicParsing\n\n          $flywayUserPassword = $token.access_token\n          $arguments += \"-password=`\"$flywayUserPassword`\"\"\n          $arguments += \"-user=`\"$flywayUser`\"\"\n        }\n        else\n        {\n            \n\t\t\t# Check to see if the querstring parameter for Azure Managed Identity is present\n            if (!$flywayUrl.ToLower().Contains(\"authentication=activedirectorymsi\"))\n            {\n                # Add the authentication piece to the jdbc url\n                if (!$flywayUrl.EndsWith(\";\"))\n                {\n                \t# Add the separator\n                    $flywayUrl += \";\"\n                }\n                \n                # Add authentication piece\n                $flywayUrl += \"Authentication=ActiveDirectoryMSI\"\n            }\n        }\n        \n        break\n    }\n    \"gcpserviceaccount\"\n    {\n\t\t# Check to see if OS is Windows and running in a container\n        if ($IsWindows -and $env:DOTNET_RUNNING_IN_CONTAINER)\n        {\n        \tthrow \"GCP Service Account authentication is not supported in a Windows container.\"\n        }\n    \n        # Define header\n        $header = @{ \"Metadata-Flavor\" = \"Google\"}\n\n        # Retrieve service accounts\n        $serviceAccounts = Invoke-RestMethod -Method Get -Uri \"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/\" -Headers $header -UseBasicParsing\n\n        # Results returned in plain text format, get into array and remove empty entries\n        $serviceAccounts = $serviceAccounts.Split([Environment]::NewLine, [StringSplitOptions]::RemoveEmptyEntries)\n\n        # Retreive the specific service account assigned to the VM\n        $serviceAccount = $serviceAccounts | Where-Object {$_.ToLower().Contains(\"iam.gserviceaccount.com\") }\n\n\t\tWrite-Host \"Generating GCP IAM token ...\"\n        # Retrieve token for account\n        $token = Invoke-RestMethod -Method Get -Uri \"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/$serviceAccount/token\" -Headers $header -UseBasicParsing\n        \n        $flywayUserPassword = $token.access_token\n        \n        $arguments += \"-user=`\"$flywayUser`\"\"\n        $arguments += \"-password=`\"$flywayUserPassword`\"\"\n        #$env:FLYWAY_PASSWORD = $flywayUserPassword\n        \n        break\n    }   \n    \"usernamepassword\"\n    {\n    \t# Add password\n        Write-Host \"Testing for parameters that can be applied to any command\"\n        if (Test-AddParameterToCommandline -parameterValue $flywayUser -acceptedCommands \"any\" -selectedCommand $flywayCommand -parameterName \"-user\")\n        {\n            Write-Host \"User provided, adding user and password command line argument\"\n            $arguments += \"-user=`\"$flywayUser`\"\"\n            $arguments += \"-password=`\"$flywayUserPassword`\"\"\n        }\n        \n        break\n    }\n    \"windowsauthentication\"\n    {\n    \t# Display to the user they've selected windows authentication.  Though this is dictated by the jdbc url, this is added to make sure the user knows that's what is\n        # being used\n        Write-Host \"Using Windows Authentication\"\n        \n        # Check for integratedauthentication=true in url\n        if (!$flywayUrl.ToLower().Contains(\"integratedsecurity=true\"))\n        {\n        \t# Check to see if the connection url ends with a ;\n            if (!$flywayUrl.EndsWith(\";\"))\n            {\n            \t# Add the ;\n                $flywayUrl += \";\"\n            }\n            \n            $flywayUrl += \"integratedSecurity=true;\"\n        }\n        break\n    }\n}\n\n$arguments += \"-url=`\"$flywayUrl`\"\"\n$arguments += \"-locations=`\"$flywayLocations`\"\"\n\nif (Test-AddParameterToCommandline -parameterValue $flywaySchemas -acceptedCommands \"any\" -selectedCommand $flywayCommand -parameterName \"-schemas\")\n{\n\tWrite-Host \"Schemas provided, adding schemas command line argument\"\n\t$arguments += \"-schemas=`\"$flywaySchemas`\"\"    \n}\n\nif (Test-AddParameterToCommandline -parameterValue $flywayLicenseKey -acceptedCommands \"any\" -selectedCommand $flywayCommand -parameterName \"-licenseKey\")\n{\n\tWrite-Host \"License key provided, adding -licenseKey command line argument\"\n\t$arguments += \"-licenseKey=`\"$flywayLicenseKey`\"\"                  \n}\nWrite-Host \"Finished testing for parameters that can be applied to any command, moving onto command specific parameters\"\n\nif (Test-AddParameterToCommandline -parameterValue $flywayCherryPick -acceptedCommands \"migrate,info,validate,check\" -selectedCommand $flywayCommand -parameterName \"-cherryPick\")\n{\n\tWrite-Host \"Cherry pick provided, adding cherry pick command line argument\"\n\t$arguments += \"-cherryPick=`\"$flywayCherryPick`\"\"    \n}\n\nif (Test-AddParameterToCommandline -parameterValue $flywayOutOfOrder -defaultValue \"false\" -acceptedCommands \"migrate,info,validate,check\" -selectedCommand $commandToUse -parameterName \"-outOfOrder\")\n{\n\tWrite-Host \"Out of order is not false, adding out of order command line argument\"\n\t$arguments += \"-outOfOrder=`\"$flywayOutOfOrder`\"\"    \n}\n\nif (Test-AddParameterToCommandline -parameterValue $flywayPlaceHolders -acceptedCommands \"migrate,info,validate,undo,repair,check\" -selectedCommand $commandToUse -parameterName \"-placeHolders\")\n{\n\tWrite-Host \"Placeholder parameter provided, adding them to the command line arguments\"\n    \n    $placeHolderValueList = @(($flywayPlaceHolders -Split \"`n\").Trim())\n    foreach ($placeHolder in $placeHolderValueList)\n    {\n    \t$placeHolderSplit = $placeHolder -Split \"::\"\n        $placeHolderKey = $placeHolderSplit[0]\n        $placeHolderValue = $placeHolderSplit[1]\n        Write-Host \"Adding -placeHolders.$placeHolderKey = $placeHolderValue to the argument list\"\n        \n        $arguments += \"-placeholders.$placeHolderKey=`\"$placeHolderValue`\"\"    \n    }   \t\n}\n\nif (Test-AddParameterToCommandline -parameterValue $flywayTarget -acceptedCommands \"migrate,info,validate,undo,check\" -selectedCommand $commandToUse -parameterName \"-target\")\n{\n\tWrite-Host \"Target provided, adding target command line argument\"\n\n\tif ($flywayTarget.ToLower().Trim() -eq \"latest\" -and $flywayCommand -eq \"undo\")\n\t{\n\t\tWrite-Host \"The current target is latest, but the command is undo, changing the target to be current\"\n\t\t$flywayTarget = \"current\"\n\t}\n\n\t$arguments += \"-target=`\"$flywayTarget`\"\"    \n}\n\nif (Test-AddParameterToCommandline -parameterValue $flywaySkipExecutingMigrations -defaultValue \"false\" -acceptedCommands \"migrate\" -selectedCommand $flywayCommand -parameterName \"-skipExecutingMigrations\")\n{\n\tWrite-Host \"Skip executing migrations is not false, adding skip executing migrations command line argument\"\n\t$arguments += \"-skipExecutingMigrations=`\"$flywaySkipExecutingMigrations`\"\"    \n}\n\nif (Test-AddParameterToCommandline -parameterValue $flywayBaselineOnMigrate -defaultValue \"false\" -acceptedCommands \"migrate\" -selectedCommand $flywayCommand -parameterName \"-baselineOnMigrate\")\n{\n\tWrite-Host \"Baseline on migrate is not false, adding the baseline on migrate argument\"\n\t$arguments += \"-baselineOnMigrate=`\"$flywayBaselineOnMigrate`\"\"    \n    \n    if (Test-AddParameterToCommandline -parameterValue $flywayBaselineVersion -acceptedCommands \"migrate\" -selectedCommand $flywayCommand -parameterName \"-baselineVersion\")\n    {\n    \tWrite-Host \"Baseline version has been specified, adding baseline version argument\"\n\t\t$arguments += \"-baselineVersion=`\"$flywayBaselineVersion`\"\"  \n    }\n}\n\nif (Test-AddParameterToCommandline -parameterValue $flywayBaselineVersion -acceptedCommands \"baseline\" -selectedCommand $flywayCommand -parameterName \"-baselineVersion\")\n{\n\tWrite-Host \"Doing a baseline, adding baseline version and description\"\n\t$arguments += \"-baselineVersion=`\"$flywayBaselineVersion`\"\"    \n    $arguments += \"-baselineDescription=`\"$flywayBaselineDescription`\"\"    \n}\n\nif (Test-AddParameterToCommandline -parameterValue $flywayInfoSinceDate -acceptedCommands \"info\" -selectedCommand $flywayCommand -parameterName \"-infoSinceDate\")\n{\n\tWrite-Host \"Info since date has been provided, adding that to the command line arguments\"\n\t$arguments += \"-infoSinceDate=`\"$flywayInfoSinceDate`\"\"\n}\n\nif (Test-AddParameterToCommandline -parameterValue $flywayInfoSinceVersion -acceptedCommands \"info\" -selectedCommand $flywayCommand -parameterName \"-infoSinceVersion\")\n{\n\tWrite-Host \"Info since version has been provided, adding that to the command line arguments\"\n\t$arguments += \"-infoSinceVersion=`\"$flywayInfoSinceVersion`\"\"\n} \n\nif (Test-AddParameterToCommandline -parameterValue $flywaySnapshotFileName -acceptedCommands \"snapshot\" -selectedCommand $commandToUse -parameterName \"-snapshot.filename\")\n{\n\tWrite-Host \"Snapshot filename has been provided, adding that to the command line arguments\"\n    $folderName = Split-Path -Parent $flywaySnapshotFileName\n    if ((test-path $folderName) -eq $false)\n    {\n    \tNew-Item $folderName -ItemType Directory\n    }\n    $arguments += \"-snapshot.filename=`\"$flywaySnapshotFileName`\"\"\n}\n\n$snapshotFileNameforCheckProvided = $false\nif (Test-AddParameterToCommandline -parameterValue $flywaySnapshotFileName -acceptedCommands \"check\" -selectedCommand $commandToUse -parameterName \"-check.deployedSnapshot\")\n{\n\tWrite-Host \"Snapshot filename has been provided for the check command, adding that to the command line arguments\"\n    $folderName = Split-Path -Parent $flywaySnapshotFileName\n    if ((test-path $folderName) -eq $false)\n    {\n    \tNew-Item $folderName -ItemType Directory\n    }\n    $arguments += \"-check.deployedSnapshot=`\"$flywaySnapshotFileName`\"\"\n    $snapshotFileNameforCheckProvided = $true\n}\n\nif ((Test-AddParameterToCommandline -parameterValue $flywayCheckBuildUrl -acceptedCommands \"check\" -selectedCommand $commandToUse -parameterName \"-check.buildUrl\") -eq $true -and $snapshotFileNameforCheckProvided -eq $false)\n{\n\tWrite-Host \"Check build URL has been provided, adding that to the command line arguments\"\n\t$arguments += \"-check.buildUrl=`\"$flywayCheckBuildUrl`\"\"\n}\n\nWrite-Host \"Checking to see if the check username and password were supplied\"\nif ((Test-AddParameterToCommandline -parameterValue $flywayCheckBuildUsername -acceptedCommands \"check\" -selectedCommand $commandToUse -parameterName \"-user\")  -eq $true -and $snapshotFileNameforCheckProvided -eq $false)\n{\n\tWrite-Host \"Check User provided, adding check user and check password command line argument\"\n\t$arguments += \"-check.buildUser=`\"$flywayCheckBuildUsername`\"\"\n\t$arguments += \"-check.buildPassword=`\"$flywayCheckBuildPassword`\"\"\n}\n\nif (Test-AddParameterToCommandline -parameterValue $flywayCheckFailOnDrift -acceptedCommands \"check drift\" -selectedCommand $flywayCommand -parameterName \"-check.failOnDrift\")\n{\n\tWrite-Host \"Doing a check drift command, adding the fail on drift\"\n\t$arguments += \"-check.failOnDrift=`\"$flywayCheckFailOnDrift`\"\"\n}\n\n\nWrite-Host \"Finished checking for command specific parameters, moving onto execution\"\n$dryRunOutputFile = \"\"\n\nif ($flywayCommand -eq \"migrate dry run\")\n{\n\t$dryRunOutputFile = Join-Path $(Get-Location) \"dryRunOutput\"\n    Write-Host \"Adding the argument dryRunOutput so Flyway will perform a dry run and not an actual migration.\"\n    $arguments += \"-dryRunOutput=`\"$dryRunOutputFile`\"\"\n}\n\n# Check to see if there's any additional arguments to add\nif (![string]::IsNullOrWhitespace($flywayAdditionalArguments))\n{\n\t# Split on space\n    $flywayAdditionalArgumentsArray = ($flywayAdditionalArguments.Split(\" \", [System.StringSplitOptions]::RemoveEmptyEntries))\n\n    # Loop through array\n    foreach ($newArgument in $flywayAdditionalArgumentsArray)\n    {\n    \t# Add the arguments\n    \t$arguments += $newArgument\n    }\n}\n\n# Display what's going to be run\nif (![string]::IsNullOrWhitespace($flywayUserPassword))\n{\n    $flywayDisplayArguments = $arguments.PSObject.Copy()\n    $arrayIndex = 0\n    for ($i = 0; $i -lt $flywayDisplayArguments.Count; $i++)\n    {\n        if ($null -ne $flywayDisplayArguments[$i])\n        {\n            if ($flywayDisplayArguments[$i].Contains($flywayUserPassword))\n            {\n                $flywayDisplayArguments[$i] = $flywayDisplayArguments[$i].Replace($flywayUserPassword, \"****\")\n            }\n        }\n    }\n\n    Write-Host \"Executing the following command: $flywayCmd $flywayDisplayArguments\"\n}\nelse\n{\n    Write-Host \"Executing the following command: $flywayCmd $arguments\"\n}\n\n# Attempt to find driver path for java\n$driverPath = (Get-ChildItem -Path (Get-ChildItem -Path $flywayCmd).Directory -Recurse | Where-Object {$_.PSIsContainer -eq $true -and $_.Name -eq \"drivers\"})\n\n# If found, add driver path to the PATH environment varaible\nif ($null -ne $driverPath)\n{\n\t$env:PATH += \"$([IO.Path]::PathSeparator)$($driverPath.FullName)\"\n}\n\n# Adjust call to flyway command based on OS\nif ($IsLinux)\n{\n    & bash $flywayCmd $arguments\n}\nelse\n{\n    & $flywayCmd $arguments\n}\n\n# Check exit code\nif ($lastExitCode -ne 0)\n{\n\t# Fail the step\n    Write-Error \"Execution of Flyway failed!\"\n}\n\n$currentDate = Get-Date\n$currentDateFormatted = $currentDate.ToString(\"yyyyMMdd_HHmmss\")\n\n# Check to see if the dry run variable has a value\nif (![string]::IsNullOrWhitespace($dryRunOutputFile))\n{     \n    $sqlDryRunFile = \"$($dryRunOutputFile).sql\"\n    $htmlDryRunFile = \"$($dryRunOutputFile).html\"\n    \n    if (Test-Path $sqlDryRunFile)\n    {\n    \tNew-OctopusArtifact -Path $sqlDryRunFile -Name \"$($flywayStepName)_$($flywayEnvironment)_$($currentDateFormatted)_dryRunOutput.sql\"\n    }\n    \n    if (Test-Path $htmlDryRunFile)\n    {\n    \tNew-OctopusArtifact -Path $htmlDryRunFile -Name \"$($flywayStepName)_$($flywayEnvironment)_$($currentDateFormatted)_dryRunOutput.html\"\n    }\n}\n\n$reportFile = Join-Path $(Get-Location) \"report.html\"\n    \nif (Test-Path $reportFile)\n{\n  \tNew-OctopusArtifact -Path $reportFile -Name \"$($flywayStepName)_$($flywayEnvironment)_$($currentDateFormatted)_report.html\"\n}",
    "Octopus.Action.PowerShell.Edition": "Core",
    "Octopus.Action.EnabledFeatures": "Octopus.Features.SelectPowerShellEditionForWindows"
  },
  "Category": "Flyway",
  "HistoryUrl": "https://github.com/OctopusDeploy/Library/commits/master/step-templates//opt/buildagent/work/75443764cd38076d/step-templates/flyway-database-migrations.json",
  "Website": "/step-templates/ccebac39-79a8-4ab4-b55f-19ea570d9ebc",
  "Logo": "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",
  "$Meta": {
    "Type": "ActionTemplate"
  }
}

History

Page updated on Tuesday, March 5, 2024