Azure Pipelines YAML parameter inside parameter name (dynamic parameter name) - azure-devops

I have a YAML template with parameters:
- name: Deploy_Test1
type: boolean
default: false
- name: Tests
type: object
default:
- "Test1"
- "Test2"
After that I iterate the Tests with each:
- ${{ each test in parameters.Tests}}:
Inside the each I can get the test value with ${{test}}.
I want to use the parameter Deploy_Test1 but dynamically, for example:
echo ${{ parameters.Deploy_${{test}} }}
In the above syntax I get an error that is invalid.
Is there is a way or a workaround to do it?

You need to use two loops here and check if you find your key.
parameters:
- name: Deploy_Test1
type: boolean
default: false
- name: Tests
type: object
default:
- "Test1"
- "Test2"
trigger: none
pool:
vmImage: ubuntu-latest
steps:
- ${{ each test in parameters.Tests}}:
- ${{ each parameter in parameters }}:
- ${{ if eq(parameter.key, format('Deploy_{0}', test)) }}:
- script: echo ${{ parameter.value }}
And then you will get:

Related

How to pass a Pipeline variable as boolean to a template?

I am trying to pass a Pipeline variable (enable_datasync_job) that I have defined using the UI:
to a template that is used within my main pipeline azure-pipelines.yml:
name: MyRepo.Deployment.$(date:yy)$(DayOfYear)$(rev:.r)
...
jobs:
- job:
steps:
...
- template: azure-pipelines.yml#Common-YAML
parameters:
...
enable_datasync_job: $(enable_datasync_job)
The Common-YAML template defines that variable as boolean:
parameters:
...
- name: enable_datasync_job
type: boolean
default: false
When I try to run my main pipeline, it currently breaks as it completes that I am not passing a boolean value to my template
I know that all pipeline variables as of type string. How do I convert that string to a boolean so that my template accepts it?
If you define the parameter that is in the azure-pipelines.yml also as boolean you will get a checkbox (boolean) instead of inputbox (string):
You should be able to pass this along the Common-YAML.
Example code
The azure-pipelines.yml:
trigger:
- main
pool:
vmImage: ubuntu-latest
parameters:
- name: enable_datasync_job
type: boolean
default: false
extends:
template: boolean.yml
parameters:
enable_datasync_job: ${{ parameters.enable_datasync_job }}
The azure-pipelines.yml, without extends:
trigger:
- main
pool:
vmImage: ubuntu-latest
parameters:
- name: enable_datasync_job
type: boolean
default: false
steps:
- template: boolean.yml
parameters:
enable_datasync_job: ${{ parameters.enable_datasync_job }}
The boolean.yml (Common-YAML):
parameters:
- name: enable_datasync_job
type: boolean
default: false
steps:
- script: |
echo the value is ${{ parameters.enable_datasync_job }}
The result

How to convert string variable to object and pass it to another pipeline?

