How to pass variables from variable group between .yaml files with CopyFiles#2? - azure-devops

I have one variable group in ADO library which store different paths and some other variables.
In my main "master" pipeline I use it as below:
variables:
- group: myGroupName
- name: nameOfMyVariable(from variables group) or JustAnyName
- value: $[variables.nameOfMyVariable] or $[variables.JustAnyName]
then in job in the first Stage (for testing, there is only one stage and job for now) I'm trying to using template yaml:
jobs:
- template: my-template.yaml
parameters:
path: $(nameOfMyVariable) or $(JustAnyName)
then in my-template.yaml I have this code:
parameters:
- name: path
type: string
default: ''
jobs:
- job: BuildSomething
steps:
- task: CopyFiles#2
inputs:
Contents: |
${{ parameters.path }}
TargetFolder: '$Build.ArtifactStagingDirectory)'
....
Rest is not that important as it just can't find files to copy and when I try to print parameters.path with echo I get error :
syntax error: invalid arithmetic operator(error token is ".nameOfMyVariable").
I do not know how to fix it so I can access variables from variable group in some of my templates. Do I need to use ##vso[task.setvariables] or something else?

If you want use variable from variable group it is enough to just include this group
variables:
- group: myGroupName
And then use variable by name $(nameOfMyVariable)
In your example it seems you try unnecessary try to declare this variable again in first yaml example.
This example is additionally incorrect because you are addings dash directly before 'value' keyword and it may cause undefined behaviour.
https://learn.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops&tabs=yaml%2Cbatch#specify-variables

