diff --git a/step-templates/grate-database-migration.json b/step-templates/grate-database-migration.json index 6eb60ee4..e2298a22 100644 --- a/step-templates/grate-database-migration.json +++ b/step-templates/grate-database-migration.json @@ -3,7 +3,7 @@ "Name": "grate Database Migrations", "Description": "Database migrations using [grate](https://github.com/erikbra/grate).\nWith this template you can either include grate with your package or use the `Download grate?` feature to download it at deploy time. If you're downloading, you can choose the version by specifying it in the `Version of grate`.\n\nNOTE: \n - AWS EC2 IAM Role authentication requires the AWS CLI be installed.\n - To run on Linux, the machine must have both PowerShell Core and .NET Core 3.1 installed.", "ActionType": "Octopus.Script", - "Version": 9, + "Version": 10, "CommunityActionTemplateId": null, "Packages": [ { @@ -23,7 +23,7 @@ "Properties": { "Octopus.Action.Script.ScriptSource": "Inline", "Octopus.Action.Script.Syntax": "PowerShell", - "Octopus.Action.Script.ScriptBody": "# Configure template\n\n# Check to see if $IsWindows is available\nif ($null -eq $IsWindows)\n{\n\tWrite-Host \"Determining Operating System...\"\n switch ([System.Environment]::OSVersion.Platform)\n {\n \t\"Win32NT\"\n {\n \t# Set variable\n $IsWindows = $true\n $IsLinux = $false\n }\n \"Unix\"\n {\n \t$IsWindows = $false\n $IsLinux = $true\n }\n }\n}\n\nif ($IsWindows)\n{\n\t$ProgressPreference = 'SilentlyContinue'\n}\n\n# Define parameters\n$grateExecutable = \"\"\n$grateOutputPath = [System.IO.Path]::Combine($OctopusParameters[\"Octopus.Action.Package[gratePackage].ExtractedPath\"], \"output\")\n$grateSsl = [System.Convert]::ToBoolean($grateSsl)\n\nFunction Get-LatestVersionDownloadUrl {\n # Define parameters\n param(\n $Repository,\n $Version\n )\n \n # Define local variables\n $releases = \"https://api.github.com/repos/$Repository/releases\"\n \n # Get latest version\n Write-Host \"Determining latest release of $Repository ...\"\n \n $tags = (Invoke-WebRequest $releases -UseBasicParsing | ConvertFrom-Json)\n \n if ($null -ne $Version) {\n # Get specific version\n $tags = ($tags | Where-Object { $_.tag_name.EndsWith($Version) })\n\n # Check to see if nothing was returned\n if ($null -eq $tags) {\n # Not found\n Write-Host \"No release found matching version $Version, getting highest version using Major.Minor syntax...\"\n\n # Get the tags\n $tags = (Invoke-WebRequest $releases -UseBasicParsing | ConvertFrom-Json)\n\n # Parse the version number into a version object\n $parsedVersion = [System.Version]::Parse($Version)\n $partialVersion = \"$($parsedVersion.Major).$($parsedVersion.Minor)\"\n\n # Filter tags to ones matching only Major.Minor of version specified\n $tags = ($tags | Where-Object { $_.tag_name.Contains(\"$partialVersion.\") -and $_.draft -eq $false })\n \n # Grab the latest\n if ($null -eq $tags)\n {\n \t# decrement minor version\n $minorVersion = [int]$parsedVersion.Minor\n $minorVersion --\n \n # Check to make sure that minor version isn't negative\n if ($minorVersion -ge 0)\n {\n \t# return the urls\n \treturn (Get-LatestVersionDownloadUrl -Repository $Repository -Version \"$($parsedVersion.Major).$($minorVersion)\")\n }\n else\n {\n \t# Display error\n Write-Error \"Unable to find a version within the major version of $($parsedVersion.Major)!\"\n }\n }\n }\n }\n\n # Find the latest version with a downloadable asset\n foreach ($tag in $tags) {\n if ($tag.assets.Count -gt 0) {\n return $tag.assets.browser_download_url\n }\n }\n\n # Return the version\n return $null\n}\n\n# Change the location to the extract path\nSet-Location -Path $OctopusParameters[\"Octopus.Action.Package[gratePackage].ExtractedPath\"]\n\n# Check to see if download is specified\nif ([System.Boolean]::Parse($grateDownloadNuget))\n{\n # Set secure protocols\n [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls11 -bor [System.Net.SecurityProtocolType]::Tls12\n $downloadUrls = @()\n\n\t# Check to see if version number specified\n if ([string]::IsNullOrWhitespace($grateNugetVersion))\n {\n \t# Get the latest version number\n $downloadUrls = Get-LatestVersionDownloadUrl -Repository \"erikbra/grate\"\n }\n else\n {\n \t# Get specific version\n $downloadUrls = Get-LatestVersionDownloadUrl -Repository \"erikbra/grate\" -Version $grateNugetVersion\n }\n\n\t# Check to make sure something was returned\n if ($null -ne $downloadUrls -and $downloadUrls.Length -gt 0)\n\t{\n \n # Check for download folder\n if ((Test-Path -Path \"$PSSCriptRoot/grate\") -eq $false)\n {\n # Create the folder\n New-Item -ItemType Directory -Path \"$PSSCriptRoot/grate\"\n }\n\n # Get URL of grate-dotnet-tool\n $downloadUrl = $downloadUrls | Where-Object {$_.Contains(\"grate-dotnet-tool\")}\n \n # Check to see if something was returned\n if ($null -eq $downloadUrl)\n {\n \t# Attempt to get nuget package\n Write-Host \"An asset with grate-dotnet-tool was not found, attempting to locate nuget package ...\"\n $downloadUrl = $downloadUrls | Where-Object {$_.Contains(\".nupkg\")}\n \n # Check to see if something was returned\n if ($null -eq $downloadUrl)\n {\n \tWrite-Error \"Unable to find appropriate asset for download.\"\n }\n }\n\n # Download nuget package\n Write-Output \"Downloading $downloadUrl ...\"\n\n # Get download file name\n $downloadFile = $downloadUrl.Substring($downloadUrl.LastIndexOf(\"/\") + 1)\n\n # Download the file\n Invoke-WebRequest -Uri $downloadUrl -OutFile \"$PSSCriptRoot/grate/$downloadFile\"\n\n # Check the extension\n if ($downloadFile.EndsWith(\".zip\"))\n {\n # Extract the file\n Write-Host \"Extracting $downloadFile ...\"\n Expand-Archive -Path \"$PSSCriptRoot/grate/$downloadFile\" -Destination \"$PSSCriptRoot/grate\"\n\n # Delete the downloaded .zip\n Remove-Item -Path \"$PSSCriptRoot/grate/$downloadFile\"\n\n # Get extracted files\n $extractedFiles = Get-ChildItem -Path \"$PSSCriptRoot/grate\"\n\n # Check to see if what was extracted was simply a nuget file\n if ($extractedFiles.Count -eq 1 -and $extractedFiles[0].Extension -eq \".nupkg\")\n {\n # Zip file contained a nuget package \n Write-Host \"Archive contained a NuGet package, extracting package ...\"\n $nugetPackage = $extractedFiles[0]\n $nugetPackage | Rename-Item -NewName $nugetPackage.Name.Replace(\".nupkg\", \".zip\")\n Expand-Archive -Path $nugetPackage.FullName.Replace(\".nupkg\", \".zip\") -Destination \"$PSSCriptRoot/grate\"\n }\n }\n\n if ($downloadFile.EndsWith(\".nupkg\"))\n {\n # Zip file contained a nuget package \n $nugetPackage = Get-ChildItem -Path \"$PSSCriptRoot/grate/$($downloadFile)\"\n $nugetPackage | Rename-Item -NewName $nugetPackage.Name.Replace(\".nupkg\", \".zip\")\n Expand-Archive -Path \"$PSSCriptRoot/grate/$($downloadFile.Replace(\".nupkg\", \".zip\"))\" -Destination \"$PSSCriptRoot/grate\" \n }\n }\n else\n {\n \tWrite-Error \"No download url returned!\"\n }\n}\n\n\n\nif ([string]::IsNullOrWhitespace($grateExecutable))\n{\n\t# Look for just grate.dll\n $grateExecutable = Get-ChildItem -Path $PSSCriptRoot -Recurse | Where-Object {$_.Name -eq \"grate.dll\"}\n \n # Check for multiple results\n if ($grateExecutable -is [array])\n {\n # choose one that matches highest version of .net\n\t\t$dotnetVersions = (dotnet --list-runtimes) | Where-Object {$_ -like \"*.NetCore*\"}\n\n\t\t$maxVersion = $null\n\t\tforeach ($dotnetVersion in $dotnetVersions)\n\t\t{\n \t\t$parsedVersion = $dotnetVersion.Split(\" \")[1]\n \t\tif ($null -eq $maxVersion -or [System.Version]::Parse($parsedVersion) -gt [System.Version]::Parse($maxVersion))\n \t\t{\n \t\t$maxVersion = $parsedVersion\n \t\t}\n\t\t}\n \n $grateExecutable = $grateExecutable | Where-Object {$_.FullName -like \"*net$(([System.Version]::Parse($maxVersion).Major))*\"}\n }\n}\n\nif ([string]::IsNullOrWhitespace($grateExecutable))\n{\n # Couldn't find grate\n Write-Error \"Couldn't find the grate executable!\"\n}\n\n# Build the arguments\n$grateSwitches = @()\n\n# Update the connection string based on authentication method\nswitch ($grateAuthenticationMethod)\n{\n \"awsiam\"\n {\n # Region is part of the RDS endpoint, extract\n $region = ($grateServerName.Split(\".\"))[2]\n\n Write-Host \"Generating AWS IAM token ...\"\n $grateUserPassword = (aws rds generate-db-auth-token --hostname $grateServerName --region $region --port $grateServerPort --username $grateUserName) \n $grateUserInfo = \"Uid=$grateUserName;Pwd=$grateUserPassword;\"\n\n break\n }\n\t\n \"azuremanagedidentity\"\n {\n \t# SQL Server driver doesn't assign password\n if ($grateDatabaseServerType -ne \"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\"}\n\n $grateUserPassword = $token.access_token\n $grateUserInfo = \"Uid=$grateUserName;Pwd=$grateUserPassword;\"\n }\n \n break\n }\n\n \"gcpserviceaccount\"\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\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 {$_.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\n $grateUserPassword = $token.access_token\n \n # Append remaining portion of connection string\n $grateUserInfo = \"Uid=$grateUserName;Pwd=$grateUserPassword;\"\n }\n\n\n \"usernamepassword\"\n {\n \t# Append remaining portion of connection string\n $grateUserInfo = \"Uid=$grateUserName;Pwd=$grateUserPassword;\"\n\n\t\tbreak \n\t}\n\n \"windowsauthentication\"\n {\n # Append remaining portion of connection string\n\t $grateUserInfo = \"integrated security=true;\"\n \n # Append username (required for non\n $grateUserInfo += \"Uid=$grateUserName;\"\n }\n \n}\n\n# Configure connnection string based on technology\nswitch ($grateDatabaseServerType)\n{\n \"sqlserver\"\n {\n # Check to see if port has been defined\n if (![string]::IsNullOrEmpty($grateServerPort))\n {\n # Append to servername\n $grateServerName += \",$grateServerPort\"\n\n # Empty the port\n $grateServerPort = [string]::Empty\n }\n }\n \"mariadb\"\n {\n \t$grateServerPort = \"Port=$grateServerPort;Allow User Variables=true;\"\n }\n \"mysql\"\n {\n \t# Use the MySQL client\n $grateDatabaseServerType = \"mariadb\"\n $grateServerPort = \"Port=$grateServerPort;Allow User Variables=true;\"\n }\n \"oracle\"\n {\n \t# Oracle connection strings are built different than all others\n $grateServerConnectionString = \"--connectionstring=`\"Data source=$($grateServerName):$($grateServerPort)/$grateDatabaseName;$($grateUserInfo.Replace(\"Uid\", \"User Id\").Replace(\"Pwd\", \"Password\")) \"\n }\n default\n {\n $grateServerPort = \"Port=$grateServerPort;\"\n }\n}\n\n# Build base connection string\nif ([string]::IsNullOrWhitespace($grateServerConnectionString))\n{\n\t$grateServerConnectionString = \"--connectionstring=`\"Server=$grateServerName;$grateServerPort $grateUserInfo Database=$grateDatabaseName;\"\n}\n\n# Check for SQL Server and Azure Managed Identity\nif (($grateDatabaseServerType -eq \"sqlserver\") -and ($grateAuthenticationMethod -eq \"azuremanagedidentity\"))\n{\n\t# Append AD component to connection string\n $grateServerConnectionString += \"Authentication=Active Directory Default;\"\n}\n\nif ($grateSsl -eq $true)\n{\n\tif (($grateDatabaseServerType -eq \"mariadb\") -or ($grateDatabaseServerType -eq \"mysql\") -or ($grateDatabaseServerType -eq \"postgres\"))\n {\n \t# Add sslmode\n $grateServerConnectionString += \"SslMode=Require;Trust Server Certificate=true;\"\n }\n elseif ($grateDatabaseServerType -eq \"sqlserver\")\n {\n \t$grateServerConnectionString += \"Trust Server Certificate=true;\"\n }\n else\n {\n \tWrite-Warning \"Invalid Database Server Type selection for SSL, ignoring setting.\"\n }\n}\n\n# Add terminating double quote to connection string\n$grateServerConnectionString += \"`\"\"\n\n$grateSwitches += $grateServerConnectionString\n\n$grateSwitches += \"--databasetype=$grateDatabaseServerType\"\n$grateSwitches += \"--silent\"\n\nif ([System.Boolean]::Parse($grateDryRun))\n{\n $grateSwitches += \"--dryrun\"\n}\n\nif ([System.Boolean]::Parse($grateRecordOutput))\n{\n $grateSwitches += \"--outputPath=$grateOutputPath\"\n \n # Check to see if path exists\n if ((Test-Path -Path $grateOutputPath) -eq $false)\n {\n \t# Create folder\n New-Item -Path $grateOutputPath -ItemType \"Directory\"\n }\n}\n\n# Add transaction switch\n$grateSwitches += \"--transaction=$($grateWithTransaction.ToLower())\"\n\n# Add Command Timeout\nif (![string]::IsNullOrEmpty($grateCommandTimeout)){\n $grateSwitches += \"--commandtimeout=$([int]$grateCommandTimeout)\"\n}\n\n# Add Baseline switch\nif ([System.Boolean]::Parse($grateBaseline)) {\n $grateSwitches += \"--baseline\"\n}\n\n# Add SQL Files Directory parameter\nif (![string]::IsNullOrEmpty($grateSqlScriptFolder)) {\n # Add up folder\n $grateSwitches += \"--sqlfilesdirectory=$grateSqlScriptFolder\"\n}\n\n# Add log verbosity flag\nif (![string]::IsNullOrEmpty($grateLogVerbosity)) {\n # Add up folder\n $grateSwitches += \"--verbosity=$grateLogVerbosity\"\n}\n\n\n# Check for version\nif (![string]::IsNullOrEmpty($grateVersion))\n{\n # Add version\n $grateSwitches += \"--version=$grateVersion\"\n}\n\n# Set grate environment\nif (![string]::IsNullOrEmpty($grateEnvironment))\n{\n # Add environment\n $grateSwitches += \"--environment=$grateEnvironment\"\n}\n\n# Set grate schema. Especially useful when migrating from RoundhousE\nif (![string]::IsNullOrEmpty($grateSchema))\n{\n # Add schema\n $grateSwitches += \"--schema=$grateSchema\"\n}\n\n# Display what's going to be run\nif (![string]::IsNullOrWhitespace($grateUserPassword))\n{\n\tWrite-Host \"Executing $($grateExecutable.FullName) with $($grateSwitches.Replace($grateUserPassword, \"****\"))\"\n}\nelse\n{\n\tWrite-Host \"Executing $($grateExecutable.FullName) with $($grateSwitches)\"\n}\n\n# Execute grate\nif ($grateExecutable.FullName.EndsWith(\".dll\"))\n{\n\t& dotnet $grateExecutable.FullName $grateSwitches\n}\nelse\n{\n\t& $grateExecutable.FullName $grateSwitches\n}\n\n# If the output path was specified, attach artifacts\nif ([System.Boolean]::Parse($grateRecordOutput))\n{ \n # Zip up output folder content\n Add-Type -Assembly 'System.IO.Compression.FileSystem'\n \n $zipFile = \"$($OctopusParameters[\"Octopus.Action.Package[gratePackage].ExtractedPath\"])/output.zip\"\n \n\t[System.IO.Compression.ZipFile]::CreateFromDirectory($grateOutputPath, $zipFile)\n New-OctopusArtifact -Path \"$zipFile\" -Name \"output.zip\"\n}\n" + "Octopus.Action.Script.ScriptBody": "# Configure template\n\n# Check to see if $IsWindows is available\nif ($null -eq $IsWindows)\n{\n\tWrite-Host \"Determining Operating System...\"\n switch ([System.Environment]::OSVersion.Platform)\n {\n \t\"Win32NT\"\n {\n \t# Set variable\n $IsWindows = $true\n $IsLinux = $false\n }\n \"Unix\"\n {\n \t$IsWindows = $false\n $IsLinux = $true\n }\n }\n}\n\nif ($IsWindows)\n{\n\t$ProgressPreference = 'SilentlyContinue'\n}\n\n# Define parameters\n$grateExecutable = \"\"\n$grateOutputPath = [System.IO.Path]::Combine($OctopusParameters[\"Octopus.Action.Package[gratePackage].ExtractedPath\"], \"output\")\n$grateSsl = [System.Convert]::ToBoolean($grateSsl)\n\nFunction Get-LatestVersionDownloadUrl {\n # Define parameters\n param(\n $Repository,\n $Version\n )\n \n # Define local variables\n $releases = \"https://api.github.com/repos/$Repository/releases\"\n \n # Get latest version\n Write-Host \"Determining latest release of $Repository ...\"\n \n $tags = (Invoke-WebRequest $releases -UseBasicParsing | ConvertFrom-Json)\n \n if ($null -ne $Version) {\n # Get specific version\n $tags = ($tags | Where-Object { $_.tag_name.EndsWith($Version) })\n\n # Check to see if nothing was returned\n if ($null -eq $tags) {\n # Not found\n Write-Host \"No release found matching version $Version, getting highest version using Major.Minor syntax...\"\n\n # Get the tags\n $tags = (Invoke-WebRequest $releases -UseBasicParsing | ConvertFrom-Json)\n\n # Parse the version number into a version object\n $parsedVersion = [System.Version]::Parse($Version)\n $partialVersion = \"$($parsedVersion.Major).$($parsedVersion.Minor)\"\n\n # Filter tags to ones matching only Major.Minor of version specified\n $tags = ($tags | Where-Object { $_.tag_name.Contains(\"$partialVersion.\") -and $_.draft -eq $false })\n \n # Grab the latest\n if ($null -eq $tags)\n {\n \t# decrement minor version\n $minorVersion = [int]$parsedVersion.Minor\n $minorVersion --\n \n # Check to make sure that minor version isn't negative\n if ($minorVersion -ge 0)\n {\n \t# return the urls\n \treturn (Get-LatestVersionDownloadUrl -Repository $Repository -Version \"$($parsedVersion.Major).$($minorVersion)\")\n }\n else\n {\n \t# Display error\n Write-Error \"Unable to find a version within the major version of $($parsedVersion.Major)!\"\n }\n }\n }\n }\n\n # Find the latest version with a downloadable asset\n foreach ($tag in $tags) {\n if ($tag.assets.Count -gt 0) {\n return $tag.assets.browser_download_url\n }\n }\n\n # Return the version\n return $null\n}\n\n# Change the location to the extract path\nSet-Location -Path $OctopusParameters[\"Octopus.Action.Package[gratePackage].ExtractedPath\"]\n\n$grateVersionNumber = $null\n\n# Check to see if download is specified\nif ([System.Boolean]::Parse($grateDownloadNuget))\n{\n # Set secure protocols\n [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls11 -bor [System.Net.SecurityProtocolType]::Tls12\n $downloadUrls = @()\n\n\t# Check to see if version number specified\n if ([string]::IsNullOrWhitespace($grateNugetVersion))\n {\n \t# Get the latest version number\n $downloadUrls = Get-LatestVersionDownloadUrl -Repository \"erikbra/grate\"\n }\n else\n {\n \t# Get specific version\n $downloadUrls = Get-LatestVersionDownloadUrl -Repository \"erikbra/grate\" -Version $grateNugetVersion\n }\n\n\t# Check to make sure something was returned\n if ($null -ne $downloadUrls -and $downloadUrls.Length -gt 0)\n\t{\n \n # Check for download folder\n if ((Test-Path -Path \"$PSSCriptRoot/grate\") -eq $false)\n {\n # Create the folder\n New-Item -ItemType Directory -Path \"$PSSCriptRoot/grate\"\n }\n\n # Get version from the url\n $grateVersionNumber = $(([Uri]$downloadUrls[0]).Segments[-2])\n $grateVersionNumber = [Version]$grateVersionNumber.Replace(\"/\", \"\")\n\n # Version 1.6.1 was the last version they used the grate-dotnet-tool name for the asset\n if ($grateVersionNumber -le [Version]\"1.6.1\")\n {\n # Get URL of grate-dotnet-tool\n $downloadUrl = $downloadUrls | Where-Object {$_.Contains(\"grate-dotnet-tool\")}\n }\n else\n {\n if ([Environment]::Is64BitOperatingSystem)\n {\n $osArchitectureBit = \"64\" \n }\n else\n {\n $osArchitectureBit = \"32\"\n }\n\n if ($isLinux)\n {\n $osType = \"linux\"\n }\n else\n {\n $osType = \"win\"\n }\n \n $downloadUrl = $downloadUrls | Where-Object {$_.Contains(\"grate-$($osType)-x$($osArchitectureBit)-self-contained-$($grateVersionNumber)\")}\n }\n \n # Check to see if something was returned\n if ($null -eq $downloadUrl)\n {\n \t# Attempt to get nuget package\n Write-Host \"An asset with grate-dotnet-tool was not found, attempting to locate nuget package ...\"\n $downloadUrl = $downloadUrls | Where-Object {$_.Contains(\".nupkg\")}\n \n # Check to see if something was returned\n if ($null -eq $downloadUrl)\n {\n \tWrite-Error \"Unable to find appropriate asset for download.\"\n }\n }\n\n # Download nuget package\n Write-Output \"Downloading $downloadUrl ...\"\n\n # Get download file name\n $downloadFile = $downloadUrl.Substring($downloadUrl.LastIndexOf(\"/\") + 1)\n\n # Download the file\n Invoke-WebRequest -Uri $downloadUrl -OutFile \"$PSSCriptRoot/grate/$downloadFile\"\n\n # Check the extension\n if ($downloadFile.EndsWith(\".zip\"))\n {\n # Extract the file\n Write-Host \"Extracting $downloadFile ...\"\n Expand-Archive -Path \"$PSSCriptRoot/grate/$downloadFile\" -Destination \"$PSSCriptRoot/grate\"\n\n # Delete the downloaded .zip\n Remove-Item -Path \"$PSSCriptRoot/grate/$downloadFile\"\n\n # Get extracted files\n $extractedFiles = Get-ChildItem -Path \"$PSSCriptRoot/grate\"\n\n # Check to see if what was extracted was simply a nuget file\n if ($extractedFiles.Count -eq 1 -and $extractedFiles[0].Extension -eq \".nupkg\")\n {\n # Zip file contained a nuget package \n Write-Host \"Archive contained a NuGet package, extracting package ...\"\n $nugetPackage = $extractedFiles[0]\n $nugetPackage | Rename-Item -NewName $nugetPackage.Name.Replace(\".nupkg\", \".zip\")\n Expand-Archive -Path $nugetPackage.FullName.Replace(\".nupkg\", \".zip\") -Destination \"$PSSCriptRoot/grate\"\n }\n }\n\n if ($downloadFile.EndsWith(\".nupkg\"))\n {\n # Zip file contained a nuget package \n $nugetPackage = Get-ChildItem -Path \"$PSSCriptRoot/grate/$($downloadFile)\"\n $nugetPackage | Rename-Item -NewName $nugetPackage.Name.Replace(\".nupkg\", \".zip\")\n Expand-Archive -Path \"$PSSCriptRoot/grate/$($downloadFile.Replace(\".nupkg\", \".zip\"))\" -Destination \"$PSSCriptRoot/grate\" \n }\n }\n else\n {\n \tWrite-Error \"No download url returned!\"\n }\n}\n\n\n\nif ([string]::IsNullOrWhitespace($grateExecutable))\n{\n # Version 1.6.1 was the last version they used the grate-dotnet-tool name for the asset\n if ($grateVersionNumber -le [Version]\"1.6.1\")\n {\n # Look for just grate.dll\n $grateExecutable = Get-ChildItem -Path $PSSCriptRoot -Recurse | Where-Object {$_.Name -eq \"grate.dll\"}\n }\n else\n {\n # Look for executable depending on OS\n if ($isLinux)\n {\n $grateExecutable = Get-ChildItem -Path $PSSCriptRoot -Recurse | Where-Object {$_.Name -eq \"grate\"} | Where-Object { ! $_.PSIsContainer }\n }\n else\n {\n $grateExecutable = Get-ChildItem -Path $PSSCriptRoot -Recurse | Where-Object {$_.Name -eq \"grate.exe\"}\n }\n }\n \n # Check for multiple results\n if ($grateExecutable -is [array])\n {\n # choose one that matches highest version of .net\n\t\t$dotnetVersions = (dotnet --list-runtimes) | Where-Object {$_ -like \"*.NetCore*\"}\n\n\t\t$maxVersion = $null\n\t\tforeach ($dotnetVersion in $dotnetVersions)\n\t\t{\n \t\t$parsedVersion = $dotnetVersion.Split(\" \")[1]\n \t\tif ($null -eq $maxVersion -or [System.Version]::Parse($parsedVersion) -gt [System.Version]::Parse($maxVersion))\n \t\t{\n \t\t$maxVersion = $parsedVersion\n \t\t}\n\t\t}\n \n $grateExecutable = $grateExecutable | Where-Object {$_.FullName -like \"*net$(([System.Version]::Parse($maxVersion).Major))*\"}\n }\n}\n\nif ([string]::IsNullOrWhitespace($grateExecutable))\n{\n # Couldn't find grate\n Write-Error \"Couldn't find the grate executable!\"\n}\n\n# Build the arguments\n$grateSwitches = @()\n\n# Update the connection string based on authentication method\nswitch ($grateAuthenticationMethod)\n{\n \"awsiam\"\n {\n # Region is part of the RDS endpoint, extract\n $region = ($grateServerName.Split(\".\"))[2]\n\n Write-Host \"Generating AWS IAM token ...\"\n $grateUserPassword = (aws rds generate-db-auth-token --hostname $grateServerName --region $region --port $grateServerPort --username $grateUserName) \n $grateUserInfo = \"Uid=$grateUserName;Pwd=$grateUserPassword;\"\n\n break\n }\n\t\n \"azuremanagedidentity\"\n {\n \t# SQL Server driver doesn't assign password\n if ($grateDatabaseServerType -ne \"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\"}\n\n $grateUserPassword = $token.access_token\n $grateUserInfo = \"Uid=$grateUserName;Pwd=$grateUserPassword;\"\n }\n \n break\n }\n\n \"gcpserviceaccount\"\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\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 {$_.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\n $grateUserPassword = $token.access_token\n \n # Append remaining portion of connection string\n $grateUserInfo = \"Uid=$grateUserName;Pwd=$grateUserPassword;\"\n }\n\n\n \"usernamepassword\"\n {\n \t# Append remaining portion of connection string\n $grateUserInfo = \"Uid=$grateUserName;Pwd=$grateUserPassword;\"\n\n\t\tbreak \n\t}\n\n \"windowsauthentication\"\n {\n # Append remaining portion of connection string\n\t $grateUserInfo = \"integrated security=true;\"\n \n # Append username (required for non\n $grateUserInfo += \"Uid=$grateUserName;\"\n }\n \n}\n\n# Configure connnection string based on technology\nswitch ($grateDatabaseServerType)\n{\n \"sqlserver\"\n {\n # Check to see if port has been defined\n if (![string]::IsNullOrEmpty($grateServerPort))\n {\n # Append to servername\n $grateServerName += \",$grateServerPort\"\n\n # Empty the port\n $grateServerPort = [string]::Empty\n }\n }\n \"mariadb\"\n {\n \t$grateServerPort = \"Port=$grateServerPort;Allow User Variables=true;\"\n }\n \"mysql\"\n {\n \t# Use the MySQL client\n $grateDatabaseServerType = \"mariadb\"\n $grateServerPort = \"Port=$grateServerPort;Allow User Variables=true;\"\n }\n \"oracle\"\n {\n \t# Oracle connection strings are built different than all others\n $grateServerConnectionString = \"--connectionstring=`\"Data source=$($grateServerName):$($grateServerPort)/$grateDatabaseName;$($grateUserInfo.Replace(\"Uid\", \"User Id\").Replace(\"Pwd\", \"Password\")) \"\n }\n default\n {\n $grateServerPort = \"Port=$grateServerPort;\"\n }\n}\n\n# Build base connection string\nif ([string]::IsNullOrWhitespace($grateServerConnectionString))\n{\n\t$grateServerConnectionString = \"--connectionstring=`\"Server=$grateServerName;$grateServerPort $grateUserInfo Database=$grateDatabaseName;\"\n}\n\n# Check for SQL Server and Azure Managed Identity\nif (($grateDatabaseServerType -eq \"sqlserver\") -and ($grateAuthenticationMethod -eq \"azuremanagedidentity\"))\n{\n\t# Append AD component to connection string\n $grateServerConnectionString += \"Authentication=Active Directory Default;\"\n}\n\nif ($grateSsl -eq $true)\n{\n\tif (($grateDatabaseServerType -eq \"mariadb\") -or ($grateDatabaseServerType -eq \"mysql\") -or ($grateDatabaseServerType -eq \"postgres\"))\n {\n \t# Add sslmode\n $grateServerConnectionString += \"SslMode=Require;Trust Server Certificate=true;\"\n }\n elseif ($grateDatabaseServerType -eq \"sqlserver\")\n {\n \t$grateServerConnectionString += \"Trust Server Certificate=true;\"\n }\n else\n {\n \tWrite-Warning \"Invalid Database Server Type selection for SSL, ignoring setting.\"\n }\n}\n\n# Add terminating double quote to connection string\n$grateServerConnectionString += \"`\"\"\n\n$grateSwitches += $grateServerConnectionString\n\n$grateSwitches += \"--databasetype=$grateDatabaseServerType\"\n$grateSwitches += \"--silent\"\n\nif ([System.Boolean]::Parse($grateDryRun))\n{\n $grateSwitches += \"--dryrun\"\n}\n\nif ([System.Boolean]::Parse($grateRecordOutput))\n{\n $grateSwitches += \"--outputPath=$grateOutputPath\"\n \n # Check to see if path exists\n if ((Test-Path -Path $grateOutputPath) -eq $false)\n {\n \t# Create folder\n New-Item -Path $grateOutputPath -ItemType \"Directory\"\n }\n}\n\n# Add transaction switch\n$grateSwitches += \"--transaction=$($grateWithTransaction.ToLower())\"\n\n# Add Command Timeout\nif (![string]::IsNullOrEmpty($grateCommandTimeout)){\n $grateSwitches += \"--commandtimeout=$([int]$grateCommandTimeout)\"\n}\n\n# Add Baseline switch\nif ([System.Boolean]::Parse($grateBaseline)) {\n $grateSwitches += \"--baseline\"\n}\n\n# Add SQL Files Directory parameter\nif (![string]::IsNullOrEmpty($grateSqlScriptFolder)) {\n # Add up folder\n $grateSwitches += \"--sqlfilesdirectory=$grateSqlScriptFolder\"\n}\n\n# Add log verbosity flag\nif (![string]::IsNullOrEmpty($grateLogVerbosity)) {\n # Add up folder\n $grateSwitches += \"--verbosity=$grateLogVerbosity\"\n}\n\n\n# Check for version\nif (![string]::IsNullOrEmpty($grateVersion))\n{\n # Add version\n $grateSwitches += \"--version=$grateVersion\"\n}\n\n# Set grate environment\nif (![string]::IsNullOrEmpty($grateEnvironment))\n{\n # Add environment\n $grateSwitches += \"--environment=$grateEnvironment\"\n}\n\n# Set grate schema. Especially useful when migrating from RoundhousE\nif (![string]::IsNullOrEmpty($grateSchema))\n{\n # Add schema\n $grateSwitches += \"--schema=$grateSchema\"\n}\n\n# Display what's going to be run\nif (![string]::IsNullOrWhitespace($grateUserPassword))\n{\n\tWrite-Host \"Executing $($grateExecutable.FullName) with $($grateSwitches.Replace($grateUserPassword, \"****\"))\"\n}\nelse\n{\n\tWrite-Host \"Executing $($grateExecutable.FullName) with $($grateSwitches)\"\n}\n\n# Execute grate\nif ($grateExecutable.FullName.EndsWith(\".dll\"))\n{\n\t& dotnet $grateExecutable.FullName $grateSwitches\n}\nelse\n{\n\t& $grateExecutable.FullName $grateSwitches\n}\n\n# If the output path was specified, attach artifacts\nif ([System.Boolean]::Parse($grateRecordOutput))\n{ \n # Zip up output folder content\n Add-Type -Assembly 'System.IO.Compression.FileSystem'\n \n $zipFile = \"$($OctopusParameters[\"Octopus.Action.Package[gratePackage].ExtractedPath\"])/output.zip\"\n \n\t[System.IO.Compression.ZipFile]::CreateFromDirectory($grateOutputPath, $zipFile)\n New-OctopusArtifact -Path \"$zipFile\" -Name \"output.zip\"\n}\n" }, "Parameters": [ { @@ -242,10 +242,10 @@ ], "StepPackageId": "Octopus.Script", "$Meta": { - "ExportedAt": "2022-10-17T22:47:54.861Z", - "OctopusVersion": "2022.4.4910", + "ExportedAt": "2025-12-08T23:46:49.374Z", + "OctopusVersion": "2025.4.10231", "Type": "ActionTemplate" }, - "LastModifiedBy": "farhanalam", + "LastModifiedBy": "twerthi", "Category": "grate" }