Using if else conditions within a Azure devops yaml pipeline template - azure-devops

Essentially I am trying to use a if else logic inside a yaml template instead of the caller pipeline.
I have following two pipelines azure-caller.yml and template.yaml
azure-caller.yml
parameters:
- name: test
displayName: 'select true or false'
values:
- true
- false
variables:
- name: test-true
${{ if eq(parameters.test, 'true') }}:
value: false
${{ elseif eq(parameters.test, 'false') }}:
value: true
stages:
- template: job-templates/template.yml
parameters:
testrue: $(test-true)
template.yml
parameters:
testrue: test_true
stages:
- stage: A
jobs:
- job: JA
steps:
- script: |
echo "Reverted value is" ${{ parameters.testrue }}
name: DetermineResult
How can I move the if else logic in the template.yml instead of azure-caller.yml? Your input will be helpful. thx

Below pipeline should do the trick with a limitation that the scope of variable test-true in the template will be limited only for stage: A
azure-caller.yml
parameters:
- name: test
displayName: 'select true or false'
values:
- true
- false
stages:
- template: job-templates/template.yml
parameters:
testrue: ${{ parameters.test }}
template.yml
parameters:
testrue: default
stages:
- stage: A
variables:
- name: test-true
${{ if eq(parameters.testrue, 'true') }}:
value: NewValueForTrueParam
${{ elseif eq(parameters.testrue, 'false') }}:
value: NewValueForFalseParam
jobs:
- job: JA
steps:
- script: |
echo "Echo value is" $(test-true)
Result

Related

Not able to set variable based on parameters in YAML

I'm trying to build a new yaml file that reads keyvault secrets based on the parameters at the runtime and declared variables with the condition as per the parameters, but this isn't working.
- name: azure_subscription
displayName: " Select subscription "
type: string
default: "service-connection-dev"
values:
- 'service-connection-dev'
- 'service-connection-sit'
- 'service-connection-tes'
- 'service-connection-prd'
variables:
- ${{ if eq('${{ parameters.azure_subscription }}', 'service-connection-sit') }}:
name: key_vault
value: 'core-kv-sit'
- ${{ if eq('${{ parameters.azure_subscription }}', 'service-connection-dev') }}:
name: key_vault
value: 'core-kv-dev'
stages:
- stage: Validate
${{ if eq(parameters.azure_subscription, 'service-connection-dev') }}:
pool:
name: agent-pool-win-dev
${{ if eq(parameters.azure_subscription, 'service-connection-sit') }}:
pool:
name: agent-pool-win-sit
jobs:
- job: Validate
steps:
- task: AzureKeyVault#2
inputs:
KeyVaultName: "${{variables.key_vault}}"
SecretsFilter: "*"
RunAsPreJob: false
azureSubscription: ${{ parameters.azure_subscription }}
I've tried using variables inside jobs, but that is also not working. Can someone please help?
Also, I'll have to declare 2 more variables as per the parameters input, Is it possible ?
Thanks in advance
- ${{ if eq('${{ parameters.azure_subscription }}', 'service-connection-sit') }}:
You're using a literal value of "${{ parameters.azure_subscription }}" for the left side of the comparison. The comparison should just be parameters.azure_subscription.
So: - ${{ if eq(parameters.azure_subscription, 'service-connection-sit') }}:

Azure devops yaml, filter parameter with string

