I have a pipeline that will clean, convert a VM to an image, and delete all of the resources. I would like to use that same pipeline to delete VMs without capturing an image. I can make all of the stages conditional, but when I end up with no stages that have no conditions. I keep seeing that the accepted solution to this problem is to make the dependsOn conditional as shown below. the deleteVM parameter is a boolean.
Parameters:
- name: deleteVM
displayName: "just delete it"
type: boolean
default: false
- stages:
- stage: aStage
${{ if eq(parameters.deleteVM, 'false') }}:
dependsOn: anActualStage
${{ if eq(parameters.deleteVM, 'true') }}:
dependsOn: []
The issue is that when I do this, and try to run the pipeline with the second condition being true, then it errors when parsing the pipeline YAML: 'dependsOn' is already defined.
P.S. I've looked at the following and they imply that the way I'm doing it is the "right" way:
Azure pipeline - Stage condition dependson
Azure Pipeline - Stage with Multiple depends on with if condition
Instead of 2 if statements, use if/else. Or simply leave out the else altogether, since you're setting the depends on to an empty list.
- stages:
- stage: aStage
${{ if eq(parameters.deleteVM, 'false') }}:
dependsOn: anActualStage
${{ else }}:
dependsOn: []
- stage: aStage
${{ if eq(parameters.deleteVM, 'false') }}:
dependsOn: anActualStage
#4c74356b41´s answer about using if-else is probably your best bet.
The way yaml works is that the dependsOn in your example is only really added to the "compiled" yaml file if your if statement resolves to true. Thus, there must be something wrong with your variables, as it looks like both of your logical statements resolves to true. I cannot really see why as it looks impossible by your example, but that is the only way I can see you getting the error you are seeing.
Related
I have a DevOps variable group with a variable like that: VARIABLE=['a', 'b', 'c'].
Then in Azure pipeline, there is a parameter like that:
parameters:
- name: parameter_test
displayName: 'Test paramter'
type: string
default: a
values:
- a
- b
- c
I want to use the variable instead of the hardcoded list, since it's present in multiple pipelines. Tried this, but docs say I can't use expressions in parameters section:
parameters:
- name: parameter_test
displayName: 'Test paramter'
type: string
default: a
values:
- ${{ each group in variables.VARIABLE }}:
- ${{ group }}
Have you ever tried things like that or have any idea how to parametrize it?
Thanks for any help!
According to this document Variable groups for Azure Pipelines - Azure Pipelines | Microsoft Docs, to reference a variable group, use macro syntax or a runtime expression, therefore the parameter cannot be defined with the value of variable from a variable group.
Instead of defining the parameter with the value of the variable in a variable group, you may consider using a core YAML to transfer the parameter/variable value into a YAML Template. Kindly refer to the below sample YAML pipeline.
# Core YAML
trigger:
- none
pool:
vmImage: ubuntu-latest
variables:
- name: Variable_core
value: b
parameters:
- name: Parameter_core
default: c
values:
- a
- b
- c
steps:
- template: Parameters.yml
parameters:
parameter_test_Azure_Repos_1: ${{ variables.Variable_core }}
parameter_test_Azure_Repos_2: ${{ parameters.Parameter_core }}
# Parameters.yml from Azure Repos
parameters:
- name: parameter_test_Azure_Repos_1
displayName: 'Test Parameter 1 from Azure Repos'
type: string
default: a
- name: parameter_test_Azure_Repos_2
displayName: 'Test Parameter 2 from Azure Repos'
type: string
default: a
steps:
- script: |
echo ${{ parameters.parameter_test_Azure_Repos_1 }}
echo ${{ parameters.parameter_test_Azure_Repos_2 }}
All non yaml files is not recommended as this is not as code, very difficult to check & audit & versionning, so as to variable group, release pipeline etc.
Azure pipeline has indeed some limitations, we can reuse the variables but not the parameters.
If I was you, even multiple pipelines use the same parameter, I will still "hard code" this directly in the pipelines just like what you wrote:
parameters:
- name: parameter_test
displayName: 'Test paramter'
type: string
default: a
values:
- a
- b
- c
I am recently doing an CI/CD setup using Azure. The goal is to have the developer select the type of build to be created i.e Staging / Prod.
Thanks to How to write if else condition in Azure DevOps Pipeline, I have added following code -
parameters:
- name: selectConfiguration
displayName: Select build configuration
type: string
default: Debug
values:
- Debug
- Release
variables:
- name: config
${{ if eq(variables['parameters.selectConfiguration'], 'Debug') }}:
value: Debug
${{ else }}:
value: Release
This gives me the following result -
But no matter what I select in this radio group, it always run the else block. i.e. the if-else always fails. Any help to understand what I am doing wrong here?
Try the below, it should work. I am using the same logic to switch between different agent pools.
variables:
${{ if eq(parameters.selectConfiguration, 'Debug') }}:
config: Debug
${{ else }}:
config: Release
In YAML pipeline, you can not use the if...else expression to assign different values to a variable in different conditions.
You can only use the if expression to determine a variable which has one specified value can be available in the specified condition. See "Conditionally assign a variable".
The if...else expression can be used to:
assign different values to an input of the task in different conditions. See "Conditionally set a task input".
run different steps in a job in different conditions. See "Conditionally run a step".
I am putting following code in azure pipeline:
- ${{ if eq(variables.enableAddonVM, true) }}:
- template: ../task-templates/addonVM.yml
parameters:
serviceConnectionName: ${{ variables.serviceConnectionName }}
customerResourceGroup: $(resourceGroupName)
location: ${{ variables.location }}
Following are my observations:
The template get skipped. I checked the value by printing it in task before it. It is showing true. Still it gets skipped.
I also applied condition next to template as shown below
template: ../task-templates/addonVM.yml
condition:
But still it get failed. We were not able to use condition after template.
I also checked addonVM.yml after removing condition. addonVM.yml was working propely.
So, My final question is how can insert variables within if condition? Is there some way to get rid of this problem?
If with variables not parameters in azure pipeline problem
It depends on whether the variable enableAddonVM already existed before compilation.
Just as jessehouwing pointed that the template variables ${{ variables.var }} are processed at compile time:
Check the document Template expression syntax for some more details.
So, if the variable enableAddonVM is generated at previous task, it could not be grabbed at compile time. That is because this variable is generated at runtime.
To resolve this issue, you could use condition, like:
steps:
- template: ../task-templates/addonVM.yml
parameters:
serviceConnectionName: ${{ variables.serviceConnectionName }}
customerResourceGroup: $(resourceGroupName)
location: ${{ variables.location }}
condition: eq(variables.enableAddonVM, true)
Is it possible to run only one build pipeline task on a schedule and not the whole pipeline on a schedule? I have a task in a build pipeline to generate some report about the pipeline. I would want the task to run once every month.
Yes.. it is possible in several ways:
you can set previously to the task the condition:
${{ if eq(variables['isBuild'], true) }}:
You can configure conditions to run the tasks that you want, depending of whatever, for example in this case using the variable isBuild, defined previously:
task: PublishBuildArtifacts#1
displayName: 'Publish artifact: drop'
inputs:
PathtoPublish: 'whatever'
ArtifactName: 'drop'
publishLocation: 'Container'
condition: eq(variables['isBuild'], true)
You can configure conditions to run the stages that you want, so previously you can group your tasks in stages, depending of whatever, for example using in this case in stage the variable isBuild, defined previously:
stage: Build
displayName: 'Build'
condition: eq(variables['isBuild'], true)
In every example in case of IsBuild be different to true will not be run.
You can find more info in https://github.com/MicrosoftDocs/azure-devops-docs/blob/main/docs/pipelines/process/conditions.md
Also If you want to schedule your task to be executed only 1st day of every month at 07:00:
trigger:
master #This is the trigger for other stages. It is not needed for the scheduled stage.
schedules:
cron: '0 7 1 * *'
displayName: 'Deploy every 1st day of every month at 07:00Z'
branches:
include:
main
always: true
Then to ensure that task runs as part of schedule, use the condition:
- stage: 'Test'
displayName: 'Deploy to the test environment'
dependsOn: Dev
condition: eq(variables['Build.Reason'], 'Schedule')
For more detail you can to to:
https://learn.microsoft.com/en-us/azure/devops/pipelines/process/scheduled-triggers?view=azure-devops&tabs=yaml#scheduled-triggers
Put the task in a second pipeline, and run that on a monthly schedule.
If you want to avoid yaml code duplication, you could define a template containing that one task and have that template called from both pipelines.
I have a pipeline (in YAML) which upgrades an infrastructure(I have 2 stages each containing a series of jobs)
I now want to upgrade multiple infrastructures simultaneously i.e. pass a list of identifiers which represents deployments to the pipeline and then let it upgrade each.
What is the best practice here for organising the pipeline? It feels like I need to generate a set of parallel jobs using a loop?
As I understand it any job failure will result in a total failure which could leave us in a very confused state.
If you purchased parallel jobs for your organization. You can use Template to generate multiple jobs according the identities parameter using expression ${{each id in parameters.identities}}.
So you can move the job which upgrades the infrastructures into a template and define your yaml pipeline as below. See below example:
Template file: upgrade-infrastructure.yml
parameters:
id: 1
jobs:
- job: upgradeinfra${{parameters.id}}
steps:
- powershell: echo "upgrade-infra-${{parameters.id}}"
azure-pipelines.yml:
#define the identities as a object to hold a array object of ids
parameters:
- name: identities
type: object
default:
ids:
- 1
- 2
trigger: none
stages:
- stage: Upgrage
pool:
vmImage: windows-latest
jobs:
- job: A
steps:
- powershell: echo "job A"
#loop through the ids array object and the each id to the template paramter to generate multiple jobs for each id.
#indentation is very important, bad indentation may cause pipeline compile error.
- ${{ each id in parameters.identities.ids }}:
- template: upgrade-infrastructure.yml
parameters:
id: ${{id}}
After you set up your yaml pipeline as above, you can enter the identities in the parameter when executing the pipeline:
Then you will see the multiple jobs are generated and run in parallel:
To make your deployments run in parallel, all you need to do is to set the dependencies. (The dependency on the previous step is automatically set). Here is an example of a stage that only depends on the build before all stage will run in parallel:
stage:
-stages : DeployTo${{ parameters.environment }}
dependsOn: ["Build"] //The stage that build the code is called "Build"
The Result looks like this:
Without the dependsOn property your pipeline stages will run sequentially and looks like this:
stages:
-stage : DeployTo${{ parameters.environment }}