E. Erfan
E. Erfan

Reputation: 1401

Azure DevOps pipeline, how to write the condition for a stage template to run it for different environments

I have three stages, Dev, QA and Prod and I need to execute the same set of exactly same jobs for all three stages. I have made a stage template and I am calling it from my main Azure DevOps pipeline.

Depending on the build branch, I need to deploy either to only Dev, or to all three environments, Dev, QA and Prod. The issue is I don't know how to set this condition in the stage template.

Here is how the stage template looks like:

parameters:
- name: deployToDev
  displayName: 'Deploy to Dev?'
  type: boolean
  default: false

- name: deployToQA
  displayName: 'Deploy to QA?'
  type: boolean
  default: false

- name: deployToProd
  displayName: 'Deploy to Prod?'
  type: boolean
  default: false

- name: variableGroup
  displayName: 'Variable Group'
  type: string

- name: stageName
  displayName: 'Stage Name'
  type: string

stages:
- stage: ${{parameters.stageName}}
  condition: and(succeeded(), eq( ${{ parameters.deployToProd}}, false), eq( ${{parameters.deploytoQA}}, false) )  
  displayName: 'Deploy to ${{parameters.stageName}}'
  variables: 
  - group: ${{parameters.variableGroup}}
  dependsOn: Build
  jobs:
  - job: <job-name>
  ...

The main pipeline where I call the stage template (I only show the main parts):

pool: 
  vmImage: ubuntu-20.04

trigger:
  branches:
    include:
      - "feature/ORGTHDATAMA-4177"
      - "main"
    exclude:
      - "release"
  paths:
    include:
      - "core_py/*"

parameters:
- name: deployToDevPipelineLvl
  displayName: 'Deploy to Dev?'
  type: boolean
  default: false

- name: deployToQAPipelineLvl
  displayName: 'Deploy to QA?'
  type: boolean
  default: false

- name: deployToProdPipelineLvl
  displayName: 'Deploy to Prod?'
  type: boolean
  default: false

variables:
  pythonVersion: 3.8
  whlPackageName: py_whl_core
  srcDirectory: core_py/$(whlPackageName)
  ${{ if eq(variables['Build.SourceBranch'], 'refs/heads/main') }}:
    BuildFrom: main
    PackageName: $(whlPackageName)
    versionOption: custom
    parameters.deployToDevPipelineLvl: true
    parameters.deployToQAPipelineLvl: true
    parameters.deployToProdPipelineLvl: true
  ${{ else }}:
    BuildFrom: branch_${{ lower(variables['Build.SourceBranchName'] ) }}
    PackageName: $(whlPackageName)_$(BuildFrom)
    versionOption: patch
    parameters.deployToDevPipelineLvl: true
    parameters.deployToQAPipelineLvl: false
    parameters.deployToProdPipelineLvl: false

name: py_whl_build_$(Date:yyyyMMdd)_$(BuildFrom)

stages:
  - stage: Build
    displayName: 'Build Stage'

    jobs:
    - job: <job-name>
    ...


  - template: templates/azure-pipeline-stage-template-us4177.yaml
    parameters:
      deployToDev: ${{parameters.deployToDevPipelineLvl}}
      deployToQA: ${{parameters.deployToQAPipelineLvl}}
      deployToProd: ${{parameters.deployToProdPipelineLvl}}
      variableGroup: databricks-sp-vg-dev-4177
      stageName: DeployToDev

  - template: templates/azure-pipeline-stage-template-us4177.yaml
    parameters:
      deployToDev: ${{parameters.deployToDevPipelineLvl}}
      deployToQA: ${{parameters.deployToQAPipelineLvl}}
      deployToProd: ${{parameters.deployToProdPipelineLvl}}
      variableGroup: databricks-sp-vg-qa-4177
      stageName: 'DeployToQA'

  - template: templates/azure-pipeline-stage-template-us4177.yaml
    parameters:
      deployToDev: ${{parameters.deployToDevPipelineLvl}}
      deployToQA: ${{parameters.deployToQAPipelineLvl}}
      deployToProd: ${{parameters.deployToProdPipelineLvl}}
      variableGroup: databricks-sp-vg-prod-4177
      stageName: DeployToProd
      

