Search code examples
azure-devopsconditional-statementsyamlpipeline

How can I Select which Environment to run from a single stage in an Azure Pipeline with conditionals or another way


I have a pipeline in Azure Devops that runs two Environments, QA and DEV and I need it to select on Azure DevOps which environment to run from a single stage instead of using two separate stages for QA and DEV

Here's the stage code that I'm using for both environments:

# BUILD ENVIRONMENTS
- stage: build_envs
  dependsOn: quality_control
  displayName: Build Envs
  condition: |
   or 
    (
      and(eq(variables.isQa, true), ne(variables['Build.Reason'], 'PullRequest')),  eq(variables['Build.Reason'], 'Manual'),
      and(eq(variables.isDev, true), ne(variables['Build.Reason'], 'PullRequest')),  eq(variables['Build.Reason'], 'Manual')
    )
  jobs:
  - job: Job_build 
    displayName:  Application Build
    steps:
    - ${{ if eq(variables.isQa, true) }}:
      - checkout: backend-qa
        path: enext-data-platform-backend
      - script: rm -rf enext-data-platform-backend/enext-data-platform-frontend
      - checkout: frontend-qa
        path: enext-data-platform-backend/enext-data-platform-frontend
          #submodules: true
      - script: |
          ls -la $(Pipeline.Workspace)/enext-data-platform-backend
          ls -la $(Pipeline.Workspace)/enext-data-platform-backend/enext-data-platform-frontend
      - powershell: |
          Write-Host "##vso[build.addbuildtag]qa"
      #displayName: Application Build DEV
    - ${{ elseif eq(variables.isDev, true) }}:
      - checkout: backend-dev
        submodules: true
      - script: git rev-parse HEAD
      - script: ls -la   
      - powershell: |
          Write-Host "##vso[build.addbuildtag]dev"
      #displayName: 'Build Tag'

    - task: npmAuthenticate@0
      displayName: npm Authenticate enext-data-platform-backend/.npmrc
      inputs:
        ${{ if eq(variables.isQa, true) }}:
          workingFile: $(Pipeline.Workspace)/enext-data-platform-backend/.npmrc
        ${{ elseif eq(variables.isDev, true) }}:
          workingFile: .npmrc
      

    - task: npmAuthenticate@0
      displayName: npm Authenticate enext-data-platform-backend/enext-data-platform-frontend/.npmrc
      inputs:
        ${{ if eq(variables.isQa, true) }}:
          workingFile: $(Pipeline.Workspace)/enext-data-platform-backend/enext-data-platform-frontend/.npmrc
        ${{ elseif eq(variables.isDev, true) }}:
          workingFile: enext-data-platform-frontend/.npmrc
    
    - task: Docker@0
      displayName: 'Build an image'
      inputs:
        command: 'buildAndPush'
        containerregistrytype: 'Container Registry'
        imageName: "$(Build.Repository.Name):$(Build.BuildId)"
        ${{ if eq(variables.isQa, true) }}: 
          buildContext: $(Pipeline.Workspace)/enext-data-platform-backend/
          Dockerfile: $(Pipeline.Workspace)/enext-data-platform-backend/Dockerfile
        ${{ elseif eq(variables.isDev, true) }}:
          buildContext: enext-data-platform-backend
          Dockerfile: 'Dockerfile'

    - script: |
        set -x
        env
        pwd
        echo "CONTAINER_APP=$(Build.Repository. Name):$(Build.BuildId)" > artifact.txt
        source ./artifact.txt
        docker save $CONTAINER_APP > container.tar
        ls -la
      displayName: 'Shell Script: APP'

    - task: PublishPipelineArtifact@1
      displayName: 'Publish Pipeline Artifact - container id'
      inputs:
        targetPath: artifact.txt
        artifact: artifact

    - task: PublishPipelineArtifact@1
      displayName: 'Publish Pipeline Artifact - container data'
      inputs:
        targetPath: container.tar
        artifact: container

    - task: Docker@0
      displayName: 'Build an image'
      inputs:
        command: 'buildAndPush'
        containerregistrytype: 'Container Registry'
        imageName: "$(Build.Repository.Name):$(Build.BuildId)"
        ${{ if eq(variables.isQa, true) }}: 
          buildContext: $(Pipeline.Workspace)/enext-data-platform-backend/
          Dockerfile: $(Pipeline.Workspace)/enext-data-platform-backend/Dockerfile
        ${{ elseif eq(variables.isDev, true) }}:
          buildContext: enext-data-platform-backend
          Dockerfile: 'Dockerfile'

    - script: | 
        echo "CONTAINER_KNEX=$(Build.Repository.Name):$(Build.BuildId)" > artifact-knex.txt
        source ./artifact-knex.txt
        docker save $CONTAINER_KNEX > container-knex.tar
        ls -la 
      displayName: 'Shell Script: knex'

    - task: PublishPipelineArtifact@1
      displayName: 'Publish Pipeline Artifact - container id: knex'
      inputs:
        targetPath: artifact-knex.txt
        artifact: artifact-knex
    - task: PublishPipelineArtifact@1
      displayName: 'Publish Pipeline Artifact - container data: knex'
      inputs:
        targetPath: container-knex.tar
        artifact: container-knex 

  - job: dbschema
    displayName:  DB Schema
    steps:
    - checkout: dbschema
      submodules: true
    - script: ls -la

    - task: Docker@0
      displayName: 'Build an image'
      inputs:
        command: 'buildAndPush'
        buildContext: enext-matflow-dbschema
        containerregistrytype: 'Container Registry'
        imageName: "$(Build.Repository.Name):$(Build.BuildId)"
        Dockerfile: 'Dockerfile'

    - script: |
        echo "CONTAINER_DB=$(Build.Repository.Name):$(Build.BuildId)" > sql_artifact.txt
        source ./sql_artifact.txt
        docker save $CONTAINER_DB > sql_container.tar
        ls -la
      displayName: 'Shell Script: DB'
    
    - task: PublishPipelineArtifact@1
      displayName: 'Publish Pipeline Artifact - sql container id'
      inputs:
        targetPath: sql_artifact.txt
        artifact: sql_artifact

    - task: PublishPipelineArtifact@1
      displayName: 'Publish Pipeline Artifact - sql container data'
      inputs:
        targetPath: sql_container.tar
        artifact: sql_container

