Azure Web App - Enable app_offline

Octopus.Script exported 2021-09-17 by harrisonmeister belongs to ‘Azure’ category.

This step template will take a provided app_offline file from a package and upload it to an Azure Web App to enable a way to safely bring down the app domain for a subsequent deployment.

It requires a set of deployment credentials for the Azure Web App.

Required:

Notes:

  • Tested on Octopus 2021.2.
  • Tested with both Windows PowerShell and PowerShell Core on Linux.

Parameters

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

Azure Web App name

AzWebApp.EnableAppOffline.AzWebAppName =

Provide the Azure Web App name.

AppOffline package source

AzWebApp.EnableAppOffline.SourcePackage =

Provide the package to source the app_offline.htm file from.

AppOffline file path

AzWebApp.EnableAppOffline.FilePath =

Provide the path (relative to the package) for the app offline file to be uploaded.

Note: If left blank or empty, the file will be sourced from the root of the package.

AppOffline file name

AzWebApp.EnableAppOffline.Filename = app_offline.htm

Optional: Choose the variation of the name of the app offline file. Default: app_offline.htm

Available options:

  • app_offline.htm
  • app_offline.html

Deployment username

AzWebApp.EnableAppOffline.Deployment.Username = $#{AzWebApp.EnableAppOffline.WebAppName}

Provide the user or application-scoped deployment username for the Azure Web App. Default: $#{AzWebApp.EnableAppOffline.WebAppName}.

Deployment password

AzWebApp.EnableAppOffline.Deployment.Password =

Provide the user or application-scoped deployment password for the Azure Web App.

Deployment REST API Url

AzWebApp.EnableAppOffline.Deployment.KuduRestApiUrl = https://#{AzWebApp.EnableAppOffline.AzWebAppName}.scm.azurewebsites.net/api/vfs

Optional: Provide a custom deployment REST API URL. Default is: https://#{AzWebApp.EnableAppOffline.AzWebAppName}.scm.azurewebsites.net/api/vfs.

Script body

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

$ErrorActionPreference = "Stop";
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

# Variables
$SourcePackage = "AzWebApp.EnableAppOffline.SourcePackage"
$AzWebAppName = $OctopusParameters["AzWebApp.EnableAppOffline.AzWebAppName"]
$FilePath = $OctopusParameters["AzWebApp.EnableAppOffline.FilePath"]
$Filename = $OctopusParameters["AzWebApp.EnableAppOffline.Filename"]
$DeployUsername = $OctopusParameters["AzWebApp.EnableAppOffline.Deployment.Username"]
$DeployPassword = $OctopusParameters["AzWebApp.EnableAppOffline.Deployment.Password"]
$DeploymentUrl = $OctopusParameters["AzWebApp.EnableAppOffline.Deployment.KuduRestApiUrl"]

# Validation
if ([string]::IsNullOrWhiteSpace($AzWebAppName)) {
    throw "Required parameter AzWebApp.EnableAppOffline.AzWebAppName not specified"
}
if ([string]::IsNullOrWhiteSpace($Filename)) {
    throw "Required parameter AzWebApp.EnableAppOffline.Filename not specified"
}
if ([string]::IsNullOrWhiteSpace($DeployUsername)) {
    throw "Required parameter AzWebApp.EnableAppOffline.Deployment.Username not specified"
}
if ([string]::IsNullOrWhiteSpace($DeployPassword)) {
    throw "Required parameter AzWebApp.EnableAppOffline.Deployment.Password not specified"
}
if ([string]::IsNullOrWhiteSpace($DeploymentUrl)) {
    throw "Required parameter AzWebApp.EnableAppOffline.Deployment.KuduRestApiUrl not specified"
}

$DeploymentUrl = $DeploymentUrl.TrimEnd('/')
$ExtractPathKey = "Octopus.Action.Package[$($SourcePackage)].ExtractedPath"