I have two Azure Devops pipelines: 'Starter' and 'Processing'. 'Starter' triggers 'Processing' and passes some parameters to it.
Starter:
trigger: none
pool:
vmImage: 'windows-2019'
stages:
- stage: A
jobs:
- template: Processing.yml
parameters:
products: $(Products)
creds: $(Creds)
Processing:
parameters:
- name: products
type: object
default: []
- name: creds
default: ''
jobs:
- ${{ each product in parameters.products }}:
- task: PowerShell#2
displayName: Importing ${{ product }} solution
inputs:
targetType: 'inline'
script: |
#Code
Key detail here is opportunity to loop through 'products' variable (each product in parameters.products), which must be setted in 'Starter' variables:
In other words, starting my pipeline I must pass list of products as 'string' and then loop through this list in second pipeline. 'Is it generally possible? Maybe products should be another type? I tried some work around but didn't get appropriate solution:
- job: Prepare_Products_Array
steps:
- task: PowerShell#2
inputs:
targetType: 'inline'
script: |
# Write your PowerShell commands here.
$productsArray = []
$productsArray = $(Products)
$productsArray = $productsArray.Split(',')
Write-Host ("##vso[task.setvariable variable=productsArray;]$productsArray")
- template: Processing.yml
parameters:
products: $env:productsArray
Exception:
From your yaml sample, you are defining the variable in YAML Pipeline UI and using parameters in YAML Template.
In this case, the variables defined on the UI will be assigned at runtime, but the parameters and expressions in the YAML template will be expanded at compile time.
Therefore, YAML UI variables cannot be passed to the Pipeline YAML Template.
And it will show the error:
Expected a...... Actual value $(Product)
This means that the pipeline variable not expanded at compile time.
I am afraid that there is no such method can pass the UI Pipeline variable to YAML Template.
Here are the workarounds:
Method 1 : You can use parameters in Starter yaml to pass the Object type parameters to YAML template.
Starter:
trigger: none
parameters:
- name: products
type: object
default: []
- name: creds
default: ''
pool:
vmImage: 'windows-2019'
stages:
- stage: A
jobs:
- template: Processing.yml
parameters:
products: ${{ parameters.products }}
creds: ${{ parameters.creds }}
Processing:
parameters:
- name: products
type: object
default: []
- name: creds
default: ''
jobs:
- job: test
steps:
- ${{ each product in parameters.products }}:
- task: PowerShell#2
displayName: Importing ${{ product }} solution
inputs:
targetType: 'inline'
script: |
echo ${{ product }}
Result: You can input the value when you run the pipeline.
Method2: You need to define the variable in Starter pipeline and change the products parameters as String type. Then you can use the expression - ${{ each product in split(parameters.products, ',')}}: to split the string.
Starter:
pool:
vmImage: 'windows-2019'
variables:
products: '1,2,3'
creds: test
stages:
- stage: A
jobs:
- template: Processing.yml
parameters:
products: ${{ variables.products }}
creds: ${{ variables.creds }}
Processing:
parameters:
- name: products
type: string
default: ''
- name: creds
default: ''
jobs:
- job: test
steps:
- ${{ each product in split(parameters.products, ',')}}:
- task: PowerShell#2
displayName: Importing ${{ product }} solution
inputs:
targetType: 'inline'
script: |
echo ${{ product }}

Using if else conditions within a Azure devops yaml pipeline template

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

AzureDevops set variables based on parameter and pass to template at next stage