Short question: how do I do the "enable" part in the pipeline below?
Long question: I have 3 jobs that I need to generate dynamically based on the parameters "params" due to business requirements. The job generated only works if the Job1, Job2 or Job3 is selected as true. Can I access parameters using string like dynamic in c#? ie: parameters["Job1"] or parameters["Job2"] or parameters["Job3"] ?
trigger: none
#Package Parameter
parameters:
- name: Job1
displayName: 'Job1'
type: boolean
default: false
- name: Job2
displayName: 'Job2'
type: boolean
default: false
- name: Job3
displayName: 'All Teams'
type: boolean
default: false
- name: 'params'
type: object
default: ["Job1",
"Job2",
"Job3"]
pool:
vmImage: ubuntu-latest
stages:
- stage: Build_dev
dependsOn:
jobs:
- ${{ each testJob in parameters.params }}:
- job:
enable: **If(parameters[testJob] == true) then enable this job**
displayName: Build ${{ testJob }}
steps:
- script: Build ${{ testJob }}
This is one way you can disable the jobs that are not set as true in the parameters. You can also use conditions for your jobs to skip them
azure-pipeline.yml
trigger: none
#Package Parameter
parameters:
- name: Job1
displayName: 'Job1'
type: boolean
default: false
- name: Job2
displayName: 'Job2'
type: boolean
default: false
- name: Job3
displayName: 'All Teams'
type: boolean
default: false
pool:
vmImage: ubuntu-latest
stages:
- stage: Build_dev
dependsOn: []
jobs:
- ${{ each parameter in parameters }}:
- ${{ if eq(parameter.Value, 'true') }}:
- template: job-template.yml
parameters:
jobName: ${{ parameter.Key }}
job-template.yml
parameters:
jobName: ''
jobs:
- job: ${{ parameters.jobName }}
displayName: Build ${{ parameters.jobName }}
steps:
- script: echo "Build ${{ parameters.jobName }}"

Azure Devops Yaml using Dynamic "each" condition

