Blue-green Deployments in Azure DevOps YAML Pipelines

Blue-green Deployments in Azure DevOps YAML Pipelines

Zero Downtime Deployments with App Service Slots

  1. Creating a multi-stage YAML pipeline in Azure DevOps for .NET projects

  2. Running tests with code coverage in Azure DevOps YAML pipelines

  3. Static code analysis with NDepend in Azure Pipelines

  4. Running e2e tests with Playwright in Azure YAML Pipelines

  5. Publishing Playwright report as an artifact in Azure DevOps

  6. Bicep Infrastructure Deployment from Azure DevOps YAML Pipelines

  7. Blue-green Deployments in Azure DevOps YAML Pipelines

  8. Pre-Deployment Health Checks in Azure DevOps YAML Pipelines

  9. Azure DevOps Best Practices: Breaking Down the Monolithic YAML


Production issues causing downtime are a nightmare for developers, yet few consider the downtime resulting from new releases. Typically, companies attempt to identify periods with the lowest customer activity, send advance notifications about the upcoming release and its inevitable downtime, and consider it normal. However, I prefer utilizing blue-green deployment to achieve zero downtime. At its core, it's about having two production environments: one that's live ('blue') and one that's idle ('green'). When we want to release a new version of our app, we deploy it to the idle environment, test it out, and once confident, switch traffic to this new environment, either all at once or only to a few customers.

In this manner, customers won't notice that just a moment ago they were browsing the old version, and now they are on the new one, even during peak traffic hours. Sounds promising, right? Let's explore how we can implement this using App Service Slots.

Blue-green deployment using Azure App Service Slots

Azure App Service slots allow us to host multiple versions of a web app. Think of them like parallel universes for your app. For instance, in my ASP .NET Web API project, when I push changes, I deploy them to a staging slot first. It mirrors my production environment, ensuring everything works perfectly. Then, with the mere click of a button (or a few lines in the YAML pipeline), I swap the staging slot with the production one.

Let's see some code to understand this better. When you're directly deploying to an App Service this is how the YAML looks like:

- stage: deploy_app
  displayName: 'Deploy To App Service'
  jobs:
    - job: deploy
      steps:
        - task: DownloadPipelineArtifact@2
          displayName: 'Download pipeline artifact'
          inputs:
            buildType: 'current'
            artifactName: 'drop'
            targetPath: '$(Pipeline.Workspace)/drop'
        - task: AzureWebApp@1
          displayName: 'Deploy to app service'
          inputs:
            azureSubscription: 'AzureConnection'
            appType: 'webAppLinux'
            appName: 'bogdan-todo-app'
            package: '$(Pipeline.Workspace)/drop'
            startUpCommand: 'dotnet ToDoApp.Server.dll'      
            runtimeStack: 'DOTNETCORE|7.0'

The second task handles the deployment; however, during blue-green deployments, we aim to deploy to a staging slot first and then swap it with the production slot. To achieve this, we must initially create a slot for our app service.

Creating a deployment slot in Azure

Not all app service plans support deployment slots. If you have a plan lower than S1, you will encounter this message when clicking on "Deployment Slots" within your App Service.

upgrading to standard or premium plan to add deployment slots in Azure

Either click on upgrade or go to the "Scale up" section and ensure that you're on a tier that has deployment slots.

Once you did that, go to "Deployment slots" and click on "Add slot":

upgrading to standard or premium plan to add deployment slots in Azure

You will notice that there's already a slot called "PRODUCTION", so we will add our "STAGING" slot now. I'll call my slot "staging" and click on "Add" at the bottom of the drawer:

creating a staging slot in Azure

That's easy, but in the last post we went over Bicep infrastructure deployment, so I want to show you how to do it with Bicep as well:

Creating a deployment slot using Bicep

It's actually pretty simple, we just need to add a webAppSlot resource to our app.bicep file:

resource webAppSlot 'Microsoft.Web/sites/slots@2022-03-01' = {
  name: 'staging'
  location: location
  kind: 'app,linux'
  parent: webApp
  properties:{
    enabled: true
    httpsOnly: true
    siteConfig: {
      httpLoggingEnabled: true
      linuxFxVersion: 'DOTNETCORE|7.0'
    }
  }
}

If we push this then the pipeline will take care of applying the infrastructure updates and our slot will be created.

When the staging slot is created you will have two instances of your API running in parallel. In my case, one is bogdan-todo-app.azurewebsites.net (production) and the other one is bogdan-todo-app-staging.azurewebsites.net (staging)

Next, we'll deploy our app to the staging slot and then swap it with production.

Deploy to a staging slot from Azure Pipelines

Let me first show you the entire deployment stage and then I'll talk about what's happening:

- stage: deploy_app
  displayName: 'Deploy To App Service'
  jobs:
    - job: deploy
      steps:
        - task: DownloadPipelineArtifact@2
          displayName: 'Download pipeline artifact'
          inputs:
            buildType: 'current'
            artifactName: 'drop'
            targetPath: '$(Pipeline.Workspace)/drop'
        - task: AzureWebApp@1
          displayName: 'Deploy to staging slot'
          inputs:
            azureSubscription: 'AzureConnection'
            appType: 'webAppLinux'
            appName: 'bogdan-todo-app'
            package: '$(Pipeline.Workspace)/drop'
            deployToSlotOrASE: true
            slotName: 'staging'
            runtimeStack: 'DOTNETCORE|7.0'
            startUpCommand: 'dotnet ToDoApp.Server.dll'      
        - task: AzureAppServiceManage@0
          displayName: 'Swap slot'
          inputs:
            azureSubscription: 'AzureConnection'
            Action: 'Swap Slots'
            SourceSlot: 'staging'
            WebAppName: 'bogdan-todo-app'

First, we're not deploying to the app service directly anymore, instead, we're deploying to the staging slot. To do this we've added these two lines:

    deployToSlotOrASE: true
    slotName: 'staging'

Then, once the deployment is done, we need to swap the slots. This means that traffic from production will be redirected to the staging slot and staging will become the new production. For this we're going to use the 'Swap Slots' action like this:

Action: 'Swap Slots'
SourceSlot: 'staging'

Now your app was upgraded to a new version without any downtime! The full code is in this repository: https://dev.azure.com/bujdea/_git/AzureDevopsYamlPipeline and you can also access the pipeline:

Wrapping Up

By utilizing blue-green deployments with App Service slots, we can achieve zero-downtime releases and ensure users experience seamless transitions between app versions.

Azure App Service slots, in conjunction with Azure DevOps YAML pipelines, have made blue-green deployments incredibly easy for my team. Naturally, this is just a basic demonstration of deployment slots, but in future articles, we will delve into more advanced scenarios such as health checks, deployment slot settings, and more.

What's Next?

In the next article, I'll showcase how to incorporate health checks into our Azure DevOps pipeline. This ensures that we only swap slots when our deployment is successful, adding another layer of confidence to our release process.

Did you find this article valuable?

Support Bogdan Bujdea by becoming a sponsor. Any amount is appreciated!