Execute Custom Terraform Script with Package

Octopus.Script exported 2021-03-15 by alastairtree belongs to ‘Terraform’ category.

Run a custom terraform script using a package and an Azure Account.

E.g. run “terraform taint some-resource” in the context of your terraform package files and authenticated against Azure using credentials in an Octopus Azure Account variable. This step operates very similarly to the built-in Terraform Apply step but allowing you the control to call terraform commands rather than using the octopus runner. Very useful for runbooks doing terraform import.

Parameters

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

Package Id (Required)

TerraformPackageId =

The ID of the nupkg/zip that contains the terraform files

Azure Account

TerraformScript.AzureAccount =

null

Terraform Script

TerraformScript.CliScript = terraform plan -var-file="octopus.tfvars" -no-color

A terraform CLI script to be executed in the context of the package, such as terraform show -no-color or terraform import some-resource

Run Terraform Init?

TerraformScript.RunInitBeforeScript = True

If true, then script will call terraform init before executing the script

Collect terraform files as artefacts?

TerraformScript.CollectArtefacts =

null

Collect terraform files artefact pattern

TerraformScript.ArtefactNameLikePattern = *.tf*

A like pattern for files to be collected as artefacts when ‘Collect Terraform Files as Artefacts’ is enabled. Example: *.tf*

Release Number Override

TerraformScript.Octopus.Release.Number =

When step is run in a runbook there is no release number, so you may need to provide one here. By default we will use the release number from the package and substitute it into variable Octopus.Release.Number in terraform files, but you may override the value here if required.

Files to substitute Release Number

TerraformScript.VersionNumberSubstitutionFilesPattern = *.tf*

A like pattern for files that should have #{Octopus.Release.Number} substituted. Example: *.tf*

Script body

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

$DynamicPackageName = "InfrastructurePackage" 
$AccountVariableName = "TerraformScript.AzureAccount"
# Version of the terrform package - used to substitute Octopus.Release.Number in tfvars file
$packageVersion = $OctopusParameters["Octopus.Action.Package[$DynamicPackageName].PackageVersion"]
# Override the package version with a custom version string - used to substitute Octopus.Release.Number in tfvars file
$userOverridenVersionNumber = $OctopusParameters["TerraformScript.Octopus.Release.Number"]
# Should we call terraform init before the script?
$runInit = [System.Convert]::ToBoolean($OctopusParameters["TerraformScript.RunInitBeforeScript"]) 

$versionNumberToSubstitute = If ($userOverridenVersionNumber) { $userOverridenVersionNumber } else { $packageVersion }
$versionNumberSubstitutionFilesPattern = if ( $OctopusParameters["TerraformScript.versionNumberSubstitutionFilesPattern"]) {
    $OctopusParameters["TerraformScript.VersionNumberSubstitutionFilesPattern"]
}
else {
    "*.tf*"
}

# Should Octopus collect all terraform files from the package after execution as artefacts?
$collectArtefacts = if ($OctopusParameters["TerraformScript.CollectArtefacts"]) { 
    [System.Convert]::ToBoolean($OctopusParameters["TerraformScript.CollectArtefacts"]) 
} 
else { $false }
# override pattern to match (terraform) files like *.t* when collecting artefacts
$artefactNameLikePattern = if ($OctopusParameters["TerraformScript.ArtefactNameLikePattern"]) { 
    $OctopusParameters["TerraformScript.ArtefactNameLikePattern"] 
}
else { 
    "*.tf*"
}
# Detect if we have Azure Account credentials from a variable and log in to azure
$HasAzureAccount = if ($OctopusParameters["$AccountVariableName.Client"]) { $true } else { $false }
# The path where the package containing terraform files is extracted to disk - usually just ./InfrastructurePackage
$terraformPackageFolder = $OctopusParameters["Octopus.Action.Package[$DynamicPackageName].ExtractedPath"];
# Override the location of the terraform exe - otherwise assume terraform is available on PATH
$CustomTerraformExe = $OctopusParameters["Octopus.Action.Terraform.CustomTerraformExecutable"]
# The terraform script to be executed
$terraformCliScriptToExecute = [Scriptblock]::Create($OctopusParameters["TerraformScript.CliScript"])

# If we have service principal creds to Azure then set ENV variables so that azurerm can authenticate
if ($HasAzureAccount) {
    Write-host "Selecting azure subscription $($OctopusParameters["$AccountVariableName.SubscriptionNumber"]) using $AccountVariableName variable"
  
    $ENV:ARM_CLIENT_ID = $OctopusParameters["$AccountVariableName.Client"]
    $ENV:ARM_CLIENT_SECRET = $OctopusParameters["$AccountVariableName.Password"]
    $ENV:ARM_SUBSCRIPTION_ID = $OctopusParameters["$AccountVariableName.SubscriptionNumber"]
    $ENV:ARM_TENANT_ID = $OctopusParameters["$AccountVariableName.TenantId"]
}

