Azure DevOps Pipeline Parsing key value pairs from template parameters to create new string variable - azure-devops

I would like to pass some key-value pair objects as parameters to my template from the below pipeline code.
azure-pipeline.yml
jobs:
- template: custom-template.yml
parameters:
customProperties:
- key1: value1
- key2: value2
Below is the pseudo code as part of my template.
custom-template.yml
parameters:
- name: customProperties
type: object
default: []
jobs:
- job: MyJob
steps:
- task: CmdLine#2
displayName: "Get all parameter values"
inputs:
targetType: "inline"
script: |
args = (${{ parameters.customProperties }} ) #TODO: loop key value pairs, create variable.
echo "##vso[task.setvariable variable=ARGS;isoutput=true]args"
- task: CmdLine#2
displayName: "Use the parameter values"
inputs:
targetType: "inline"
script: |
echo $(ARGS) # from previous task
With the above pipeline object input, in my actual template processing, I want to form a string variable in the format as below and use that value in the subsequent steps in my template.
-Dkey1=value1 -Dkey2=value2
There are several examples to loop through the parameter values and conditionally include other tasks. But my requirement is to create a variable from the parameter values.

Related

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 JSON to declare variables in a YAML

I have a JSON called config.json and I would like to use the values it has to shit variables in a YML.
This is the format of the JSON
{
"config":
{
"ruta": "releases/package.xml",
"testLevel": "--testlevel NoTestRun",
"destroy": "false"
}
}
I need to get the value of "destroy" and load it into a variable in a YML.
Here I have a breakthrough and I need to use that variable in the next stage called "Validation" so that the condition is met.
stages:
- stage: LoadEnv
displayName: Load environment variables
jobs:
- job:
variables:
- name: DESTROY
value: false
steps:
- task: FileTransform#1
inputs:
folderPath: '$(System.DefaultWorkingDirectory)'
targetFiles: 'azure/config.json'
fileType: 'json'
- script: echo ${{variables.DESTROY}}
- stage: Validation
displayName: Validate environment
condition: eq( dependencies.LoadEnv.outputs['Load.variables.DESTROY'], 'false')
jobs:
- job: Validation
.......
I know things are missing but any help is welcome.

Evaluate an expression at template expansion time

I have an azure pipeline template that takes parameters, and I'd like to set one of the parameters based on an expression:
- template: templates/mytemplate.yml
parameters:
TestBackCompat: eq('${{ parameters.CIBuildId }}', 'MasterLatestBuildId')
CreateNupkg: ${{ parameters.CreateNupkg }}
As written, it doesn't work, because the expression isn't evaluated until runtime.
Is there a way to evaluate the expression at compile-time? Simple variable replacement (e.g., the usage of CreateNuPkg in the script above) works OK.
From this official document:
https://learn.microsoft.com/en-us/azure/devops/pipelines/process/templates?view=azure-devops#template-expressions
Template expressions can expand template parameters, and also
variables. You can use parameters to influence how a template is
expanded. The object works like the variables object in an expression.
How the variable object works:
https://learn.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops&tabs=yaml%2Cbatch#runtime-expression-syntax
So the usage in your situation should be like this:
give_parameters.yml
trigger:
- none
parameters:
- name: CIBuildId
type: string
default: 'MasterLatestBuildId'
extends:
template: using_parameters.yml
parameters:
TestBackCompat: ${{ eq(parameters.CIBuildId, 'MasterLatestBuildId')}}
using_parameters.yml
parameters:
- name: CIBuildId
type: string
default: 'x'
- name: TestBackCompat
type: string
default: 'x'
variables:
- name: test1
value: ${{ parameters.TestBackCompat}}
- name: test2
value: ${{ parameters.CIBuildId}}
steps:
- task: PowerShell#2
inputs:
targetType: 'inline'
script: |
Write-Host $(test1)
Write-Host $(test2)
Write-Host ${{ parameters.TestBackCompat}}
Result:

Azure Yaml DevOps variable handling

So I have a YAML file where I am trying to do something like the following:
# top of the file, declare global variables
variables:
- name: MyName
value: 'apple'
...
parameters:
- name: SwitchName
type: boolean
default: false
...
stages:
- stage: Build
displayName: 'Build'
...
jobs:
variables:
- ${{ if eq(parameters.SwitchName, true) }}:
- name: MyName
value: '$(MyName)_pie'
...
steps:
- task: PowerShell#1
inputs:
scriptType: inlineScript
inlineScript: |
Write-Output $(MyName)
...
#end of script
The goal is to control the pipeline to print apple in default run and apple_pie when the user selects that parameter in the UI to be true.
I understand that parameters are compile-time and variables are run-time. I know that to overwrite a global variable you can create a variable at a job level and change it as yo see fit. Sadly, the way how template works do not let me redeclare that variable properly and I end up with $(MyName)_pie. For some reason, Yaml fails to see that inside that template there is a runtime variable that is needed to be defined.
What are my options here to achieve desired behavior?
Anything missing in my understanding here?
You can change to use the ${{ variables.MyName }} to call the variable in root level.
For example:
variables:
- name: MyName
value: 'apple'
parameters:
- name: SwitchName
type: boolean
default: false
stages:
- stage: Build
displayName: 'Build'
jobs:
- job: A
variables:
- ${{ if eq(parameters.SwitchName, true) }}:
- name: MyName
value: '${{ variables.MyName }}_pie'
steps:
- script: "echo $(MyName)"
Result:
Since you used an if expression to reassign value, the variable will be assigned at compile time.
Refer to this doc: Runtime expression syntax
You can use the template expression format: ${{ variables.var }}

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.