Just like Kontekst says, when it comes to variable groups, you don't need to declare the variable names and values in your yaml. Once you declare the variable groups in your yaml, you could use the variables from the groups directly.
And if you are using parameters against templates, for your scenario, I suppose that you could declare the parameters in your main yaml, and input the parameter value into your template. (and you don't have to use the variable groups)
My main yaml as below.
trigger:
- none
pool:
vmImage: ubuntu-latest
parameters:
- name: pathmain
displayName: Source Path
type: string
default: README.md
values:
 - azure-pipelines.yml
 - README.md
jobs:
 - job:
   steps:
   - script: echo ${{ parameters.pathmain }}
 - template: test-template.yml
   parameters:
     path: ${{parameters.pathmain}}
My test-template as below.
parameters:
- name: path
displayName: Source Path
type: string
jobs:
- job: BuildSomething
steps:
   - task: CopyFiles#2
     inputs:
       Contents: |
         ${{ parameters.path }}
       TargetFolder: '$Build.ArtifactStagingDirectory'

Related

Why do my variable group variables have an empty value?

I set up a variable group called secret-variables and gave it access to my pipeline.
In my pipeline I use a variable template and inside that variable template I define the variable group. I pass the variables from the variable template to pipeline templates using template expression syntax.
azure-pipelines.yml:
trigger:
- master
pool:
vmImage: windows-latest
variables:
- template: pipeline-variables.yml
stages:
- template: templates/myPipelineTemplate.yml
parameters:
mySecretVariable: ${{ variables.mySecretVariable }}
pipeline-variables.yml:
variables:
# secret-variables contain mySecretVariable
- group: secret-variables
- name: foo
value: bar
Yet the value of mySecretVariable in myPipelineTemplate.yml is empty. What have I missed?
It seems that variable groups do not support template expression syntax. Macro syntax needs to be used in stead, change the code in the stage to:
stages:
- template: templates/myPipelineTemplate.yml
parameters:
mySecretVariable: $(mySecretVariable)
This works because macro syntax is evaluated at runtime and template expression is evaluated at compile time.

How can I pass a pre-defined pipeline build parameter to a template in Azure DevOps pipelines?

Template:
parameters:
- name: PathPrefix
displayName: 'Path prefix'
type: string
default: ''
steps:
- task: DotNetCoreCLI#2
displayName: 'dotnet restore'
inputs:
command: restore
projects: ${{parameters.PathPrefix}}**/$(Build.DefinitionName).sln
Pipeline:
resources:
repositories:
- repository: devops
name: foo/devops
type: git
ref: master
trigger:
branches:
include:
- refs/heads/*
jobs:
- job: Job_1
displayName: Agent job 1
pool:
vmImage: windows-latest
steps:
- checkout: self
- template: azure/pipelines/pipeline.yaml#devops
parameters:
PathPrefix: $(Build.DefinitionName)
Run error during the restore step:
##[error]No files matched the search pattern.
Even setting verbosityRestore: detailed on the restore step doesn't give me any more information.
If I don't set PathPrefix, it seems to use the default empty string and find the solution file (in some cases). However, if I do set the prefix, which is needed in some repos, it can't find the file. I've tried various ways of referencing the parameter within the template (${{}}, $(), $[] and others) and different ways of specifying it within the pipeline, including hard-coding the path (though I want to use the variable instead), but nothing works.
I thought maybe variables would work instead, so I also tried specifying variables in the pipeline and using them in the template, but that results in the same error. Defining the variable in the template gave me a compilation error for the template (unexpected token 'variable' or something similar).
Look at what you're passing and mentally expand the results.
${{parameters.PathPrefix}}**/$(Build.DefinitionName).sln
If Build.DefinitionName is foo, and you pass that in as PathPrefix, then what you get is:
foo**/foo.sln
It looks like you want an extra forward slash in there, so you get foo/**/foo.sln.

Azure Yaml Pipelines - Dynamic object parameter to template

I would like to trigger a job template with an object as parameter.
Unfortunately, even based on the examples I couldn't find a way to do that.
I would appreciate if someone could guide me how to achieve this.
What I want to achieve, is to replace the ["DEPLOY", "CONFIG"] part with a dynamic variable:
- template: job-template.yaml
parameters:
jobs: ["DEPLOY", "CONFIG"]
This is not possible. YAML is very limited here and you may read more about this here
Yaml variables have always been string: string mappings.
So for instance you can define paramaters as complex type
Template file
parameters:
- name: 'instances'
type: object
default: {}
- name: 'server'
type: string
default: ''
steps:
- ${{ each instance in parameters.instances }}:
- script: echo ${{ parameters.server }}:${{ instance }}
Main file
steps:
- template: template.yaml
parameters:
instances:
- test1
- test2
server: someServer
But you are not able to do it dynamically/programmatically as every output you will create will end up as simple string.
What you can do is to pass as string and then using powershell split that string. But it all depends what you want to run further because you won't be able to simply iterate over yaml structure in that way. All what you can do is to run in in powershell loop and do something, but it can be not enough for you.
It's possible with some logic. see below
- template: job-template.yaml
parameters:
param: ["DEPLOY", "CONFIG"]
and in job-template.yaml file you can define. So every job name will be different
parameters:
param: []
jobs:
- ${{each jobName in parameters.param}}:
- job: ${{jobName}}
steps:
- task: Downl......

Using a pipeline variable for a variable group in YAML?

I would like to use a common pipeline definition for our solutions. Using variables, I would like to specify solution specific settings. This works, except for a variable group.
I would like to use the pipeline definition in my variable group definition.
For example:
group: $(Build.DefinitionName).Dev
But that does not work. Another option would be to use a pipeline variable, but neither does work:
group: $(buildDefinitonName).Dev
group: {{ variables.buildDefinitonName }}.Dev
What does work is a parameter, but I do not want to specify it for each run.
group: ${{ parameters.buildDefinition }}.Dev
One option is to use your deployment in a template and scope the variable group to a job in that template. The parameter passed into the template would be the environment.
Here is how the template could look:
jobs:
- deployment: Deploy_JobName
variables:
- group: 'ProjectName${{ parameters.stage}}'
The parameter in the template would look like:
parameters:
- name: stage
type: string
This template would be called from a joblike:
jobs:
- template: template.yml
parameters:
stage: ${{ parameters.stage }}
Thanks for your response. Found out that
group: ${{ variables['Build.Definition'] }}.Dev
also works. So you can use predefined variables, but not pipeline variables this way.

Using variables expansion to load a template variables file per environment

I'm attempting to create multiple pipelines in Azure DevOps but I would like to reuse the same pipeline YAML file with the differences per environment being loaded from a separate template variables file.
For that purpose I've created two variable files, which are located in the same folder as the pipeline definition:
# vars.dev.yml
variables:
- name: EnvironmentName
value: Development
# vars.prd.yml
variables:
- name: EnvironmentName
value: Production
And the definition of the pipeline is the following:
trigger: none
pr: none
variables:
- name: EnvironmentCode
value: dev
- name: EnvironmentFileName
value: vars.$('EnvironmentCode').yml
stages:
- stage: LoadVariablesPerEnvironment
displayName: Load Variables Per Environment
variables:
- template: $(EnvironmentFileName)
jobs:
- job: ShowcaseLoadedVariables
steps:
- pwsh: Write-Host "Variables have been loaded for the '$ENV:ENVIRONMENTNAME' environment"
displayName: Output Environment Variables
After importing the pipelines using the Azure DevOps UI, I can go to settings of each and set the Environment Code variable to whatever desired environment code:
However I'm always getting the same error when I try to run the pipeline, regardless of the code I fill in the variable value:
So the question here is: Is this kind of variable expansion not supported or is there a different way that I should use to accomplish this?
Thanks!
EDIT
I was able to expand the variables using another method. The new version of the pipeline is as such:
variables:
- name: EnvironmentCode
value: dev
- name: EnvironmentFileName
value: vars.${{ variables.EnvironmentCode }}.yml
stages:
- stage: LoadVariablesPerEnvironment
displayName: Load Variables Per Environment
variables:
- template: ${{ variables.EnvironmentFileName }}
jobs:
- job: ShowcaseLoadedVariables
steps:
- pwsh: Write-Host "Variables have been loaded for the '$ENV:ENVIRONMENTNAME' environment"
displayName: Output Environment Variables
However there is yet the issue of loading different files. I made different attempts and verified the following:
If you give a different environment code using the UI, when running
the pipeline, the value it assumes is still the one that's on the
pipeline definition;
If you remove from the pipeline definition the
default value or the variable entirely the expression
${{variables.EnvironmentCode}} will return an empty string
assuming the filename to be vars..yml which doesn't exist.
Is this kind of variable expansion not supported or is there a
different way that I should use to accomplish this?
If I am not misunderstand, at first, you want to use $() to get the variable you defined using the UI but failed. But later, ${{ }} can give you the value of the variable EnvironmentCode.
In fact, while you change to use ${{ }}, it just accessing the variable you predefined in the YAML files instead of the one you defined with UI. Just see this doc: Variable templates.
For the variable you defined with UI, it can be get and used with the format $()(Note: ${{ }} is the format of get the variables which defined in YAML file). But also, there some things you need to pay attention is for the variables you defined in UI, it can only be get/accessed after the build begin to run, because the variable which defined with UI exists in environment only after the build compiled begin. In one word, they are the agent-scope variable. That's why the value it used is still the one that's on the pipeline definition instead of on the UI.
If you remove from the pipeline definition the default value or the
variable entirely the expression ${{variables.EnvironmentCode}} will
return an empty string assuming the filename to be vars..yml which
doesn't exist.
As the document defined and I mentioned before, ${{}} is format which used to get the value of variable which defined in YAML files rather than the one which defined using UI.
In the steps of job, the variable that defined in the UI or defined in the YAML file can all be get/accessed with the format $(). Still, for the variable which defined in the YAML file can also get with ${{variables.xxxx}}. But at this time, if the variable name which defined in YAML file is same with the one defined with UI, the server can only get the one defined in YAML file.