Function Invoke-Exec {
    [CmdletBinding()]
    param(
        [Parameter(Position = 0, Mandatory = 1)][scriptblock]$cmd
    )
    $scriptExpanded = $ExecutionContext.InvokeCommand.ExpandString($cmd).Trim().Trim("&")
    Write-Verbose "Executing command: $scriptExpanded"

    & $cmd | Out-Default

    if ($lastexitcode -ne 0) {
        throw ("Non-zero exit code '$lastexitcode' detected from command: '$scriptExpanded'")
    }
}

#================= Prep to run terraform ========================

# List the contents of the package - useful debugging
Write-Verbose "Using package contents:"
Get-ChildItem $terraformPackageFolder -Verbose

# Use a custom version of terraform? If so add it to the path for this sesssion. Otherwise assume terraform already on PATH
if ($CustomTerraformExe) {
    $terraformfolder = Split-Path $CustomTerraformExe;
    # add custom terraform to path if required
    if ($ENV:PATH -notcontains $terraformfolder) { 
        $ENV:PATH += ";$terraformfolder"; 
    }
    Write-Verbose "PATH: $ENV:PATH"
    Write-Host "`nUsing terraform.exe from $terraformExePath"
}

Write-Host "Running in $terraformPackageFolder"
Set-Location $terraformPackageFolder

# Substitute #{Octopus.Release.Number} in *.tf files because that variable is not availe during runbook execution
Get-ChildItem . -file | Where-Object { $_.Name -like $versionNumberSubstitutionFilesPattern } | ForEach-Object {
    Write-Host "Replacing #{Octopus.Release.Number} in $($_.FullName) with $versionNumberToSubstitute"
    (Get-Content $_.FullName) -replace "#{Octopus.Release.Number}", $versionNumberToSubstitute | Set-Content $_.FullName
}   

#================= terraform init ========================

# optionally initialise terraform
if($runInit) {
  Write-Host "`n terraform init`n"
  terraform init -no-color
}
#================= Run terraform script ========================

