Navigate back to the homepage

Azure App Service - Best Practices - Staging Slots

Alex Woodhead
June 30th, 2021 · 3 min read

Why use Staging Slots with Azure App Services?

Making use of staging slots within Azure App Services is a recommended best-practice by Microsoft for all Production applications. Utilising deployment slots enables:

  • Zero downtime updates to your application’s code
  • Configuration changes applied and tested prior to swapping the slots
  • Better perceived performance by application users, who don’t have to witness application warmup

To make use of deployment slots, your App Service has to be on the appropriate level of App Service Plan, which at the time of writing is the “Standard” plan level and above. See Microsoft’s App Service Limits documentation for details.

Creating and configuring an Azure App Service Staging Slot:

Creating the deployment slot to deploy to is a one-line command with the Azure CLI:

1az webapp deployment slot create -n [YOUR_APP_SERVICE_NAME] -g [YOUR_RESOURCE_GROUP] -s [YOUR_SLOT_NAME]

You may want to configure the health check endpoint for the App Service, so the load balancer can detect whether an instance is healthy. This will be transferred across to the Production slot when the slots are swapped:

1az webapp config set -g [YOUR_RESOURCE_GROUP] -n [YOUR_APP_SERVICE_NAME] -s [YOUR_SLOT_NAME] --generic-configurations "{\`"healthCheckPath\`": \`"[YOUR_HEALTH_CHECK_RELATIVE_URL]\`"}"

You may also want to disable ARR Affinity (Sticky Sessions), so that users aren’t stuck on an unhealthy instance. This is dependent on your application either being stateless, or maintaining state in a centralised store, such as SQL Server or Redis.

The command for disabling Arr Affinity (Sticky Sessions) is:

1az webapp update -n [YOUR_APP_SERVICE_NAME] -g [YOUR_RESOURCE_GROUP] -s [YOUR_SLOT_NAME] --client-affinity-enabled false

Deploying to an App Service Slot with Azure DevOps

Create an Azure Classic Service Connection

Firstly you will need to create an Azure Service Connection within your Azure DevOps instance that can be referenced within the pipelines. This will make use of a service principle account, which will need to have the required permissions within your Azure estate to provision and administrate resources.

Firstly Navigate to the Service Connections page within your Azure DevOps project (you will need to be a Project Administrator to access these settings). Select New Service Connection.

Select Service Connections within your Project Settings

Next Select “Azure Classic” as the type of Service Connection that you want to create.

Select Azure Classic

Finally, input the details of your Azure Subscription and the Service Principle Credentials that are required to access Azure and create/manage resources:

Input Subscription Details and Service Principle Credentials

Ensure on the final step that you make sure to enable “Grant access permissions to all pipelines”, which will enable the Service Connection to be used in pipelines created within your project.

Create and configure a deployment slot in an Azure DevOps pipeline

Making use of the Azure CLI Task available within Azure DevOps enables you to call the necessary Azure CLI commands to create and configure a staging deployment slot for your app service. The YAML for the task is shown below:

1- task: AzureCLI@2
2 displayName: 'Create and Configure Slot'
3 inputs:
4 azureSubscription: '[YOUR_AZURE_SERVICE_CONNECTION_NAME - SEE ABOVE]'
5 scriptType: ps
6 scriptLocation: inlineScript
7 inlineScript: |
8 az webapp deployment slot create -n [YOUR_APP_SERVICE_NAME] -g [YOUR_RESOURCE_GROUP] -s [YOUR_SLOT_NAME]
9 az webapp update -n [YOUR_APP_SERVICE_NAME] -g [YOUR_RESOURCE_GROUP] -s [YOUR_SLOT_NAME] --client-affinity-enabled true
10 az webapp config set -g [YOUR_RESOURCE_GROUP] -n [YOUR_APP_SERVICE_NAME] -s [YOUR_SLOT_NAME] --use-32bit-worker-process false
11 az webapp config set -g [YOUR_RESOURCE_GROUP] -n [YOUR_APP_SERVICE_NAME] -s [YOUR_SLOT_NAME] --generic-configurations "{\`"healthCheckPath\`": \`"/api/health/\`"}"

Having created and configured the slot, you can now have a second task in your pipeline that will deploy an artefact to that slot:

1- task: AzureRmWebAppDeployment@4
2 displayName: 'Deploy Candidate to slot'
3 inputs:
4 azureSubscription: '[YOUR_AZURE_SERVICE_CONNECTION_NAME - SEE ABOVE]'
5 WebAppName: '[YOUR_APP_SERVICE_NAME]'
6 deployToSlotOrASE: true
7 ResourceGroupName: '[YOUR_RESOURCE_GROUP]'
8 SlotName: [YOUR_SLOT_NAME]
9 packageForLinux: '$(System.DefaultWorkingDirectory)/**/*[YOUR_BUILD_ARTEFACT_NAME].zip'
10 enableCustomDeployment: true
11 RemoveAdditionalFilesFlag: true
12 ExcludeFilesFromAppDataFlag: false
13 enableXmlTransform: true
14 enableXmlVariableSubstitution: false

Finally, having either run automated tests, or manually verified that the application is running correctly, you can now swap the slots, with the new slot replacing the production slot for your App Service. This results in a seamless transition to the latest version of your application:

1- task: AzureAppServiceManage@0
2 displayName: 'Swap Deployment Slots:'
3 inputs:
4 azureSubscription: '[YOUR_AZURE_SERVICE_CONNECTION_NAME - SEE ABOVE]'
5 WebAppName: '[YOUR_APP_SERVICE_NAME]'
6 ResourceGroupName: '[YOUR_RESOURCE_GROUP]'
7 SourceSlot: [YOUR_SLOT_NAME]
8 PreserveVnet: true

Further considerations

Having worked with deployment slots, and the App Service Manage task referenced above in production scenarios, I have witnessed occasional disparities between when the task reports that the slots have swapped and when Azure actually completes the process.

Having raised a support case with Microsoft on the matter, the advice received was to query the App Service logs to ascertain if the slow swap had completed. Wanting to ensure that this could be automated, the following powershell script was produced, which would poll the logs until receiving the correct “Succeeded” state and wrapped in a DevOps powershell task:

1- task: AzurePowerShell@5
2 displayName: 'Query logs to ensure swap complete'
3 inputs:
4 azureSubscription: '[YOUR_AZURE_SERVICE_CONNECTION_NAME - SEE ABOVE]'
5 ScriptType: InlineScript
6 Inline: |
7 $azureSubscriptionId = "[YOUR_SUBSCRIPTION_ID]"
8 $azureResourceGroup = "[YOUR_RESOURCE_GROUP]"
9 $azureAppServiceName = "[YOUR_APP_SERVICE_NAME]"
10
11 Write-Output "Query Logs to confirm swap completion"
12
13 $date = Get-Date
14 $date = $date.AddMinutes(-30)
15
16 [bool] $swapComplete = 0
17 $logCheckCount = 0
18
19 while(!$swapComplete){
20 $logCheckCount++
21
22 $logs = Get-AzLog -ResourceId "/subscriptions/$azureSubscriptionId/resourceGroups/$azureResourceGroup/providers/Microsoft.Web/sites/$azureAppServiceName/slots/staging" -StartTime $date
23
24 $logs | where { $_.operationName.value -match 'Microsoft.Web/sites/slots/slotsswap/action' -or $_.operationName.value -match 'Microsoft.Web/sites/slots/SlotSwap/action' } | Sort-Object eventTimestamp | ForEach-Object {
25 Write-Host $_.eventTimestamp $_.status.value $_.description
26 }
27
28 Write-Output $logs[0]
29 $LastLogObject = $logs[0]
30 $LastLogStatus = $LastLogObject.Status.Value
31
32 Write-Output "Last log entry status: $LastLogStatus"
33
34 if($LastLogObject.Status.Value -eq "Succeeded"){
35 $swapComplete = 1
36 break
37 }
38
39 Write-Output "Checked logs $logCheckCount times"
40
41 if($logCheckCount -eq 20){
42 throw "Retry count exceeded"
43 }
44
45 Write-Output "Waiting 30 seconds before checking again"
46
47 Start-Sleep -s 30
48 }
49 azurePowerShellVersion: LatestVersion

This felt an unfortunate necessity at the time. Hopefully the App Service Manage task within DevOps will be enhanced in the future to correctly identify the point in time at which the swap has completed, before allowing the pipeline to continue.

Summary

I would consider making use of deployment slots within Azure App Services an essential, within a production application. Without utilising deployment slots, users of the application will witness application restarts and warmups, causing a loss of credibility of your application.

Further to this, adding the ability to conduct quick automated smoke tests against your application running in the production environment, along with any configuration changes, is likely to reduce everyone’s stress levels. So why not get started with deployment slots today?

More articles from Alex Woodhead

Simple guide to upgrading AKS Clusters on Azure

Quick and simple guide to upgrading AKS clusters on Microsoft Azure

September 27th, 2020 · 1 min read

Your job won't fix your career

Warning to the 9-5 developers out there. Don't rely on your job to provide you with the experience to further your career.

April 1st, 2020 · 2 min read
© 2020–2021 Alex Woodhead
Link to $https://twitter.com/woodheadioLink to $https://github.com/woodheadioLink to $https://www.linkedin.com/in/woodheadio/