Is it possible to use variable group in IF statements? - Azure DevOps - azure-devops

I have declared a variable group "myVariableGroup" and inside this I have a variable name "myVariable" with default value = true.
Pipeline looks like this
I declared the group variable name in variables
first step, I debug the result of myVariable, working fine, I get the value true
second step, I pass the variable to a template parameter
variables:
- group: myVariableGroup
steps:
- script: "echo myVariableFROM group vars = $(myVariable)"
displayName: debug groupvars
- template: "./.azure-devops/some-template.yml"
parameters:
myVariableParam: $(myVariable)
Template looks like this
parameter declared with default value false
debug the parameter value, result being true (it works)
I'm using an if statement to determine if the bash script should run or not, but this is not working
parameters:
- name: myVariableParam
type: boolean
default: false
- script: "echo parameters.myVariableParam = ${{ parameters.myVariableParam}}"
displayName: debug parameters.myVariableParam
- ${{ if eq(parameters['myVariableParam'], true) }}:
- script: "echo parameters.myVariableParam= ${{ parameters.myVariableParam}}"
Questions:
Is it possible to use group variables inside if statements or the IF is interpreted before running the pipeline and value is not defined?
The only way to achieve this is via conditions? I have a corner case where I don't really want to use those
Maybe I miss something pretty obvious and can anybody help me with this?

Based on the #Alex's comments, updated the template to use condition.
I know this seems a bit hacky but if doesn't seem to be evaluated properly when using with boolean. See this answer for details
parameters:
myVariableParam: 'false'
steps:
- script: |
echo "##vso[task.setvariable variable=myVariableNew;]$myVariableEnv"
displayName: setting myVariableNew to parameter.myVariableParam
env:
myVariableEnv: ${{parameters['myVariableParam']}}
- script: |
echo "myVariableNew: $(myVariableNew)"
displayName: check myVariableNew
- script: |
echo "I should run when myVariableNew is $(myVariableNew)"
condition: eq(variables['myVariableNew'], 'true')
myVariable set to true
myVariable set to false

Related

Why am i getting an error when declaring variables YAML

