How to prevent multiple simultaneous deployments in Azure DevOps

How to prevent multiple simultaneous deployments in Azure DevOps

CI/CD has completely transformed the way we work today. Instead of releasing the software once a month or somewhere in the future, we now release it multiple times a day. In our environment, each merge to the main branch triggers a deployment to production. Consequently, if we approve and merge several PRs simultaneously, we may encounter a race condition when two or more deployments attempt to deploy to the same environment.

The solution to this problem is an Exclusive lock, which allows only one pipeline to proceed at a time. When multiple runs attempt to deploy, they may either wait (using a sequential lock) or be canceled, with only the latest one acquiring a lock (using a runLatest).

Now, let's examine the code snippet below:

    - main

  vmImage: ubuntu-latest

lockBehavior: runLatest

- stage: 'deployment'
  - deployment: 'deployment'
    environment: production
          - script: sleep 30

The setting lockBehavior: runLatest indicates that we want to run only the latest deployment, canceling any other concurrent deployments. sequential is another possible lock behavior that allows all runs to deploy in a line.

environment: production specifies the environment to which we intend to deploy. It is crucial to define this parameter, as environments are considered protected resources in Azure DevOps, and we can apply an Exclusive Lock to them.

If you run the pipeline created from this YAML file multiple times, you probably won't notice any difference, as all deployments will run concurrently. To rectify this, we need to set an exclusive lock on our production environment. To do so:

  • Navigate to Environments->production->'Approvals and checks' tab

  • Click Add check(+) button

  • Select Exclusive lock

  • Click Next button

  • Click Create button

Now the lock will take effect, and after running the pipeline multiple times, you will most likely observe that only the latest deployment is executed. If you need to deploy all runs concurrently, simply change lockBehavior to sequential.

Image credits: "Photo of Train on Train Tracks" by Benjamin Suter