As shown above, I am passing parameters from main pipeline to stage template.These parameters value varies depending on build source branch; I am checking the parameter values in stage template to decide for what environment it runs. The thing is when I run the pipeline from non-main branches the stage template runs for all three environments (Dev, QA and Prod) which is due to the condition.

Questions:

  1. How to correct the condition that it only runs for Dev if the build branch is not main? And how to run for all three environments, when the build branch is main?
  2. Is there a better way to set the parameters deployToDev, deployToQA, and deployToProd? Like having one parameter instead of three?
  3. Should the condition check be in stage at all or should it be in the main pipeline? If it is the better approach how should I implement it?

Upvotes: 3

Views: 6470

Answers (1)

Krzysztof Madej
Krzysztof Madej

Reputation: 40533

I run this:

        - pwsh: |
            echo "$(parameters.deployToDevPipelineLvl) - ${{parameters.deployToDevPipelineLvl}}"
            echo "$(parameters.deployToQAPipelineLvl) - ${{parameters.deployToDevPipelineLvl}}"
            echo "$(parameters.deployToProdPipelineLvl) - ${{parameters.deployToDevPipelineLvl}}"

on the main branch and I got:

true - False
true - False
true - False

And when I ran this on the non-main branch I got:

true - False
false - False
false - False

So your issue here is that you pass runtime parameters:

parameters:
- name: deployToDevPipelineLvl
  displayName: 'Deploy to Dev?'
  type: boolean
  default: false

then you set variables:

  ${{ if eq(variables['Build.SourceBranch'], 'refs/heads/main') }}:
    BuildFrom: main
    PackageName: $(whlPackageName)
    versionOption: custom
    parameters.deployToDevPipelineLvl: true
    parameters.deployToQAPipelineLvl: true
    parameters.deployToProdPipelineLvl: true
  ${{ else }}:
    BuildFrom: branch_${{ lower(variables['Build.SourceBranchName'] ) }}
    PackageName: $(whlPackageName)_$(BuildFrom)
    versionOption: patch
    parameters.deployToDevPipelineLvl: true
    parameters.deployToQAPipelineLvl: false
    parameters.deployToProdPipelineLvl: false

(this will not overwrite parameters).

To pass later parameters as template parameters:

  - template: templates/azure-pipeline-stage-template-us4177.yaml
    parameters:
      deployToDev: ${{parameters.deployToDevPipelineLvl}}
      deployToQA: ${{parameters.deployToQAPipelineLvl}}
      deployToProd: ${{parameters.deployToProdPipelineLvl}}
      variableGroup: databricks-sp-vg-qa-4177
      stageName: 'DeployToQA'

If you just want to run stages based on the branch you don't need all runtime parameters at the main pipeline file.

So with this pipeline:

pool: 
  vmImage: ubuntu-20.04

trigger:
  branches:
    include:
      - "feature/ORGTHDATAMA-4177"
      - "main"
    exclude:
      - "release"
  paths:
    include:
      - "core_py/*"

variables:
  pythonVersion: 3.8
  whlPackageName: py_whl_core
  srcDirectory: core_py/$(whlPackageName)
  ${{ if eq(variables['Build.SourceBranch'], 'refs/heads/main') }}:
    BuildFrom: main
    PackageName: $(whlPackageName)
    versionOption: custom
    deployToDevPipelineLvl: True
    deployToQAPipelineLvl: True
    deployToProdPipelineLvl: True
  ${{ else }}:
    BuildFrom: branch_${{ lower(variables['Build.SourceBranchName'] ) }}
    PackageName: $(whlPackageName)_$(BuildFrom)
    versionOption: patch
    deployToDevPipelineLvl: True
    deployToQAPipelineLvl: False
    deployToProdPipelineLvl: False

name: py_whl_build_$(Date:yyyyMMdd)_$(BuildFrom)

stages:
  - stage: Build
    displayName: 'Build Stage'

    jobs:
    - job: SomeJobe
      steps:
        - pwsh: |
            echo "$(deployToDevPipelineLvl) - ${{variables.deployToDevPipelineLvl}}"
            echo "$(deployToQAPipelineLvl) - ${{variables.deployToQAPipelineLvl}}"
            echo "$(deployToProdPipelineLvl) - ${{variables.deployToProdPipelineLvl}}"

            
  - template: template-file2.yml
    parameters:
      deploy: ${{variables.deployToDevPipelineLvl}}
      variableGroup: databricks-sp-vg-dev-4177
      stageName: DeployToDev

  - template: template-file2.yml
    parameters:
      deploy: ${{variables.deployToQAPipelineLvl}}
      variableGroup: databricks-sp-vg-qa-4177
      stageName: 'DeployToQA'

  - template: template-file2.yml
    parameters:
      deploy: ${{variables.deployToProdPipelineLvl}}
      variableGroup: databricks-sp-vg-prod-4177
      stageName: DeployToProd