I have the code as below which works fine
variables:
- group: docker-settings
I need to add a variable to use in a condition so i insert the variable as below but then I get an error? If I remove -group :docker-settings it works, if I remove isMaster line instead it works but it doesnt like them both there? What am I doing wrong?
variables:
- group: docker-settings
isMaster: $[eq(variables['Build.SourceBranch'], 'refs/heads
I used these docs
https://learn.microsoft.com/en-us/azure/devops/pipelines/process/conditions?view=azure-devops&tabs=yaml
I used the name / value notation and fixed the value based on the MS example and set master instead of main. I guess this is what you want to have.
variables:
- group: docker-settings
- name: 'isMaster'
value: $[eq(variables['Build.SourceBranch'], 'refs/heads/master')]
Microsoft example:
variables:
staticVar: 'my value' # static variable
compileVar: ${{ variables.staticVar }} # compile time expression
isMain: $[eq(variables['Build.SourceBranch'], 'refs/heads/main')] # runtime expression
steps:
- script: |
echo ${{variables.staticVar}} # outputs my value
echo $(compileVar) # outputs my value
echo $(isMain) # outputs True
Don't give up with yaml and Azure DevOps ;-)

Use query-time variables in pipeline YAML

I have set up a pipeline with variables users can enter using the UI like this:
UI for userinput of variable called 'forceRelease'
I now want to use this variable in the pipeline yaml inside an if-statement like this:
jobs:
- job: Demo
steps:
- ${{ if eq(variables['forceRelease'], 'true') }}:
...some more stuff...
This does'nt work. I've tried different approaches but could not find the right syntax.
If I use the variable inside a condition, it works fine. Like this:
jobs:
- job: MAVEN_Build
- task: Bash#3
condition: eq(variables['forceRelease'], 'true')
I also tried to map the variable inside the variables block to a new pipeline variable like this:
variables:
isReleaseBranch: ${{ startsWith(variables['build.sourcebranch'],'refs/heads/pipelines-playground') }}
isForceRelease: $(forceRelease)
The first variable using 'build.sourcebranch' works fine. My approach using forceRelease doesnt work :(
Any ideas would be appreciated!
Cheers,
Dirk
AFAIK this is working as intended. User set variables are not expanded during parsing of the template.
You can read more on pipeline processing here:
https://learn.microsoft.com/en-us/azure/devops/pipelines/process/runs?view=azure-devops
You should instead use parameters.
parameters:
- name: "forceRelease"
type: boolean
default: "false"
- name: "someOtherParameter"
type: string
default: "someValue"
stages:
- ${{ if eq(parameters['forceRelease'], true)}}:
- stage: build
jobs:
- job: bash_job
steps:
- task: Bash#3
inputs:
targetType: 'inline'
script: |
# Write your commands here
And then when you run the pipeline you have the option to enable the parameter forceRelease or add someOtherParameter string.

Azure Devops yml pipeline if else condition with variables

I am trying to use if else conditions in Azure Devops yml pipeline with variable groups. I am trying to implement it as per latest Azure Devops yaml pipeline build.
Following is the sample code for the if else condition in my scenario. test is a variable inside my-global variable group.
variables:
- group: my-global
- name: fileName
${{ if eq(variables['test'], 'true') }}:
value: 'product.js'
${{ elseif eq(variables['test'], false) }}:
value: 'productCost.js'
jobs:
- job:
steps:
- bash:
echo test variable value $(fileName)
When the above code is executed, in echo statement we don't see any value for filename, i.e. it empty, meaning none of the above if else condition was executed, however when I test the if else condition with the following condition.
- name: fileName
${{ if eq('true', 'true') }}:
value: 'product.js'
Filename did echo the correct value, i.e. product.js. So my conclusion is that I am not able to refer the variables from the variable group correctly. So any suggestion will be helpful and appreciated.
Thanks!
Unfortunately there is no ternary operator in Azure DevOps Pipelines. And it seems unlikely considering the state of https://github.com/microsoft/azure-pipelines-yaml/issues/256 and https://github.com/microsoft/azure-pipelines-yaml/issues/278. So for the time being the only choices are :
conditional insertion : it works with parameters, and should work with variables according to the documentation (but it is difficult to use properly),
or the hacks you can find in this Stack Overflow question.
Another work-around has been posted by Simon Alling on GitHub (https://github.com/microsoft/azure-pipelines-yaml/issues/256#issuecomment-1077684972) :
format(
replace(replace(condition, True, '{0}'), False, '{1}'),
valueIfTrue,
valueIfFalse
)
It is similar to the solution provided by Tejas Nagchandi, but I find it a little bit better because the syntax looks closer to what it would be if there was a ternary operator.
I was able to achieve the goal using some dirty work-around, but I do agree that using parameters would be much better way unless ternary operators are available for Azure DevOps YAML pipeline.
The issue is that ${{ if condition }}: is compile time expression, thus the variables under variable group are not available.
I was able to use runtime expressions $[<expression>]
Reference: https://learn.microsoft.com/en-us/azure/devops/pipelines/process/expressions?view=azure-devops
My pipeline:
trigger:
- none
variables:
- group: Temp-group-for-testing
- name: fileName
value: $[replace(replace('True',eq(variables['test'], 'True'), 'value1'),'True','value2')]
stages:
- stage: test
jobs:
- job: testvar
continueOnError: false
steps:
- bash: echo $(fileName)
displayName: "echo variable"
Results are available on github
After detailed investigation I realized that if else doesnt work with variables in Az Devop yaml pipelines, it only works with parameters. However the solution posted by #Tejas Nagchandi is a workaround and might be able to accomplish the same logic of if else setting variable value with replace commands. Hats off to TN.

YAML pipeline definition which retrieves variables values from pipeline variables

I have a YAML file which forms an Azure DevOps pipeline. The pipeline itself defines four variables which are needed in the variables section of the YAML...
variables:
environmentIdentifier: "$(environmentIdentifier)"
keyVaultSourceName: "$(keyVaultSourceName)"
location: "$(location)"
locationIdentifier: "$(locationIdentifier)"
The variables are definitely set for each run of the pipeline, but when it runs I encounter errors further down in my script which indicate that these variables were not populated correctly...
ERROR: (InvalidResourceGroup) The provided resource group name 'rg-main-$(locationIdentifier)' has these invalid characters: '$:'. The name can only be a letter, digit, '-', '.', '(', ')' or '_'.
I've also tried...
$env:location
${{variables['location']}}
...but incurred the same error.
How should I correctly declare vars in the variables section of the pipeline definition, where their values are retrieved from the pipeline's variables?
You need to define as:
variables:
- name: location
value: 'Australia Southeast'
If you want them at a later stage as a template expression use:
${{ variables.location }}
and if you want to use them inside a script:
steps:
- bash: echo $(location)
- powershell: echo $(location)
- script: echo $(location)
Check this Link and the below extracted sample for more information.
variables:
- name: one
value: initialValue
steps:
- script: |
echo ${{ variables.one }} # outputs initialValue
echo $(one)
displayName: First variable pass
- bash: echo '##vso[task.setvariable variable=one]secondValue'
displayName: Set new variable value
- script: |
echo ${{ variables.one }} # outputs initialValue
echo $(one) # outputs secondValue
displayName: Second variable pass

How can I invoke a YAML pipeline that has both variables and runtime parameters?

I have a scenario where I need to have both:
runtime parameters, so that the pipeline can be triggered manually from the UI, where users triggering it can choose from a predefined set of options (defined in YAML)
variables, so that the pipeline can be invoked via REST APIs
Regarding runtime parameters, I was able to create the following sample pipeline:
parameters:
- name: image
displayName: Pool Image
type: string
default: ubuntu-latest
values:
- windows-latest
- ubuntu-latest
trigger: none
stages:
- stage: A
jobs:
- job: A
steps:
- pwsh: |
echo "This should be triggering against image: $MY_IMAGE_NAME"
env:
MY_IMAGE_NAME: ${{ parameters.image }}
When I run it, I can see the dropdown list where I can choose the image name and it is reflected in the output message of the PowerShell script.
Regarding variables, I have defined one called "image" here (notice the value is empty):
The idea now is to invoke the pipeline from REST APIs and have the image name replaced by the value coming from the variable:
{
"definition": {
"id": 1
},
"sourceBranch": "master",
"parameters": "{\"image\": \"windows-latest\" }"
}
In order to make the step print the value I'm passing here, I need to correct the environment variable in some way. I thought it would be sufficient to write something like:
env:
MY_IMAGE_NAME: ${{ coalesce(variables.image, parameters.image) }}
That's because I want to give the priority to the variables, then to parameters, so that in case none is specified, I always have a default value the pipeline can use.
However, this approach doesn't work, probably because we're dealing with different expansion times for variables, but I don't really know what I should be writing instead (if there is a viable option, of course).
What I also tried is:
env:
MY_IMAGE_NAME: ${{ coalesce($(image), parameters.image) }}
MY_IMAGE_NAME: ${{ coalesce('$(image)', parameters.image) }}
MY_IMAGE_NAME: $[ coalesce(variables.image, parameters.image) ]
MY_IMAGE_NAME: $[ coalesce($(image), parameters.image) ]
None of those are working, so I suspect this may not be feasible at all.
There is a workaround that I'm currently thinking of, which is to create two different pipelines so that those can be invoked independently, but while this is quite easy for me to accomplish, given I'm using a lot of templates, I don't find it the right way to proceed, so I'm open to any suggestion.
I tested and found you might need to define a variable and assign the parameter's value to it (eg. Mimage: ${{parameters.image}}). And define another variable(eg. Vimage) and assign $[coalesce(variables.image, variables.Vimage)] to it. Then refer to $(Vimage) in the env field of powershell task. Please check out below yaml.
parameters:
- name: image
displayName: Pool Image
type: string
default: ubuntu-latest
values:
- windows-latest
- ubuntu-latest
trigger: none
stages:
- stage: A
jobs:
- job: A
variables:
Mimage: ${{parameters.image}}
Vimage: $[coalesce(variables.image, variables.Mimage)]
steps:
- pwsh: |
echo "This should be triggering against image: $env:MY_IMAGE_NAME"
env:
MY_IMAGE_NAME: $(Vimage)
Env field of powershell task is usually for mapping secret variables. You can directly refer to $(Vimage) in the powershell script: echo "This should be triggering against image: $(Vimage).
Note: To queue a build via REST API with provided parameters, you need to check Let users override this value when running this pipeline to make the varilabe to be settable at queue time.
Update:
You can try passing the variables to the parameters of the template to make the parameters for template dynamic. Please check below simple yaml.
jobs:
- template: template.yaml
parameters:
MTimage: ${{parameters.image}}
VTimage: $(Vimage)
template.yaml:
parameters:
MTimage:
VTimage:
jobs:
- job: buildjob
steps:
- powershell: |
echo "${{parameters.VTimage}}"
echo "${{parameters.MTimage}}"