Friday 13 October 2023

Conditionally Passing Different Values to Template Parameters in Azure DevOps Pipelines

Parameters Azure pipeline template helps to achieve dynamic behaviours in pipeline templates. Passing different values for template parameters based on conditions, via a single usage of teplate will help to reduce duplication of usage of template in the pipeline. Let's look at how to conditionally pass different values to template paramters with a practical example.

The expected behavoir

Let's assume there are three stages in the pipeline.

  • Unit test stage - to build and unit test
  • Build and push Docker image stage
  • Deploy app stage

If we are in develop branch (refs/heads/develop) we want to run unit tests, build dcoker image and depending on both stages want to run a deploy.


If any other branch we just want to run build docker image and deploy stages.


How to do

To achieve above we have to have a conditional stage dependency set depending on the pipeline running branch.  The deploy app stage can take an input parameter to get the dependson stages as shown below. The default value is set as both the unit test and docker byuld stages. Instead it is possible to set it as [] to have no depends on stages.

parameters:
  - name: dependson
    type: object
    default:
      - build_and_unittest
      - build_and_push_docker_image
  - name: appname
    type: string

stages:
  - stage: deploy_app
    dependsOn: ${{ parameters.dependson }}
    displayName: 'Deploy App'
    jobs:
      - job: deploy_app
        displayName: 'Deploy App'
        pool:
          vmImage: 'ubuntu-latest'
        steps:
          - checkout: none
         
          - script: |
              echo "Deploy App ${{ parameters.appname }}"

The docker build and unit test stages are somple and defined as shown below. Note in both below stages there is no dependsOn stage set so they run parallelly once the pipeline is started running.

stages:
  - stage: build_and_push_docker_image
    dependsOn: []
    displayName: 'Build and Push Docker Image'
    jobs:
      - job: build_and_push_docker_image
        displayName: 'Build and Push Docker Image'
        pool:
          vmImage: 'ubuntu-latest'
        steps:
          - checkout: none
         
          - script: |
              echo "Build and Push Docker Image"


stages:
  - stage: build_and_unittest
    dependsOn: []
    displayName: 'Build and Unit Test'
    jobs:
      - job: build_and_unittest
        displayName: 'Build and Unit Test'
        pool:
          vmImage: 'ubuntu-latest'
        steps:
        - checkout: none
               
        - script: |
            echo "Build and Unit Test"


In the pipeline wich uses these three stages, unit test stage can be set to run only if the branch is develop as shown below.

stages:
  - ${{ if eq(variables['Build.SourceBranch'], 'refs/heads/develop') }}:
    - template: templates/stages/build_and_unittest.yml

Then when using the deploy app stage the depends on paramter can be set as conditionally as shown below. Note that unlike above condition we should not use a starting - when we set conditions for the parameters. If the branch is dev we pass both stages unit test and docker build as dependencies to the deploy app stage. If the the branch is not develop then just the build docker image passed as the dependson parameter value.

  - template: templates/stages/deploy_app.yml
    parameters:
      ${{ if eq(variables['Build.SourceBranch'], 'refs/heads/develop') }}:
        dependson:
          - build_and_unittest
          - build_and_push_docker_image
      ${{ else }}:
        dependson:
          - build_and_push_docker_image
      appname: 'myapp'

The pipline code using all stages is as below. This way we can use various different conditions in paramters passing to templates.

pool:
  vmimage: 'ubuntu-latest'

stages:
  - ${{ if eq(variables['Build.SourceBranch'], 'refs/heads/develop') }}:
    - template: templates/stages/build_and_unittest.yml
     
  - template: templates/stages/build_and_push_docker_image.yml
  - template: templates/stages/deploy_app.yml
    parameters:
      ${{ if eq(variables['Build.SourceBranch'], 'refs/heads/develop') }}:
        dependson:
          - build_and_unittest
          - build_and_push_docker_image
      ${{ else }}:
        dependson:
          - build_and_push_docker_image
      appname: 'myapp'


No comments:

Popular Posts