Azure DevOps Pipelines: Automated build and deploy

When you check-in the code (or merge a pull request) of your ASP.NET WebApp or Api, it would be great when everything is build, tested and deployed to a test or staging site.

Azure Pipelines are a great service to do this. Combined with Deployment slots you can even swap the running staging site with your production site to have the new version instantly on-line. And when something goes wrong, just swap back to the old production site (well… on condition that you didn’t do a non-backward-compatible migration of your database or something like that).

There is an awfully lot of documentation about Azure Pipelines, because it can be used for many different techniques in many different ways, so it’s easy to get lost when you start. Also, the tools to help you with the setup work fine with a simple example solution, but when your solution is a little more complicated, they might fail. I will make these presumption (and add some links to help you find your way when your solution is different):

  • A solution with an ASP.NET Core WebApp and/or Api and one or more .NET Core or .NET Standard class libraries.
  • NuGet packages with their PackageReferences in the .csproj files (not in package.json).
  • An Azure DevOps Git repository.
  • Publish to a deployment slot of an Azure App Service.
  • Manual swap to production slot.

Table of contents

  1. Configure your App Service in the Azure Portal
    1. Add a deployment slot
    2. Create an Azure Service Connection
  2. Create the DevOps Pipeline
  3. Swap Staging to Production
  4. References

Configure your App Service in the Azure Portal

Add a deployment slot

  • Select your App Service in the Azure Portal (search for App services)
  • In the left pane, select Deployment slots > Add Slot.
  • Set the name of the slot (e.g. Staging). At Clone settings from: select your production/parent App Service to copy its settings. Click Add.
  • After creation click Close.
  • Click the newly created deployment slot name. Your slot is now shown as an App Service. The settings in the Configuration tab are copied from your parent App Service, but other settings not. Check for correct values.
  • If you use the Key Vault, make sure to turn on Managed identities for Azure resources: in the Identity section turn on Status in the System assigned tab and select Save. You also must enable the slot to use the KeyVault with its Managed Identity, see: Enable the WebApi/WebApp to use the KeyVault with its Managed Identity.
  • If you use AD B2C authentication, make sure to add the URL of the deployment slot to the callback urls of your AD B2C directory. See: TODO.

Create an Azure Service Connection

To deploy your app, your Azure DevOps Pipeline must have access to your Azure App Service. This is possible with an Azure Service Connection. I’ll describe here the simple approach to create one. For more advanced scenarios see: Connect to Microsoft Azure.

  • Open your Azure DevOps project.
  • Select Project Settings at the bottom - left.
  • Open the Service connections page.
  • Choose + New service connection (or Create service connection) and select Azure Resource Manager.
  • Select Service Principal Authentication (default).
  • Connection name: You can, for instance, use the name of the resource group of your App Service or the name of your Azure subscription.
  • Scope level: Subscription (default).
  • Subscription: Select your Azure subscription.
  • Resource Group: Select the Resource group of your App Service or leave blank for full subscription access.
  • Select OK to create the connection.

Create the DevOps Pipeline

A pipeline is defined in a YAML file called azure-pipelines.yml in the root of your repository. Azure DevOps can help you to create the YAML file (see: Create your first pipeline), but the result did not make me happy.

You can create an azure-pipelines.yml file like this in the root of your repository:

  - master # Branch to trigger the build

  vmImage: 'windows-latest'

  solution: '**/*.sln'
  buildConfiguration: 'Release'
  azureServiceConnection: 'connectionYouCreated'
  azureAppServiceName: 'yourAppService'
  deployToSlot: 'yourSlotName'
  projectToDeploy: 'yourWebAppOrApiProject.csproj'
  packageName: '' # Use the FOLDER name of the projectToDeploy.

- task: DotNetCoreCLI@2
  displayName: Build All $(buildConfiguration)
    command: build
    projects: '**/*.csproj'
    arguments: '--configuration $(buildConfiguration)'

- task: DotNetCoreCLI@2
  displayName: Zip $(projectToDeploy) to $(packageName)
    command: publish
    publishWebProjects: False
    projects: '**/$(projectToDeploy)'
    arguments: '--configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)'
    zipAfterPublish: True

- task: AzureWebApp@1
  displayName: Deploy to $(deployToSlot) slot
    azureSubscription: '$(azureServiceConnection)'
    appName: '$(azureAppServiceName)'
    slotName: '$(deployToSlot)'
    package: $(System.ArtifactsDirectory)/**/$(packageName)
    deploymentMethod: auto

Commit the created azure-pipelines.yml file and sync with your Azure DevOps Git repository.

DevOps Pipelines will recognize the file, add it as a pipeline and trigger the build the next time you sync (or merge a pull request) with your repository.

Swap Staging to Production

  • Select your App Service in the Azure Portal (search for App services)
  • Select the Deployment slots page.
  • Select Swap at the top of the page.
  • Select the source (e.g. Staging) and target (e.g. Production) slot and verify the Config Changes.
  • Press Swap to start the swapping.
  • When the swap is finished, the content of the Staging slot is now on the Production site, and the old content of the Production slot is on the Staging site.
  • When something is wrong, its easy to swap back the old Production version: just do the swap again.
  • You might want to add two extra deployment slots: One to deploy your automated build to, and one for staging/testing. Then you can safely swap your staging slot to production when testing is ready. The old production version will be in the staging slot and there is no risk for accidentally overwriting it with an automated build just when you want to swap back the old production version because of an unexpected issue…


Copyright © 2020-2024 Marcel Wolterbeek, Amsterdam, The Netherlands.
Source code and documentation licensed by a MIT license.