Automatically stop (and start) an Azure App Service main site, or disable and re-enable Always On, during Git- / Kudu-based deployment

Let's say you do GitHub or Bitbucket continuous deployments to an Azure App Service. At some point, deployments start failing, and after a bit of further investigation, you notice in the Kudu deployment logs that a file in use or permissions error is occurring when trying to copy over existing files of the same names.
Now let's say that temporarily stopping the main site, or that disabling Always On, allows you to redeploy successfully. So you have a workaround. While not a showstopper, you would prefer to not have to manually intervene in your continuous deployments. There are some tricks you might be able to implement with regard to specific situations (for example renaming certain files on the site or skipping copying these files during deployment).

 

However, since the inclusion of Azure Resource Manager (ARM) PowerShell modules on the App Service image, you can now run ARM PowerShell commands from your App Service, which means that can do operations such as stopping and starting your main site process by using ARM commands.

 

You can incorporate these commands into a Kudu custom deployment script (deploy.cmd) to automatically stop the site towards the beginning of the Kudu deployment script, and start the site at the end of the Kudu deployment script.

 

There are a couple limitations to this approach that I should mention:
-This approach will make your site unavailable while the deployment is occurring. However, you can work around this by using deployment slots, and automate the process of swapping the slots via AutoSwap (or alternatively via an additional ARM call).
-This approach currently relies upon PowerShell (which can also be executed from the Command-Line as I am doing in this example). AFAIK there isn't a way to make this work if using a bash script within App Service (deploy.sh).

 

Here are basic steps to use this approach. You will of course want to test this piece-by-piece to ensure that each part works and to avoid causing issues in production.

  1. To run the ARM PowerShell commands in App Service, you will need a Service Principal. I have posted basic steps on how to create a Service Principal in preparation here.
  2. To invoke the PowerShell commands during deployment, you will need a deployment script. You can download the deploy.cmd that gets generated when you do a Kudu deployment, by going to https://<AppServiceName>.scm.azurewebsites.net/DebugConsole , navigating to D:\home\site\deployments\tools and then downloading the deploy.cmd via the download icon. Alternatively, you can generate a custom deploy.cmd by using the steps described here .
  3. To use the deployment script, you will need to add a .deployment file to the root of your repository (note that there is a dot (.) at the beginning of the filename), and point this configuration to the deploy.cmd as follows:
    [config]
    command = deploy.cmd
  4. Edit the deploy.cmd so that it stops the site at the beginning of deployment and starts the site after the deployment is completed:

 

  • To stop the site at the beginning of the deployment: place the following command near the top of the file (after the @if "%SCM_TRACE_LEVEL%" NEQ "4" @echo off line), replacing the values in single quotes (ie your service principal user name and password, tenant id, App Service's resource group, and App Service name):
    powershell -command "Login-AzureRmAccount -Credential (New-Object -TypeName pscredential –ArgumentList 'appId@domain.com', (ConvertTo-SecureString 'password' -AsPlainText –Force)) -ServicePrincipal –TenantId 'tenantId';Stop-AzureRmWebApp -ResourceGroupName 'resourcegroup' -Name 'mysite';"

 

  • To start the site at the end of the deployment: Place the following command at the end of the file (same as the above command, except this will be Start-AzureRmWebApp instead of Stop-AzureRmWebApp:
    powershell -command "Login-AzureRmAccount -Credential (New-Object -TypeName pscredential –ArgumentList 'appId@domain.com', (ConvertTo-SecureString 'password' -AsPlainText –Force)) -ServicePrincipal –TenantId 'tenantId';Start-AzureRmWebApp -ResourceGroupName 'resourcegroup' -Name 'mysite';"

 

You can also do variations on these commands, such as Stop-AzureRmWebAppSlot and Start-AzureRmWebAppSlot, and use this in combo with Auto Swap to avoid downtime in production while you stop a slot and deploy to the slot:

 

For example, to stop the slot:
powershell -command "Login-AzureRmAccount -Credential (New-Object -TypeName pscredential –ArgumentList 'appId@domain.com', (ConvertTo-SecureString 'password' -AsPlainText –Force)) -ServicePrincipal –TenantId 'tenantId';Stop-AzureRmWebAppSlot -ResourceGroupName 'resourcegroup' -Name 'mysite' -slot 'dev';"

 

To disable Always On on a slotless App Service:

powershell -command "Login-AzureRmAccount -Credential (New-Object -TypeName pscredential –ArgumentList 'appId@domain.com', (ConvertTo-SecureString 'password' -AsPlainText –Force)) -ServicePrincipal –TenantId 'tenantId';Set-AzureRMResource -PropertyObject @{siteConfig = @{AlwaysOn = $false}} -Name 'mysite' -ResourceGroupName 'resourcegroup' -ResourceType Microsoft.Web/sites -EA SilentlyContinue;"

 

To disable Always On on a slot:

powershell -command "Login-AzureRmAccount -Credential (New-Object -TypeName pscredential –ArgumentList 'appId@domain.com', (ConvertTo-SecureString 'password' -AsPlainText –Force)) -ServicePrincipal –TenantId 'tenantId';Set-AzureRMResource -PropertyObject @{siteConfig = @{AlwaysOn = $false}} -Name 'mysite/myslot' -ResourceGroupName 'resourcegroup' -ResourceType Microsoft.Web/sites/slots -EA SilentlyContinue;"

 

Note: replace $false with $true to re-enable Always On.

 

And of course you can combine stopping the site / disabling Always On, as well as starting the site / re-enabling Always On.

 

More information about customizing Kudu deployments can be found at the following links:

https://github.com/projectkudu/kudu/wiki/Customizing-deployments https://github.com/projectkudu/kudu/wiki/Custom-Deployment-Script

 

Further information about dealing with locked files during deployment can be found at the following link:

https://github.com/projectkudu/kudu/wiki/Dealing-with-locked-files-during-deployment