$ExtractPath = $OctopusParameters[$ExtractPathKey]
$FilePath = Join-Path -Path $ExtractPath -ChildPath $FilePath
if (!(Test-Path $FilePath)) {
    throw "Either the local or package extraction folder $FilePath does not exist or the Octopus Tentacle does not have permission to access it."
}

$sourceFilePath = Join-Path -Path $FilePath -ChildPath $Filename

if (!(Test-Path $sourceFilePath)) {
    throw "The file located at '$sourceFilePath' does not exist or the Octopus Tentacle does not have permission to access it."
}
$destinationFilePathUri = "$DeploymentUrl/site/wwwroot/$filename"

# Local variables
$StepName = $OctopusParameters["Octopus.Step.Name"]

Write-Verbose "AzWebApp.EnableAppOffline.AzWebAppName: $AzWebAppName"
Write-Verbose "AzWebApp.EnableAppOffline.FilePath: $FilePath"
Write-Verbose "AzWebApp.EnableAppOffline.Filename: $FileName"
Write-Verbose "AzWebApp.EnableAppOffline.Deployment.Username: $DeployUsername"
Write-Verbose "AzWebApp.EnableAppOffline.Deployment.Password: ********"
Write-Verbose "AzWebApp.EnableAppOffline.Deployment.KuduRestApiUrl: $DeploymentUrl"

Write-Verbose "Step Name: $StepName"