And in Azure DevOps when I run the pipeline looks like this:

enter image description here

And it runs the two environments at the same time, and what I'm trying to do is that I can select on Azure DevOPs which Env to run like this:

enter image description here

As of now I'm trying with conditionals but I'm very lost.

I've been trying with conditionals but I'm really new to Azure and I don't know how to set them in a correct way


Solution

  • The Stages to run function only displays each - stage under stages of a YAML definition. From the YAML definition you shared, it only contained one single stage - stage: build_envs and ${if} insertions will not expand one stage into two.

    For this, you may consider using ${each} expression to expand the stage. Please refer to the sample below. Since there are only two environments, you may also consider evaluating the value of an object type of parameter.

    parameters:
    - name: Envs
      type: object
      default:
        - QA
        - DEV
      
    stages:
    - stage: quality_control
      displayName: Quality Control
      jobs:
      - job: Job1
        steps:
        - checkout: none
    # BUILD ENVIRONMENTS
    - ${{ each env in parameters.Envs }}:
      - stage: build_${{ env }}
        dependsOn: quality_control
        displayName: Build ${{ env }}
        condition: eq(variables['Build.Reason'], 'Manual')
        jobs:
        - job: Job_build 
          displayName:  Application Build
          steps:
          - ${{ if eq(env, 'QA') }}:
            - checkout: backend-qa
              path: enext-data-platform-backend
            - script: echo ${{ env }}
          - ${{ elseif eq(env, 'Dev') }}:
            - checkout: backend-dev
              submodules: true
            - script: echo ${{ env }}
    
    - stage: publish_artifact
      displayName: Pulish Artifact
      jobs:
      - job: Job1
        steps:
        - checkout: none
    

    enter image description here

    Besides, since your pipeline will use either QA or DEV environment and the build_envs stage is only executed when the pipeline is manually triggered, you may consider setting Envs parameter value at queue time and run different sets of steps accordingly. Here is another sample for your reference.

    parameters:
    - name: Envs
      type: string
      default: QA
      values:
      - QA
      - DEV
    
    stages:
    - stage: quality_control
      displayName: Quality Control
      jobs:
      - job: Job1
        steps:
        - checkout: none
    # BUILD ENVIRONMENTS
    - stage: build_${{ parameters.Envs}}
      dependsOn: quality_control
      displayName: Build ${{ parameters.Envs }}
      condition: eq(variables['Build.Reason'], 'Manual')
      jobs:
      - job: Job_build 
        displayName:  Application Build
        steps:
        - ${{ if eq(parameters.Envs, 'QA') }}:
          - checkout: backend-qa
            path: enext-data-platform-backend
          - script: echo ${{ parameters.Envs}}
        - ${{ elseif eq(parameters.Envs, 'Dev') }}:
          - checkout: backend-dev
            submodules: true
          - script: echo ${{ parameters.Envs }}
    

    enter image description here