In Azure DevOps, I'm trying to create a pipeline which offers a simple selection of pre-set options to the user running it. These options will be converted into different combinations of parameters as specified by a templated stage (the definition of which, I have no control over). The idea of my pipeline is that frequently-used build configurations are easy to select correctly, rather than having to manually set 3 or 4 different parameters.
I need the "Build.Setup" from immutable_pipeline to print config_one, profile_one when the first radio is selected (buildType=type1), config_two, profile_two when buildType=type2, and so on.
Unfortunately I'm really struggling to get any variable value into the templated stage other than the defaults. Are ADO variables even mutable variables at all - or just constants?
I've read the MS docs extensively and understand the meaings of the different macro declaration types. I've tried many different combinations of syntaxes ${{...}}, $(...) and $[...], all behave differently but none seems able to deliver what's needed. Is this even possible? Is there a simple solution someone can suggest?
Pipeline:
name: $(Date:yyyyMMdd).$(Rev:r)
parameters:
- name: buildType
displayName: 'Type of build'
type: string
default: 'type3'
values: ['type1', 'type2', 'type3']
pool:
name: default
variables:
- name: config
value: 'defaultConfig'
- name: profile
value: 'defaultProfile'
stages:
- stage: Stage1
displayName: Prepare build config
jobs:
- job: Job1_1
steps:
- checkout: none
- task: Bash#3
name: SetVariables
inputs:
targetType: inline
script: |
p1='${{ parameters.buildType }}'
v1='$(config)'
v2='$(profile)'
echo -e "BEFORE: p1='${p1}'\n v1='${v1}'\n v2='${v2}'"
case ${p1} in
type1)
v1='config_one'
v2='profile_one'
;;
type2)
v1='config_two'
v2='profile_two'
;;
type3)
v1='config_three'
v2='profile_three'
;;
esac
echo -e "AFTER: p1='${p1}'\n v1='${v1}'\n v2='${v2}'"
echo "##vso[task.setvariable variable=config]${v1}"
echo "##vso[task.setvariable variable=profile;isOutput=True]${v2}"
- job: Job1_2
dependsOn: Job1_1
variables:
- name: variable1
value: $(config)
- name: variable2
value: $[ dependencies.Job1_1.outputs['SetVariables.profile']]
steps:
- task: Bash#3
name: GetVariables2
inputs:
targetType: inline
script: |
echo -e 'SAME STAGE: v1="$(variable1)"\n v2="$(variable2)"'
# Next stage - use computed values for "config" and "profile"
- template: templates/immutable_pipeline.yml
parameters:
config: $(config)
profile: ${{ variables.profile }}
templates/immutable_pipeline.yml:
Note that I don't have access to change this, I can't make it dependsOn: Stage1.Job1_1.
parameters:
- name: config
displayName: 'Config'
type: string
default: 'unset'
- name: profile
displayName: 'Profile'
type: string
default: 'unset'
stages:
- stage: Build
displayName: Templated build
jobs:
- job: Setup
pool:
name: default
demands:
- Agent.OS -equals Linux
steps:
- checkout: none
- script: |
echo '##[info] parameters.config=${{ parameters.config }}'
echo '##[info] parameters.profile=${{ parameters.profile }}'
I just found one solution (which is arguably simpler than using variables) using the ${{ if eq(...) }}: syntax:
name: $(Date:yyyyMMdd).$(Rev:r)
parameters:
- name: buildType
displayName: 'Type of build'
type: string
default: 'type3'
values: ['type1', 'type2', 'type3']
pool:
name: default
stages:
- template: templates/immutable_pipeline.yml
${{ if eq(parameters.buildType, 'type1') }}:
parameters:
config: config_one
profile: profile_one
${{ if eq(parameters.buildType, 'type2') }}:
parameters:
config: config_two
profile: profile_two
${{ if eq(parameters.buildType, 'type3') }}:
parameters:
config: config_three
profile: profile_three
Still interested in whether the original approach of setting variables is even possible, if only beause I've spent so much time on it.

How to check if array contains string with Azure DevOps pipeline expressions

I have the following pipeline template that I want to use to conditionally execute stages based on the input parameter stages.
parameters:
- name: dryRun
default: false
type: boolean
- name: path
type: string
default: terraform
- name: stages
type: object
default:
- test
- prod
stages:
- stage:
pool:
vmImage: 'ubuntu-latest'
displayName: "Deploy to test"
condition: in('test', ${{ parameters.stages }})
jobs:
- template: terraform-job.yaml
parameters:
stage: test
path: ${{ parameters.path }}
dryRun: ${{ parameters.dryRun }}
- stage:
pool:
vmImage: 'ubuntu-latest'
displayName: "Deploy to production"
condition: in('prod', '${{ join(', ', parameters.stages) }}')
jobs:
- template: terraform-job.yaml
parameters:
stage: production
path: ${{ parameters.path }}
dryRun: ${{ parameters.dryRun }}
In the example you can see two of the approached I tried (I tried a lot...). The last one (in('prod', '${{ join(', ', parameters.stages) }}')) actually compiles but then the check only works partially as the array gets converted into a single string: 'test,prod' which will fail the in('test', 'test,prod') check.
And the first example (in('test', ${{ parameters.stages }})) is the one I think should work with logical thinking but I get the following error when compiling the template: /terraform-deployment.yml (Line: 19, Col: 16): Unable to convert from Array to String. Value: Array.
So now the question:
How do I check if a string is part of an array that was defined as a parameter?
2022 update
You can now use containsValue:
condition: ${{ containsValue(parameters.stages, 'test') }}
Try contains instead:
condition: contains('${{ join(';',parameters.stages) }}', 'test')