try {
    $credPair = "$($DeployUsername):$($DeployPassword)"
    $encodedCredentials = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($credPair))
    $headers = @{ 
        Authorization = "Basic $encodedCredentials"
        # Ignore E-Tag
        "If-Match" = "*" 
    }
    
    Write-Host "Invoking Put request for '$sourceFilePath' to '$destinationFilePathUri'"
    $response = Invoke-RestMethod -Method Put -Infile $sourceFilePath -Uri $destinationFilePathUri -Headers $headers -UserAgent 'powershell/1.0' -ContentType 'application/json'
    
    Write-Verbose "Response: $response"
}
catch {
    $ExceptionMessage = $_.Exception.Message
    $ErrorDetails = $_.ErrorDetails.Message
    $Message = "An error occurred invoking the Azure Web App REST API: $ExceptionMessage"
    if (![string]::IsNullOrWhiteSpace($ErrorDetails)) {
        $Message += "`nDetail: $ErrorDetails"
    }

    Write-Error $Message -Category ConnectionError
}

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": "852b78ae-eb32-43c0-bf55-0a5bdd7bebc8",
  "Name": "Azure Web App - Enable app_offline",
  "Description": "This step template will take a provided app_offline file from a package and upload it to an Azure Web App to enable a way to safely bring down the app domain for a subsequent deployment.\n\nIt requires a set of [deployment credentials](https://docs.microsoft.com/en-gb/azure/app-service/deploy-configure-credentials) for the Azure Web App.\n\n**Required:** \n- Credentials with access to the [Kudu VFS API](https://github.com/projectkudu/kudu/wiki/REST-API#vfs)\n\nNotes:\n\n- Tested on Octopus `2021.2`.\n- Tested with both Windows PowerShell and PowerShell Core on Linux.",
  "Version": 1,
  "ExportedAt": "2021-09-17T17:45:50.539Z",
  "ActionType": "Octopus.Script",
  "Author": "harrisonmeister",
  "Packages": [
    {
      "Id": "ff7d24cc-7288-428f-985a-155c467d63ff",
      "Name": "AzWebApp.EnableAppOffline.SourcePackage",
      "PackageId": null,
      "FeedId": null,
      "AcquisitionLocation": "Server",
      "Properties": {
        "Extract": "True",
        "SelectionMode": "deferred",
        "PackageParameterName": "AzWebApp.EnableAppOffline.SourcePackage"
      }
    }
  ],
  "Parameters": [
    {
      "Id": "b847fd97-bc71-4c89-99fb-bd5c2327027a",
      "Name": "AzWebApp.EnableAppOffline.AzWebAppName",
      "Label": "Azure Web App name",
      "HelpText": "Provide the Azure Web App name.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "e15156cc-1fbd-439f-bbea-5eb1f5da8f2f",
      "Name": "AzWebApp.EnableAppOffline.SourcePackage",
      "Label": "AppOffline package source",
      "HelpText": "Provide the package to source the `app_offline.htm` file from. ",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "Package"
      }
    },
    {
      "Id": "22fdf143-fd7b-48d8-ba52-eed69268b61c",
      "Name": "AzWebApp.EnableAppOffline.FilePath",
      "Label": "AppOffline file path",
      "HelpText": "Provide the path (relative to the package) for the app offline file to be uploaded.\n\n*Note:* If left blank or empty, the file will be sourced from the root of the package.\n",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "0b2edf69-da0d-4750-882a-4036c67f0129",
      "Name": "AzWebApp.EnableAppOffline.Filename",
      "Label": "AppOffline file name",
      "HelpText": "*Optional:* Choose the variation of the name of the app offline file. Default: `app_offline.htm`\n\nAvailable options:\n\n- `app_offline.htm`\n- `app_offline.html`",
      "DefaultValue": "app_offline.htm",
      "DisplaySettings": {
        "Octopus.ControlType": "Select",
        "Octopus.SelectOptions": "app_offline.htm|app_offline.htm\napp_offline.html|app_offline.html"
      }
    },
    {
      "Id": "c873f396-0ca1-4df9-b083-860541d24b61",
      "Name": "AzWebApp.EnableAppOffline.Deployment.Username",
      "Label": "Deployment username",
      "HelpText": "Provide the user or application-scoped [deployment](https://docs.microsoft.com/en-gb/azure/app-service/deploy-configure-credentials) username for the Azure Web App. Default: `$#{AzWebApp.EnableAppOffline.WebAppName}`.",
      "DefaultValue": "$#{AzWebApp.EnableAppOffline.WebAppName}",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "1c18f666-c91d-411c-a86b-b92d0bddc4ed",
      "Name": "AzWebApp.EnableAppOffline.Deployment.Password",
      "Label": "Deployment password",
      "HelpText": "Provide the user or application-scoped [deployment](https://docs.microsoft.com/en-gb/azure/app-service/deploy-configure-credentials) password for the Azure Web App.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "Sensitive"
      }
    },
    {
      "Id": "023c4e13-2a25-4e29-916f-aaafea1d8284",
      "Name": "AzWebApp.EnableAppOffline.Deployment.KuduRestApiUrl",
      "Label": "Deployment REST API Url",
      "HelpText": "*Optional:* Provide a custom deployment REST API URL. Default is: `https://#{AzWebApp.EnableAppOffline.AzWebAppName}.scm.azurewebsites.net/api/vfs`.",
      "DefaultValue": "https://#{AzWebApp.EnableAppOffline.AzWebAppName}.scm.azurewebsites.net/api/vfs",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    }
  ],
  "Properties": {
    "Octopus.Action.Script.ScriptSource": "Inline",
    "Octopus.Action.Script.Syntax": "PowerShell",
    "Octopus.Action.Script.ScriptBody": "$ErrorActionPreference = \"Stop\";\n[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\n\n# Variables\n$SourcePackage = \"AzWebApp.EnableAppOffline.SourcePackage\"\n$AzWebAppName = $OctopusParameters[\"AzWebApp.EnableAppOffline.AzWebAppName\"]\n$FilePath = $OctopusParameters[\"AzWebApp.EnableAppOffline.FilePath\"]\n$Filename = $OctopusParameters[\"AzWebApp.EnableAppOffline.Filename\"]\n$DeployUsername = $OctopusParameters[\"AzWebApp.EnableAppOffline.Deployment.Username\"]\n$DeployPassword = $OctopusParameters[\"AzWebApp.EnableAppOffline.Deployment.Password\"]\n$DeploymentUrl = $OctopusParameters[\"AzWebApp.EnableAppOffline.Deployment.KuduRestApiUrl\"]\n\n# Validation\nif ([string]::IsNullOrWhiteSpace($AzWebAppName)) {\n    throw \"Required parameter AzWebApp.EnableAppOffline.AzWebAppName not specified\"\n}\nif ([string]::IsNullOrWhiteSpace($Filename)) {\n    throw \"Required parameter AzWebApp.EnableAppOffline.Filename not specified\"\n}\nif ([string]::IsNullOrWhiteSpace($DeployUsername)) {\n    throw \"Required parameter AzWebApp.EnableAppOffline.Deployment.Username not specified\"\n}\nif ([string]::IsNullOrWhiteSpace($DeployPassword)) {\n    throw \"Required parameter AzWebApp.EnableAppOffline.Deployment.Password not specified\"\n}\nif ([string]::IsNullOrWhiteSpace($DeploymentUrl)) {\n    throw \"Required parameter AzWebApp.EnableAppOffline.Deployment.KuduRestApiUrl not specified\"\n}\n\n$DeploymentUrl = $DeploymentUrl.TrimEnd('/')\n$ExtractPathKey = \"Octopus.Action.Package[$($SourcePackage)].ExtractedPath\"\n\n$ExtractPath = $OctopusParameters[$ExtractPathKey]\n$FilePath = Join-Path -Path $ExtractPath -ChildPath $FilePath\nif (!(Test-Path $FilePath)) {\n    throw \"Either the local or package extraction folder $FilePath does not exist or the Octopus Tentacle does not have permission to access it.\"\n}\n\n$sourceFilePath = Join-Path -Path $FilePath -ChildPath $Filename\n\nif (!(Test-Path $sourceFilePath)) {\n    throw \"The file located at '$sourceFilePath' does not exist or the Octopus Tentacle does not have permission to access it.\"\n}\n$destinationFilePathUri = \"$DeploymentUrl/site/wwwroot/$filename\"\n\n# Local variables\n$StepName = $OctopusParameters[\"Octopus.Step.Name\"]\n\nWrite-Verbose \"AzWebApp.EnableAppOffline.AzWebAppName: $AzWebAppName\"\nWrite-Verbose \"AzWebApp.EnableAppOffline.FilePath: $FilePath\"\nWrite-Verbose \"AzWebApp.EnableAppOffline.Filename: $FileName\"\nWrite-Verbose \"AzWebApp.EnableAppOffline.Deployment.Username: $DeployUsername\"\nWrite-Verbose \"AzWebApp.EnableAppOffline.Deployment.Password: ********\"\nWrite-Verbose \"AzWebApp.EnableAppOffline.Deployment.KuduRestApiUrl: $DeploymentUrl\"\n\nWrite-Verbose \"Step Name: $StepName\"\n\ntry {\n    $credPair = \"$($DeployUsername):$($DeployPassword)\"\n    $encodedCredentials = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($credPair))\n    $headers = @{ \n        Authorization = \"Basic $encodedCredentials\"\n        # Ignore E-Tag\n        \"If-Match\" = \"*\" \n    }\n    \n    Write-Host \"Invoking Put request for '$sourceFilePath' to '$destinationFilePathUri'\"\n    $response = Invoke-RestMethod -Method Put -Infile $sourceFilePath -Uri $destinationFilePathUri -Headers $headers -UserAgent 'powershell/1.0' -ContentType 'application/json'\n    \n    Write-Verbose \"Response: $response\"\n}\ncatch {\n    $ExceptionMessage = $_.Exception.Message\n    $ErrorDetails = $_.ErrorDetails.Message\n    $Message = \"An error occurred invoking the Azure Web App REST API: $ExceptionMessage\"\n    if (![string]::IsNullOrWhiteSpace($ErrorDetails)) {\n        $Message += \"`nDetail: $ErrorDetails\"\n    }\n\n    Write-Error $Message -Category ConnectionError\n}"
  },
  "Category": "Azure",
  "HistoryUrl": "https://github.com/OctopusDeploy/Library/commits/master/step-templates//opt/buildagent/work/75443764cd38076d/step-templates/azure-web-app-enable-appoffline.json",
  "Website": "/step-templates/852b78ae-eb32-43c0-bf55-0a5bdd7bebc8",
  "Logo": "iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAMAAACahl6sAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADNQTFRF////AHjXf7vrv931QJrh7/f8EIDaIIncMJHfYKvmz+b3n8zw3+76j8Ttr9XycLPpUKLkkKvYFAAABGZJREFUeNrsnNmCqjoQRc1MEiD8/9cer7Yt2KBJZQC8ez07sKlKTQlcLgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoUSnt8YxXlFuGHSbIaxvj+fip4btkLn1blkWLaF5v03yLhLOYlVuGYfMOMZzNGxCOzhjTJqFkXnjq3Dr1yyvPI3hGl3Ih3zzHHNKudRstRhX5O58vIcShY67Gq6EPIESlzUWvazaGAOGbvU7ArDu/g8M4o8opDZWvbvPzlL/MMBE8jT9T9W7PbAJlHPTBFRf9yVTEcs63msXz2UHLSgf650G/d5t+wjbxxB2UCMqGrk8/LFSD7uJMeNt5bcJCyQZyAe5Fo9KYfWS2flQrr4b4tpuzaeWjYs49rt9LHf9uZD7+VbyVi9EBNrjYjuq2sxQOrl+p+HuBVu45qvqfq691ttYFQ5KyKbyJgaIY/NGxrlWZwlwGvmvu1oY3PuAv0niTq6tZ78jk//9uc1r1r4lQki7y7sp2Tu4V1y2iLoqFTqi1lIGcpFiebrZNZ1dOkF0cCIlO8jQ47nCkam9Lilz9GhDF1I6XGLzfnhwDIIZVfI7+8SSgfHsijqXENOGJF5QorG4EcW0OrScqX/dDrXpr70Ut/BII+1OfECPuYz/NWxYmgrCsUskxPvyhgmrw+WGZ6lGTuOlIyCYWTFyWjpM5KIZRUIOwjRNYRQ6tZF9BXtk8hWAHPtLNJ727Fq0JSkC1FDRRF0Jalj0d5qVh2KEpM2TuSsCYTCT6ZkdmFYI9LrYp5QayWbo6NXlZwcRD/61pth5Fq5EX423QQxNjhqWvvklkljOLkYjrmphXPZOJOk6Pg7HKMsrtQKcowzZoK3rx1ZUelGMdQA/HaKkjAt2RgqpZeYqbNbH7Hp2ct4nqfSPOfe0ftiSTZJydOV6rG5bQbyLK+nRuCC0343PzDgiOXyQA5c14BTZi98uR/5KJ1SnatLdoO50WWBQZPTq0VgsklU3h932actuo17ayrHrb/3ykiegd3KbqF2wbV6RrlsJ07yLcpsWFTul9RyK6ZScr+tk7oNrFj0o7HQUlj4EiEvJ6rPLKSmlMZCrksl1OnLaRkxc+/HB1naMhNtT/6yM2bDs6azCRHrM3aVPN7aW8irD/10B8njpAMcsl8okXcdKrl4sPsLmQVy/Sj90ucPRc/d/Bxxj+dXSpCayen32D+hLi16MsIV8gfCXrYp6ySsiJKRUF0XXiLpVbFU+fNv4r7mOwhFsX4ZdwpSi1DYs2jb6ebZ9788cblTzMrYhu7sf/17IFdtuviJ2ioHA6pMHkoH4CLUeMBU7iGkxuM/YgcdderF9ibRdc7O982F1HpYhjfWUe+x5a6pjop9iNLfoePvlsdZdTSMwfxSmTY20Q0eHnUNzga1edeNmmqbg18aMVR1L9vwSXHF9TfIWBxpKLs2hj3eQeBC0USvp2HHF3eIkRdhFOd6ER8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA/I/4J8AAo/80BciBec4AAAAASUVORK5CYII=",
  "$Meta": {
    "Type": "ActionTemplate"
  }
}

History

Page updated on Friday, September 17, 2021