try {
    # Execute the provided script
    Invoke-Exec $terraformCliScriptToExecute
}
finally {
    # optionally collect all terraform files as artefacts
    if ($collectArtefacts) {
        Get-ChildItem . -File | Where-Object { $_.name -like $artefactNameLikePattern } | New-OctopusArtifact
    }
}

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": "1d2a62d0-1f12-4417-8566-4ca1b8e6a69c",
  "Name": "Execute Custom Terraform Script with Package",
  "Description": "Run a custom terraform script using a package and an Azure Account.\n\nE.g. run \"terraform taint some-resource\" in the context of your terraform package files and authenticated against Azure using credentials in an Octopus Azure Account variable. This step operates very similarly to the built-in Terraform Apply step but allowing you the control to call terraform commands rather than using the octopus runner. Very useful for runbooks doing terraform import.",
  "Version": 1,
  "ExportedAt": "2021-03-15T18:13:18.243Z",
  "ActionType": "Octopus.Script",
  "Author": "alastairtree",
  "Packages": [
    {
      "Name": "InfrastructurePackage",
      "Id": "582b5176-aa5d-4166-a5fa-9b760a3b2d7d",
      "PackageId": null,
      "FeedId": null,
      "AcquisitionLocation": "Server",
      "Properties": {
        "Extract": "True",
        "SelectionMode": "deferred",
        "PackageParameterName": "TerraformPackageId"
      }
    }
  ],
  "Parameters": [
    {
      "Id": "0170e094-b282-4924-b1ed-5103015e366f",
      "Name": "TerraformPackageId",
      "Label": "Package Id (Required)",
      "HelpText": "The ID of the nupkg/zip that contains the terraform files",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "Package"
      }
    },
    {
      "Id": "3b7abc1b-49d9-4302-86e8-a85cc6eb2a75",
      "Name": "TerraformScript.AzureAccount",
      "Label": "Azure Account",
      "HelpText": null,
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "AzureAccount"
      }
    },
    {
      "Id": "53195dc5-dcc4-4a35-b3af-b63a0958ae46",
      "Name": "TerraformScript.CliScript",
      "Label": "Terraform Script",
      "HelpText": "A terraform CLI script to be executed in the context of the package, such as `terraform show -no-color` or ` terraform import some-resource`",
      "DefaultValue": "terraform plan -var-file=\"octopus.tfvars\" -no-color",
      "DisplaySettings": {
        "Octopus.ControlType": "MultiLineText"
      }
    },
    {
      "Id": "2d907ba2-41f4-47d4-9592-1111a9ab8f93",
      "Name": "TerraformScript.RunInitBeforeScript",
      "Label": "Run Terraform Init?",
      "HelpText": "If true, then script will call terraform init before executing the script",
      "DefaultValue": "True",
      "DisplaySettings": {
        "Octopus.ControlType": "Checkbox"
      }
    },
    {
      "Id": "d0d1018d-d8aa-4c34-8da9-5c893076f8fe",
      "Name": "TerraformScript.CollectArtefacts",
      "Label": "Collect terraform files as artefacts?",
      "HelpText": null,
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "Checkbox"
      }
    },
    {
      "Id": "d379e1f0-fac5-48cb-a011-249e0bcb401e",
      "Name": "TerraformScript.ArtefactNameLikePattern",
      "Label": "Collect terraform files artefact pattern",
      "HelpText": "A like pattern for files to be collected as artefacts when 'Collect Terraform Files as Artefacts' is enabled. Example: `*.tf*`",
      "DefaultValue": "*.tf*",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "7b24c582-de47-4dad-9e37-c300802d1df9",
      "Name": "TerraformScript.Octopus.Release.Number",
      "Label": "Release Number Override",
      "HelpText": "When step is run in a runbook there is no release number, so you may need to provide one here. By default we will use the release number from the package and substitute it into variable Octopus.Release.Number in terraform files, but you may override the value here if required.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "23e0e330-6b6b-4f7a-bd95-e44e69d1bcf6",
      "Name": "TerraformScript.VersionNumberSubstitutionFilesPattern",
      "Label": "Files to substitute Release Number",
      "HelpText": "A like pattern for files that should have #{Octopus.Release.Number} substituted. Example: `*.tf*`",
      "DefaultValue": "*.tf*",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    }
  ],
  "Properties": {
    "Octopus.Action.RunOnServer": "true",
    "Octopus.Action.EnabledFeatures": "Octopus.Features.SubstituteInFiles",
    "Octopus.Action.Script.ScriptSource": "Inline",
    "Octopus.Action.Script.Syntax": "PowerShell",
    "Octopus.Action.Script.ScriptBody": "$DynamicPackageName = \"InfrastructurePackage\" \n$AccountVariableName = \"TerraformScript.AzureAccount\"\n# Version of the terrform package - used to substitute Octopus.Release.Number in tfvars file\n$packageVersion = $OctopusParameters[\"Octopus.Action.Package[$DynamicPackageName].PackageVersion\"]\n# Override the package version with a custom version string - used to substitute Octopus.Release.Number in tfvars file\n$userOverridenVersionNumber = $OctopusParameters[\"TerraformScript.Octopus.Release.Number\"]\n# Should we call terraform init before the script?\n$runInit = [System.Convert]::ToBoolean($OctopusParameters[\"TerraformScript.RunInitBeforeScript\"]) \n\n$versionNumberToSubstitute = If ($userOverridenVersionNumber) { $userOverridenVersionNumber } else { $packageVersion }\n$versionNumberSubstitutionFilesPattern = if ( $OctopusParameters[\"TerraformScript.versionNumberSubstitutionFilesPattern\"]) {\n    $OctopusParameters[\"TerraformScript.VersionNumberSubstitutionFilesPattern\"]\n}\nelse {\n    \"*.tf*\"\n}\n\n# Should Octopus collect all terraform files from the package after execution as artefacts?\n$collectArtefacts = if ($OctopusParameters[\"TerraformScript.CollectArtefacts\"]) { \n    [System.Convert]::ToBoolean($OctopusParameters[\"TerraformScript.CollectArtefacts\"]) \n} \nelse { $false }\n# override pattern to match (terraform) files like *.t* when collecting artefacts\n$artefactNameLikePattern = if ($OctopusParameters[\"TerraformScript.ArtefactNameLikePattern\"]) { \n    $OctopusParameters[\"TerraformScript.ArtefactNameLikePattern\"] \n}\nelse { \n    \"*.tf*\"\n}\n# Detect if we have Azure Account credentials from a variable and log in to azure\n$HasAzureAccount = if ($OctopusParameters[\"$AccountVariableName.Client\"]) { $true } else { $false }\n# The path where the package containing terraform files is extracted to disk - usually just ./InfrastructurePackage\n$terraformPackageFolder = $OctopusParameters[\"Octopus.Action.Package[$DynamicPackageName].ExtractedPath\"];\n# Override the location of the terraform exe - otherwise assume terraform is available on PATH\n$CustomTerraformExe = $OctopusParameters[\"Octopus.Action.Terraform.CustomTerraformExecutable\"]\n# The terraform script to be executed\n$terraformCliScriptToExecute = [Scriptblock]::Create($OctopusParameters[\"TerraformScript.CliScript\"])\n\n# If we have service principal creds to Azure then set ENV variables so that azurerm can authenticate\nif ($HasAzureAccount) {\n    Write-host \"Selecting azure subscription $($OctopusParameters[\"$AccountVariableName.SubscriptionNumber\"]) using $AccountVariableName variable\"\n  \n    $ENV:ARM_CLIENT_ID = $OctopusParameters[\"$AccountVariableName.Client\"]\n    $ENV:ARM_CLIENT_SECRET = $OctopusParameters[\"$AccountVariableName.Password\"]\n    $ENV:ARM_SUBSCRIPTION_ID = $OctopusParameters[\"$AccountVariableName.SubscriptionNumber\"]\n    $ENV:ARM_TENANT_ID = $OctopusParameters[\"$AccountVariableName.TenantId\"]\n}\n\nFunction Invoke-Exec {\n    [CmdletBinding()]\n    param(\n        [Parameter(Position = 0, Mandatory = 1)][scriptblock]$cmd\n    )\n    $scriptExpanded = $ExecutionContext.InvokeCommand.ExpandString($cmd).Trim().Trim(\"&\")\n    Write-Verbose \"Executing command: $scriptExpanded\"\n\n    & $cmd | Out-Default\n\n    if ($lastexitcode -ne 0) {\n        throw (\"Non-zero exit code '$lastexitcode' detected from command: '$scriptExpanded'\")\n    }\n}\n\n#================= Prep to run terraform ========================\n\n# List the contents of the package - useful debugging\nWrite-Verbose \"Using package contents:\"\nGet-ChildItem $terraformPackageFolder -Verbose\n\n# Use a custom version of terraform? If so add it to the path for this sesssion. Otherwise assume terraform already on PATH\nif ($CustomTerraformExe) {\n    $terraformfolder = Split-Path $CustomTerraformExe;\n    # add custom terraform to path if required\n    if ($ENV:PATH -notcontains $terraformfolder) { \n        $ENV:PATH += \";$terraformfolder\"; \n    }\n    Write-Verbose \"PATH: $ENV:PATH\"\n    Write-Host \"`nUsing terraform.exe from $terraformExePath\"\n}\n\nWrite-Host \"Running in $terraformPackageFolder\"\nSet-Location $terraformPackageFolder\n\n# Substitute #{Octopus.Release.Number} in *.tf files because that variable is not availe during runbook execution\nGet-ChildItem . -file | Where-Object { $_.Name -like $versionNumberSubstitutionFilesPattern } | ForEach-Object {\n    Write-Host \"Replacing #{Octopus.Release.Number} in $($_.FullName) with $versionNumberToSubstitute\"\n    (Get-Content $_.FullName) -replace \"#{Octopus.Release.Number}\", $versionNumberToSubstitute | Set-Content $_.FullName\n}   \n\n#================= terraform init ========================\n\n# optionally initialise terraform\nif($runInit) {\n  Write-Host \"`n terraform init`n\"\n  terraform init -no-color\n}\n#================= Run terraform script ========================\n\ntry {\n    # Execute the provided script\n    Invoke-Exec $terraformCliScriptToExecute\n}\nfinally {\n    # optionally collect all terraform files as artefacts\n    if ($collectArtefacts) {\n        Get-ChildItem . -File | Where-Object { $_.name -like $artefactNameLikePattern } | New-OctopusArtifact\n    }\n}\n",
    "Octopus.Action.SubstituteInFiles.TargetFiles": "InfrastructurePackage\\*.tf\nInfrastructurePackage\\*.tfvars"
  },
  "Category": "Terraform",
  "HistoryUrl": "https://github.com/OctopusDeploy/Library/commits/master/step-templates//opt/buildagent/work/75443764cd38076d/step-templates/terraform-custom-script.json",
  "Website": "/step-templates/1d2a62d0-1f12-4417-8566-4ca1b8e6a69c",
  "Logo": "iVBORw0KGgoAAAANSUhEUgAAAPoAAAEGCAYAAABMy4jrAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAABa4SURBVHhe7Z2LtxXVfcf7d5C2q2tlNU2bQNRISNWgkmDw3YjWalZboXk0bYVEl64KrmAS0VIkqMhDEAEfFUFLVAL4gFgRggEhQaAK0fC4lzcXuJeHPHf9npld5h73mceZvWf/Zub7Weu7ll7OY87MfGb27OcfKUJI5aHohNQAik5IDaDohNQAik5IDaDohNQAik5IDaDohNQAik5IDaDohNQAik5IDaDohNQAik5IDaDohNQAik5IDaDohNQAik5IDaDohNQAik5IDaDohNSAyom+e+9+9d6mzWrFb9YyArJp84fhkSE+Kb3oBw93q9lzF6i//5e71Wf6XcoIzMLX3wyPFvFFaUU/cfKkmjr7OfVXF19tPLkYOfnT8weppctXhUeO+KCUou/cvVcNuuE240nFyMyfXfB1tXzVmvAIkqIpnejrN32g+l12vfFkYuSkj+Fvn+0/WP16zW/DI0mKpFSi7+jcxaJ6SQLRjbJ/5Qr17vqN4RElRVEa0XuOHFWXXHPrp04cRmb69B1o/Dvy5wOGNEpmpDhKI/q9Dz5iPGkYmYkTHfmLv75SbXx/S3h0iWtKIXrnrj3Gk6VVvjjwOjV02AjGU1CHkiQ68oWvXau2fLQtPMrEJaUQ/T8fm2k8UZpz55hx6sOt28N3EV/8YXuH8fiYgosCj5l7SiH611M0pb325orw1UQCn/vqEONxMuW8y7+ltnXsDN9JXCBe9DTF9onT5oSvJlL4/EVXGY9Vq1w4+MZG/wjiBvGio/uk6cTQwd2AyCOr6MjFV9/aGKtA7CNe9Kfnv2w8KXSGjRgVvpJIIkn0VpV1aEI90HUo/BRiC/GiT39qnvGE0Ln/51PDVxJJpLmj9+lnlv0bQ4epg4cOh59EbCBe9MfnPG88GXQoukzSFt1b3dkh+9Fjx8JPI3mh6MQJ7TyjN+fab/9AHT/+cfiJJA8UnTjBhujIDbfdrj4+cSL8VNIuFJ04wZboyM3fvUOdPHUq/GTSDhSdOMGm6AhmEDp9+nT46SQrFJ04wbboyPCRo9WZM2fCbyBZoOjECS5ER/75rvvU2bNnw28haaHoxAmuREdGjBobfgtJC0UnTnApOnLXfePDbyJpoOjECa5FR0Y/8HD4bSQJik6cUIToyE8nTAm/kcRB0YkTihIdmTB1VvitpBUUnTihSNGRabPnht9MTFB04oSiRUdmPfff4beTZig6cYIP0ZFnX3wl3AIShaITJ/gSHXnhlVfDrSAaik6c4FP0P/7SZerlJcvCLSGAohMn+BQd+ZPzLldLli0Pt4ZQdOIE36IjXK75HBSdOEGC6AiWa1717u/CraovFN0CPSc4mqoZKaIjXMGVolth4oouddfivaqzmxMjaCSJjtR9BVeKbgGI/qVJW1X/KdvUoysPqmMneYeXJjqCbdq0+cNwC+sFRbeAFl1n0MwdasHGHlVn3SWKjnzhkmtquYIrRbdAs+g6Nz3XqdbtrOd0xVJFR/peep36aFtHuKX1gKJboJXoOj/65V6143C9ZjGVLDpy/qBvqe0du8KtrT4U3QJJoiNfnrxV/fztLnWkJs/v0kVH6rSCK0W3QBrRdS57Yruav6FHnam47zcOH2k8Xr7Sap23r155s9qz70C41dWFolsgi+g6NzzbqdZ0Vvf5fcGiN4zHy2dayV6HFVwpugXaEV1nxMI9auvBaj6/S7urx2Xmsy+GW11NxIuetGzy2InTwlf6I4/oCJ7fxy/vUt0V62F3qLunMVur6bh5jWEF1yf/i6J7pep39GgGztiunlvfrU5X7Pl9/4GDatEbb6mHpjzpJcN/OLpxrqDorvOZvr3PI97RPVOHO3pzrn+mU72z43j46cQG//6zCcbzR4d3dM+UQfSHLYuuM+q1feE3kLz85KHJxvNHh6J7pk5F9+Z8c9aO8BtIXu4b/5jx/NFh0d0zFD3goeVdatmHx8L/Kweoalj0wRE16dcHgz94hKILh6IHjFy4p/G34S/uUh91nQz/Kpf3951U3563s7HN977u/xGEoguHogdo0ZELHtuqxr11QB06Lm+t8APHzqifLtuvzov8DoruH4pugaJF1/na9KA57pQA30+cVmr22sPqomnbPrWdFN0/FN0CvkTXQXPc6g5/3Wnf3npMXTWnw7htCEX3D0W3gG/Rde5YVOxwWNQV/OCl5O2i6P6h6BaQIjpy4eSt6uGVB9XxU+6612GoLeoIUFdg2obmUHT/UHQLSBJd54pP3rt48xGr01lhaC2myLr8ie3G72wViu4fim4BiaLroIkLTV15Wb/7RGNqLNN3JIWi+4eiW0Cy6AiautDkhaavrOzuOd3oimv63LSh6P6h6BaQLroOmr7QBIamsCTwjD/tnUNqwNRPN5dlDUX3D0W3AJ6FTSd43gyZbVd0HTSFoUmsFei2OvjJHcb3thOK7h+Kbgms0oLZXk0neruxfUdvDprGot1p8SyPLram1+YJRfcPRbcM5oFDBxbTCZ81UdHvXGT3IqLz3QW7w29Q6pbng77ptjPmjf3hN/iDogunbKIDzBCDrqnoomo68dOGotuDogunjKJrMOjkgTcPqPNTdixpDkW3B0UXTplF1/yh61Rbz74U3R4UXThVEF3zxu+PqitjBn80h6Lbg6ILp0qiA7RhT1+drn2aotuDogunaqJr9h45re55Nb7HGUW3B0UXTlVF17wX04ecotuDogun6qIDjDAzjQqj6Pag6MKpg+gajPOe8HZXY4kmCELR7UHRhVMn0TWYJebfXt5D0S1C0YVTR9E1GAOuoej5oOjCqbPoUSh6Pii6cCh6AIrzmPzRJFKeoMeept0ZZOKCSSsweYVvKLpwKHpvMK2zrdFxiCvR8VnRRw/fUHThUPQA1MhrsGCDjdFxiG3R0USIpkJMJCkJii4cih5w95K96j/+54DqPnHOIIyOyzLtsim2RNfTTEcvSJKg6MKh6AH6+XzgjO1q3nvdve6YmCWmndFxiA3RMVNNkQtHtANFFw5FD2iuiLvh2U61bmfvZZiwrPLVGUbHIXlER11B3NxzkqDowqHoAa1q3NHsFq3Vxui4LLO3tiO6nk1WwuKOaaHowqHoAXFNa/2nbFOTV/VehintfOxZRM8zP7xvKLpwKHpAmjZ0TNGMqZqjJK2wklZ0Wyu++IKiC4eiB6QRXQdSfhCRMm7NtCTR9cVDZl16eii6cCh6QBbRERSz71u6X3UdP1fMRtMXmsDQFKZf10p0POPjWd/lqqxFQtGFQ9EDsoquc/Hj29RT63pXnKEpTK9rbhId3yWh26pNKLpwKHpAu6LrXPtUh3pnx/Hw0wLQNPazZecGnIx+fV+ji20VoejCoegBeUXXwTh36Z1bXEDRhUPRA2yJjmAGG8xkI7W7qgsounAoeoBN0XVQC//alqPhN1Qbii4cih7gQnTkO5GJJ6oMRRcORQ+g6Pmg6MKh6AEUPR8UXTgUPYCi54OiC4eiB1D0fFB04VD0AIqeD4ouHIoeQNHzQdGFQ9EDKHo+KLpwKHoARc8HRRcORQ+g6Pmg6MKh6AEUPR8UXTgUPYCi54OiC4eiB2ChQpOoeRNdZLHKUHThUPSA02eVmru+W106I/8yTNHwjh6EonuGovcGSzKNX97VGFNuEjdrKHoQiu4Zim4Gs8SMWBjM+5YnFD0IRfcMRY9nTefHjeWZTBKnCUUPQtE9Q9GTwbzt8zf0qMsM87YnhaIHoeieoejpwRxwE1d09Zq3PSkUPQhF9wxFzw6e33/0y3Tt7hQ9CEX3DEVvHyyrHLeeGkLRg1B0z1D0fGBCZ6y7NmjmDopuOH90KLpnKLodjn3y/P7oyoONJZYp+qdD0T1D0e3S2X1a3b3k3PM7RQ9C0T1D0d3w3u4T6u/m7qToYSi6Zyi6W367q5qLKjZD0YVD0YkNKLpwKDqxAUUXDkUnNqDowqHoxAYUXTgUndiAogvHt+gYGXbqTPagRxqRA0UXjm/Rn1hzqFdPsrRZ3VGPZquyQNGFQ9GJDSi6cCSJfuWcjkZvslaJTvxA0WVB0YUjSfTFm4+EfzUz7q0DFF0oFF04VRR9w/9uUcNHjjb+njrmLy++urE/DnX3hHvIPhRdOFUUffBN/2T8LXXPsBGjwj1kH4ounCqKbvodzKVqwJCbwz1kH4ouHIpenwy+cXi4h+xD0YVTRdFHjn7A+FvqnoemPBnuIftQdOFUUXQw/el5DeGHDhtR+/x43KPq+V8sCveMGyi6cCSJjkUOd/ecbpkxS8+teJokOikWii4cSaJnCUUvnu0du9TK1esamfHMfDVh6qxGbhw+UvUffNP/nzN9+vY+h/r0G6juffARp817vqHoCVB0uUDseS8tttonAW36P7z3QbVk2fJKiU/RE3hty1F1+yt7MmfL/pPhJxCboLMR7tJF9UXARQSlA1xUygxFJ+LBnXXMuEmNdnbTOVBUcHGB9GWEohOxQHDcvVGcNh17X8EFB48MZYKiE5FAJGmCNwd3+BW/WRtusWwoOhHF4qVveS+iZw1q9aULT9GJCFBMhzCmY5wluhMOBsigpx2CzjgQUQf/r/9Nvx7db02flyWoR5BaU0/RiXdQk95uMf3zF13V6GGIkkBeyVCzjosALhKm70oTFOclyk7RiVcgVlbJcfdFt1lcIFyCiwcuIl/55t8at6NV8Htcb1tWKDrxBmrUTce0VSAc5PMBivkoPZi2yxTI7rr/fhYoegJz1h5WA6Zuy5y1O9kzrhUo2qL3mel4mgLBJEiD7Ybwpm1sFVzMJEDRE2AXWLtAlrS92iA4xJL2zItn+SxDjXFR8/0bKHoCUdEHzdyhrnmqo2UueZyzwMaRRXI8h0sTvBk8h6d9fofsPqHoCURFtzkevY6kbT7D3bIsNC5eKZvmUDrxBUVPwIXoKPphsETZOobkSfPQ0FaRVIGVhbRFeV+/j6In4EL0tMXX6mSg4W+9g+fxsnQnbQVmDTL9tmh8Nb1R9ARciG76HVUNJnUw/T0aFH2ltTu3C5r/kprhIHvR9Q8UPQGKniMpiuuQouxjvZuB7KbfGk3RPegoegIuRM/TxbIs6RPG9G/RVOVO3kyaYnyRNfEUPQEXouNKjhNBD6ioYgYMSW52KmvFW1rSVNAVVS9B0ROIir5gU486cvJsy9z/q3SiVx1cyJL6r6Oveh1IanpDk2MRUPQEoqJnSZ1Fh8SmY6Xjco01aeCil9Sppoj++xQ9AYqeDVSsmY6TDk76omucfYN6CNO+0HG55pyGoiew8P0j6h9f2JU57++r5yywSc+lVX8ub0VSKcf1fqHoxBpJdy5U0tUVlGLi2tddt61TdGKNpL7sZe/5lpekIa4u+8JTdGKFpGfzOt/NNUl3dZfP6hSdWCGpg0hVO8ZkBc/ipv2j42o/UXRiBdyxTccHKdOw0yKIa25zVXyn6CQ3KJKajo2Or3nebKJXabVBXA08+sC7gKKT3CQN4igzuIhFhxXjv/MWr5NaJ1wM8qHoJDdxbedl7gXXLLmOjTHlcZVyLtrUKXoCc9d3qytm7cic9btPhJ9QfeL6tZe1g0wryXXyyl70xZGiJ8AusPH4KIa6JklynTyyJ9W+24aiJxAVHfO1Y6bXVrlwcv1Ej+sEgpFbZSOt5Dpo+8Z7soL3mD5Px3bnIoqeQFR0W+PRq0Sc6C57erkgq+Q66EPQDnFNkhS9KWUUHcVZzC6CLqNlz4WDhxqPC1Im0duVHGn3d8aJbrtug6In4EJ0CGL6LaVMzLxwZenbnkfyPHPeFVkaougJuBDd9DtKm5KLnlfyPDXvFD0CRZeduAkg87Y1m0DvNFuf61NyEDc+wHYTG0VPwIXocc9mVYpNIFV0ZZsx4yaF/9IeviUHKPGYPh+xPdqPoifgQnScZOgwgYNZ9piOiY4tIJWpU0670yVLkBxQ9AhVFL0q4IQ3HRMEI7Rs0EpynayyS5EcYFtM36NjE4qeQFT0Cx7bqr48uXXO/+Tf6yS66xM1SXKdtLJLkhzE3dFtXSg1FD2BqOhZUgfRgemY6OQBTVZpJNdJkl2a5IBF9wi+RX9hQ4/6m2c6M2fDnnoMajEdEx3I1S5Js6aa0kp2iZKDuOG9FL0prkUn8cRVyOVpR293fbpm2aVKDuLa0W2vZEPRSS5ciZ5mRdJW0bJLlhyww0wEii6buDtvu4M9NEmLQcQFskuWHMT9PoreFIruF9d3pTyyt5OiJAeuSkMmKDrJRZzotrpxFiV7kZKDuFYFit4Uiu6XuJlScCLbwrXsRUuO7zJth47tmXkoOskFKrxMx0XH5lTPrmQvWnIQVxJyMTMPRSe5iXvWtN1MZFt2H5KDuIpC2/sMUHSSm7jhli7WE7Mluy/JUSw3bY+Oi22i6CQ3RT9vgryy+5IcxNVrYLtcQNGJFeLWE8vbnt6KdmX3KTmI63uA3+QC8aI/Pf9l4w7RGTl6bPhK4pO4vumofc/T7z2OrLL7ljyp9ONqnTrxor/6q7eNO0TnG0OHha8kPknqsmq7p1eUtLL7lhwkTQzq6oIoXvS16zcZd0g0eA3xD0QyHR/E5V0dJMkuQfK4YamI7XnioogX/fjHJ9Rn+w827hidS665VZ08dSp8B/FF0jJDLpqNorSSXYLkIOlu7nIbxYsOvnfHj407Jpqrbvme2taxM3wH8UVcpRziogY+SrPsUiRPughiu11SCtFXvfs7485pDu78t99zv5rz/C8axSSm+IydONV4bHRcn9AA9QUoBuO7JEgOojPYmuL6AlgK0cEt37/TuIMYgYlZ1AGxvdyQdDBk1rQfdFxWVGpKI/rGD35v3EmMvPTpN9D492ik3Gldk1Rkx6OFy0pKTWlEB+MmPWHcWYzAJNzVUQvvurjqGzzKmH57NEXczUGpRAcjRo017jBGWBJERzCwo4i7mQ9QYokbb46g4rKo31860c+ePasefGS6cccx0pJchG81c2uZgbxpprEq8vGldKJrXlq8VH1x4HXGHcjISZ++9ZI9reRFV0iWVnRw9NgxNWHqLAovPSmK8ZC97MV41Dmkkdx1xyETpRY9yrvrN6pHZjyt7v7JeHXb7fc0JkNgZOT6f/hX9bkBQ4wnfTRlfmZP80yOuOzmGkdlRCeygQhxfeF1IEvZmt6SmtB0MEWUrwsZRSeFkaa5CYHsZelUg3XaTb+hObjI+WxOpOikUNLe/RA8t0tta8dFK2mQio6E/vYUnRQO+qKnKcYjuLujwlXKszsuPEldWqNBcV3CowhFJ17AyZ800i0aCD/jmfnhu4sHF5q0xXQdVERKuUBRdOINSIA7nkmSVsEosHkvLS6sSI8LEkoUaWrUoylilF4WKDrxTvMY8rRBcxwktF00XrJseePunTS0tFUkViRSdCKCuLnh0wRS4tkZxfuVq9c1knTXxwVCvxYXjOEjR2e+c0eDegdU0kmEohMxQBI815okyhvUkCNpeq61E5RKpLYQAIpOxAHhs1TU+QwuTJIF11B0IhY860oVHoJLLaaboOhEPJicIW27u+vgwlOWXntRKDopBWiKQ0cbPAsXLT3kxogzV6uoFAFFJ6UENeaQL2s7fNqgaI6WgDI8f6eBopPSAxkhJYr4GAYKSdPU3qNkoF+LkgLej2K5lN5sNqHopPJAXFSc6dQRik5IDaDohNQAik5IDaDohNQAik5IDaDohNQAik5IDaDohNQAik5IDaDohNQAik5IDaDohNQAik5IDaDohNQAik5IDaDohNQAik5IDaDohNQAik5I5VHq/wCVysbCAzGnhwAAAABJRU5ErkJggg==",
  "$Meta": {
    "Type": "ActionTemplate"
  }
}

History

Page updated on Monday, March 15, 2021