and this template:

parameters:
- name: deploy
  displayName: 'Deploy'
  type: boolean
  default: false

- name: variableGroup
  displayName: 'Variable Group'
  type: string

- name: stageName
  displayName: 'Stage Name'
  type: string

stages:
- stage: ${{parameters.stageName}}
  condition: and(succeeded(), eq( ${{ parameters.deploy}}, true))  
  displayName: 'Deploy to ${{parameters.stageName}}'
  dependsOn: Build
  jobs:
  - job: Build
    steps:
    - pwsh: |
        echo "${{ parameters.deploy}}"
    

You will get on the main branch:

enter image description here

and this on non-main branch:

enter image description here

So you hot this pipeline without parameters at all.

Conditions on the template are good as having them on a higher level is impossible. You could have here if expression, but then you will change the structure of the pipeline (I mean you will not see skipped stages at all). Is it all up to you and your preference?

If you want to keep your runtime parameters:

parameters:
- name: deployToDevPipelineLvl
  displayName: 'Deploy to Dev?'
  type: boolean
  default: false

- name: deployToQAPipelineLvl
  displayName: 'Deploy to QA?'
  type: boolean
  default: false

- name: deployToProdPipelineLvl
  displayName: 'Deploy to Prod?'
  type: boolean
  default: false

pool: 
  vmImage: ubuntu-20.04

trigger:
  branches:
    include:
      - "feature/ORGTHDATAMA-4177"
      - "main"
    exclude:
      - "release"
  paths:
    include:
      - "core_py/*"

variables:
  pythonVersion: 3.8
  whlPackageName: py_whl_core
  srcDirectory: core_py/$(whlPackageName)
  ${{ if eq(variables['Build.SourceBranch'], 'refs/heads/main') }}:
    BuildFrom: main
    PackageName: $(whlPackageName)
    versionOption: custom
    deployToDevPipelineLvl: True
    deployToQAPipelineLvl: True
    deployToProdPipelineLvl: True
  ${{ else }}:
    BuildFrom: branch_${{ lower(variables['Build.SourceBranchName'] ) }}
    PackageName: $(whlPackageName)_$(BuildFrom)
    versionOption: patch
    deployToDevPipelineLvl: True
    deployToQAPipelineLvl: False
    deployToProdPipelineLvl: False

name: py_whl_build_$(Date:yyyyMMdd)_$(BuildFrom)

stages:
  - stage: Build
    displayName: 'Build Stage'

    jobs:
    - job: SomeJobe
      steps:
        - pwsh: |
            echo "$(deployToDevPipelineLvl) - ${{variables.deployToDevPipelineLvl}}"
            echo "$(deployToQAPipelineLvl) - ${{variables.deployToQAPipelineLvl}}"
            echo "$(deployToProdPipelineLvl) - ${{variables.deployToProdPipelineLvl}}"

            
  - template: template-file2.yml
    parameters:
      deploy: ${{ or(variables.deployToDevPipelineLvl, parameters.deployToDevPipelineLvl) }}
      variableGroup: databricks-sp-vg-dev-4177
      stageName: DeployToDev

  - template: template-file2.yml
    parameters:
      deploy: ${{ or(variables.deployToQAPipelineLvl, parameters.deployToQAPipelineLvl) }}
      variableGroup: databricks-sp-vg-qa-4177
      stageName: 'DeployToQA'

  - template: template-file2.yml
    parameters:
      deploy: ${{ or(variables.deployToProdPipelineLvl, parameters.deployToProdPipelineLvl) }}
      variableGroup: databricks-sp-vg-prod-4177
      stageName: DeployToProd

you need test your values like this ${{ or(variables.deployToProdPipelineLvl, parameters.deployToProdPipelineLvl) }}

Upvotes: 3

Related Questions