How do I do the condition part in the code below under "Approval_Test" stage? I have to generate the condition dynamically , can't hardcode it due to some requirements. The stage will also be generated dynamically based on the parameters param.
"Approval_Test" stage can only be run after all solution1, 2 and 3 are finished and/or skipped
#azure-pipeline.yml
trigger: none
#Package Parameter
parameters:
- name: "params"
type: object
default:
Solution1:
name: "Solution1"
Solution2:
name: "Solution2"
Solution3:
name: "Solution3"
stages:
- ${{ each param in parameters.params }}:
- stage: Deploy_dev_${{ param.value.name }}
jobs:
- template: deploy-dev.yml
- stage: Approval_Test
dependsOn:
- ${{ each param2 in parameters.params }}:
- Deploy_dev_${{ param2.value.name }}
condition: |
#The "each" below would not work and throw errors.
and
(
- ${{ each param2 in parameters.params }}:
in(dependencies.Deploy_dev_${{ param2.value.name }}.result, 'Succeeded', 'SucceededWithIssues', 'Skipped'),\
)
jobs:
- deployment: Approval
environment: 'sandbox'
This was a tough nut to crack and I wasn't able to solve it with the each keyword, but please let me present a feasible alternative.
Alternate solution: Job status check functions
Without your requirement of also having the skipped stages the solution would be adding condition: succeeded() to the stage: Approval_Test :
But this doesn't work for a skipped stage.
Not Failed?
Unfortunately there is no 'Skipped' to check from the job status functions, but what if we take the opposite of failed(), not(failed())!?
The works out fine:
#azure-pipeline.yml
trigger: none
#Package Parameter
parameters:
- name: "params"
type: object
default:
Solution1:
name: "Solution1"
Solution2:
name: "Solution2"
Solution3:
name: "Solution3"
stages:
- ${{ each param in parameters.params }}:
- stage: Deploy_dev_${{ param.value.name }}
jobs:
- template: deploy-dev.yml
- stage: skipped # test to simulate a skip from the template
condition: failed()
jobs:
- job: concat
steps:
- ${{ each parameter in parameters.params }}:
- script: echo Deploy_dev_${{ parameter.value.name }}.result
- stage: Approval_Test
condition: not(failed())
dependsOn:
- skipped # test to simulate a skip from the template
- ${{ each param2 in parameters.params }}:
- Deploy_dev_${{ param2.value.name }}
jobs:
- deployment: Approval
environment: 'sandbox'
(Don't) use the equivalent
According to the docs failed() is a equivalent for eq(variables['Agent.JobStatus'], 'Failed').
Would eq(variables['Agent.JobStatus'], 'Succeeded', 'SucceededWithIssues', 'Skipped') not be the solution then?
No!
Why I don't understand, but in my tests (above) with the yaml (below) it wouldn't fly.
#azure-pipeline.yml
trigger: none
#Package Parameter
parameters:
- name: "params"
type: object
default:
Solution1:
name: "Solution1"
Solution2:
name: "Solution2"
Solution3:
name: "Solution3"
stages:
- ${{ each param in parameters.params }}:
- stage: Deploy_dev_${{ param.value.name }}
jobs:
- template: deploy-dev.yml
- stage: skipped # test to simulate a skip from the template
condition: failed()
jobs:
- job: concat
steps:
- ${{ each parameter in parameters.params }}:
- script: echo Deploy_dev_${{ parameter.value.name }}.result
- stage: Approval_Test
condition: in(variables['Agent.JobStatus'], 'Succeeded', 'SucceededWithIssues', 'Skipped')
dependsOn:
- skipped # test to simulate a skip from the template
- ${{ each param2 in parameters.params }}:
- Deploy_dev_${{ param2.value.name }}
jobs:
- deployment: Approval
environment: 'sandbox'
Conclusion
If not(failed()) works for you, I advice you to use that.

How to use a dependsOn for the previous loop in Azure Devops YAML pipeline while using a each argument

I have a pipeline setup that is very basic and using the below template
parameters:
- name: countries
type: object
default:
- country: 'uk'
envs: ['development', 'preproduction', 'production']
- country: 'us'
envs: ['development', 'preproduction', 'production']
stages:
- ${{ each c in parameters.countries }}:
- stage: ${{ c.country }}
displayName: ${{ c.country }}
# some variable here to control if to use [] to deploy to all stages or just one
dependsOn: []
jobs:
- ${{ each env in c.envs }}:
- job: ${{ c.country }}_${{ env }}
steps:
- script: echo "Hello world"
condition: contains(variables['System.JobDisplayName'], 'production')
- script: echo "Hello world 2"
I want to have it so it only deploys to the new job loop in the pipeline if the previous job loop has succeeded. I can't seem to figure out a way of doing this. Any help will be appreicated. Thanks!
I managed to achieve this with the below job spec
- job: ${{ c.country }}_${{ env }}
${{if eq(env, 'preproduction')}}:
condition: eq(dependencies.${{ c.country }}_development.result, 'Succeeded')
dependsOn: ${{ c.country }}_development
${{if eq(env, 'production')}}:
condition: eq(dependencies.${{ c.country }}_preproduction.result, 'Succeeded')
dependsOn: ${{ c.country }}_preproduction
steps:

It doesn't work pass variable from one stage to another and iterate with an each loop

I need to do an each from a variable obtained with stageDependencies previously.
It doesn't work Pass variable from one stage to another and iterate with an each loop using a split function
What am I doing wrong?
Code:
task.yml
steps:
- task: Bash#3
name: env_string
inputs:
targetType: 'inline'
script: |
environmentsStr='dev,prd'
echo "##vso[task.setvariable variable=environmentsString;isOutput=true]$environmentsStr"
pipeline
- stage: stage1
jobs:
- job: job_stage1
steps:
- template: tasks.yml
- stage: stage2
dependsOn:
- stage1
variables:
- name: envFromVar
value: 'dev,prd'
- name: envFromStageDependencies
value: $[ stageDependencies.stage1.job_stage1.outputs['env_string.environmentsString'] ]
jobs:
- job: job_stage2
steps:
- template: stage3.yml
parameters:
envFromStageDependencies: $(envFromStageDependencies)
environmentsFromVar: ${{ variables.envFromVar }}
stage3.yml
parameters:
- name: envFromStageDependencies
type: string
- name: environmentsFromVar
type: string
steps:
- ${{ each environment in split(parameters.envFromStageDependencies, ',') }}:
- bash: |
echo "env ${{ environment }}" # OUTPUT FAIL; (env dev,prd)
- ${{ each environment in split(parameters.environmentsFromVar, ',') }}:
- bash: |
echo "envVar ${{ environment }}" # OUTPUT OK, 2 iterations envVar [dev,prd]
OUTPUT
BASH -> env dev,prd
BASH -> envVar dev
BASH -> envVar prd
You should change your pipeline YAML like as below:
. . .
jobs:
- job: job_stage2
steps:
- template: stage3.yml
parameters:
environments: ${{ variables.envFromStageDependencies }}
environmentsFromVar: ${{ variables.envFromVar }}
As #Daniel Mann has mentioned, the template is processed at compile time which is before runtime. So, to pass the variable values into the template, you should use the compile time variable syntax (${{ variables.varName }}) instead of runtime variable syntax ($(varName)).
For more details, you can reference the document "Understand variable syntax".