Azure DevOps: How to pass on a variable to a template read from Library variable group - azure-devops

I have a variable group defined in pipeline > Library > variable group > called 'template-variable-group'
All that I am trying to accomplish here is to pass on the value of the variable my-temp-var (in this case as you can see its value is my-template-value) to a template from the yaml file.
I have a yaml based pipeline as follows.
variables:
- group: template-variable-group
name: $(date:yyyyMMdd)$(rev:.r)
stages:
- stage: Build
jobs:
- job: buildWebApp
displayName: Build Release pipeline for Discount Service on Master branch
steps:
- script: |
echo Here we go
displayName: 'Command Line Script'
- template: template.yaml
parameters:
variableToTemplate: ${{variables['my-temp-var']}}
And the template.yaml file is as follows.
parameters:
variableToTemplate:
steps:
- task: CmdLine#2
inputs:
script: |
echo Hello Hello Hello Hello Hello Hello
echo ${{ parameters.variableToTemplate }}
displayName: 'Run a two-line script'
I am not able to do that.
As you can see, that value is not reaching the template. What am I missing.
I saw this SO Answer but did not seem to be of help.

You can just pass it like a regular variable:
$(my-temp-var)

Passing it this way $(my-temp-var) will only serve for printing the value somewhere. If you try to use it as a parameter to something say for an ssh connection, the passed value will not work. I am still exploring why but it does not wok.

Related

Dynamic variables not available in other stages of azure pipeline

I am new to azure pipelines and am currently experimenting with the passing variables to the later jobs. Here is the current snippet whereby I am trying to extract the project version from the pom file using maven help: evaluate plugin. The pomVersion variable is populated but is not available in the same step or later steps in the second job via the projectVersionDynamic variable.
stages:
- stage: FirstStage
jobs:
- job: FirstJob
pool:
vmImage: 'ubuntu-latest'
steps:
- task: Bash#3
inputs:
targetType: 'inline'
script: |
pomVersion=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
echo $pomVersion ##Prints semantic version 2.27.0-SNAPSHOT
echo '##vso[task.setvariable variable=projectVersionDynamic;isOutput=true]$pomVersion'
echo '##vso[task.setvariable variable=projectVersionStatic;isOutput=true]2.27.0'
echo $(Task1.projectVersionDynamic) ##Error message
echo $projectVersionDynamic ##Is empty
name: Task1
displayName: Task1 in JobOne of FirstStage
- job: SecondJob
dependsOn: FirstJob
variables:
DynVar: $[ dependencies.FirstJob.outputs['Task1.projectVersionDynamic'] ]
StaVar: $[ dependencies.FirstJob.outputs['Task1.projectVersionStatic'] ]
pool:
vmImage: 'ubuntu-latest'
steps:
- task: Bash#3
inputs:
targetType: 'inline'
script: |
echo 'SecondJob'
echo $(DynVar) ##Is empty
echo $DynVar ##Is empty
echo $(StaVar) ##Prints 2.27.0
echo $StaVar ##Is empty
displayName: Task in JobTwo of FirstStage
Observation: projectVersionDynamic value does not get populated and is not available in the same task or subsequent tasks within or in later jobs / stages. However, the static variable gets populated in projectVersionStatic without any issues.
Is it possible to set dynamic values for user-defined variables in azure pipelines or is it that I am doing something wrong? I see an example here under the Stages section where it seems to be working.
Variables in Azure Pipelines can be really tricky sometimes. The documentation isn't always crystal-clear on how it's supposed to work. Looking at your example, a cpuple of observations:
echo '##vso[task.setvariable variable=projectVersionDynamic;isOutput=true]$pomVersion' - your single quotes need to be double quotes to expand the value of $pomVersion into the echo statement
(and this is where things get fuzzy): The purpose of task.setvariable is to communicate values to other tasks or jobs. From the documentation: "This doesn't update the environment variables, but it does make the new variable available to downstream steps within the same job."
$variable syntax won't work because the task.setVariable doesn't inject into the running environment - rather, it's a signal to the pipeline to capture the output and store it away for later use.
$(variable) syntax won't work because it's expanded just before the job starts, so it's too late to capture here.
If you use my suggestion in point 1) about double-quoting the task.setVariable, you should see the value available in the second job.
For anybody having a use case where variables defined in one azure pipeline stage needs to be used in another stage, this is how it can be done:
Following snipett of azure pipeline that defines a variable and make it available to be used in a job in the same stage and in a job in another stage:
stages:
- stage: stage1
jobs:
- job: job1
steps:
- task: "AzureCLI#2"
name: step1
inputs:
inlineScript: |
my_variable=a-value
echo "##vso[task.setvariable variable=var_name;isOutput=true]$my_variable"
- job: job2
variables:
stage1_var: $[ dependencies.job1.outputs['step1.var_name'] ]
steps:
- bash: |
echo "print variable value"
echo $(stage1_var)
- stage: stage2
jobs:
- job: job1
variables:
stage2_var: $[ stageDependencies.stage1.job1.outputs['step1.var_name'] ]
steps:
- bash: |
echo "print variable value"
echo $(stage2_var)

Azure devops Yaml :how to set for loop over agent pool name

I have a use case to run the same stage on different Azure pool name, looking to have a logic to iterate over the pool name
Is there something like this which can work ?
stages:
- ${{ each name in parameters.poolNames }}:
- stage: Stage1
jobs:
- job: Job1
pool:
name: ${{parameters.poolNames}}
steps:
- script: echo Hello, world!
displayName: "Run echo hello world ${{parameters.regionName}} ${{parameters.poolName}}"
I think what you are looking for is the matrix strategy. Have a look at this example, you should be able to adapt it your use case.

Combine variables in Azure Devops Build Pipeline YAML

I'm creating a docker build and push task in an Azure Devops Build pipeline YAML file, and I'd like to tag the image with the combination of two variables, the project name and the build number, so the tag would be service_22 (service is the project, 22 is the build number).
How do I join two variables together, e.g. $(variable)_$(variable2)
- task: Docker#2
inputs:
containerRegistry: 'Azure'
repository: 'jdmcontainers'
command: 'buildAndPush'
Dockerfile: 'Dockerfile'
tags: |
$(Build.BuildId)
$(imageName)
That's the current file, the tags are added as individual separate tags.
For those who come here and are actually looking for a way to combine two strings/variables in the job variable declaration, you can use the format() expression (docs link):
- job: Build
variables:
job_variable: $[format('Hey-{0}', variables['Build.Reason'])]
Hope that helps someone, at leased I searched for this for hours and found nothing. ^^
Try with below format:
steps:
- task: Docker#2
displayName: buildAndPush
inputs:
xxxx
tags: '$(System.TeamProject)_$(Build.Buildid)'
$(System.TeamProject) is one environment variable which can get the current project name.
Try this it works in Azure pipeline yaml
parameters:
- name: String1
displayName: StringName
type: string
values: 'Hello '
variables:
- name: String2
value: 'World'
- name: StringConcat
value: ${{parameters.String1}}${{variables.String2}}

Access build pipeline output variable in Environment Approval / Check

I am trying to build an automatic Environment Approval with Invoke Azure Function. I need to pass a variable that is created in a previous build stage. I have added the variable to the function body, but the variable is not evaluated:
Task that creates the output variable (in previous stage)
- task: CmdLine#2
inputs:
script: |
# Debug output (value visible in logs)
pulumi stack output resourceGroupName
echo "##vso[task.setvariable variable=resourceGroupName;isoutput=true]$(pulumi stack output resourceGroupName)"
workingDirectory: 'infra'
Body for the Azure function:
{ "ResourceGroup": "$(resourceGroupName)" }
Log:
2020-03-26T15:57:01.2590351Z POST https://azure-function-url
Request body: {
"ResourceGroup": "$(resourceGroupName)"
}
Added the variable to the function body, but the variable is not
evaluated
This is a expected action. Here what you are doing is share variable across stages, which does not supported directly until now.
Output variable just used to share values between steps instead of stages.
Work around:
If you want to use the generated variable in next stage, a workaround you can consider to use is writing the variable to a file, leveraging pipeline artifacts.
Sample steps:
Here I will pass one variable which name is resourceGroupName to next stage.
1) Create a folder which will contain the variables you want to pass
mkdir -p $(Pipeline.Workspace)/variables
2) Write the contents of the variable to file StageUsed:
echo "$resourceGroupName" > $(Pipeline.Workspace)/variables/StageUsed
3) In next stage, add one job before your InvokeAzureFunction job. Download the variables pipeline artifact that previous stage published.
4) Transfer each file into a variable:
resourceGroupName=$(cat $(Pipeline.Workspace)/variables/StageUsed)
5) Make the variable exposed in the current job, and set it reference name as Out:
echo "##vso[task.setvariable variable=resourceGroupName;isoutput=true]$resourceGroupName"
6) Now, you can access the variable in your InvokeAzureFunction job by calling dependencies.secondjob.outputs['output.resourceGroupName']
Sample Script:
trigger:
- master
pool:
vmImage: 'ubuntu-latest'
stages:
- stage: FirstStage
jobs:
- job: firstjob
pool:
vmImage: 'Ubuntu-16.04'
steps:
- bash: |
resourceGroupName="value"
mkdir -p $(Pipeline.Workspace)/variables
echo "$resourceGroupName" > $(Pipeline.Workspace)/variables/resourceGroupName
- publish: $(Pipeline.Workspace)/variables
artifact: variables
- stage: SecondStage
jobs:
- job: secondjob
pool:
vmImage: 'Ubuntu-16.04'
steps:
- download: current
artifact: variables
- bash: |
resourceGroupName=$(cat $(Pipeline.Workspace)/variables/resourceGroupName)
echo "##vso[task.setvariable variable=resourceGroupName;isoutput=true]$resourceGroupName"
name: output
- bash: |
echo "$(output.resourceGroupName)"
- job: ServerJob
dependsOn: secondjob
pool: server
variables:
resourceGroupName: $[dependencies.secondjob.outputs['output.resourceGroupName']]
steps:
- task: AzureFunction#1
inputs:
function:
method: 'POST'
body: '$(sdf)'
waitForCompletion: 'false'

Azure Devops - passing variables between job templates

Normal (non-template) jobs in Azure DevOps yaml support inter-job variable passing as follows:
jobs:
- job: A
steps:
- script: "echo ##vso[task.setvariable variable=skipsubsequent;isOutput=true]false"
name: printvar
- job: B
condition: and(succeeded(), ne(dependencies.A.outputs['printvar.skipsubsequent'], 'true'))
dependsOn: A
steps:
- script: echo hello from B
How do I do something similar in the following, given that templates don't support the dependsOn syntax? I need to get an output from the first template and pass it as 'environmentSlice' to the second template.
- stage: Deploy
displayName: Deploy stage
jobs:
- template: build-templates/get-environment-slice.yml#templates
parameters:
configFileLocation: 'config/config.json'
- template: build-templates/node-app-deploy.yml#templates
parameters:
# Build agent VM image name
vmImageName: $(Common.BuildVmImage)
environmentPrefix: 'Dev'
environmentSlice: '-$(dependencies.GetEnvironmentSlice.outputs['getEnvironmentSlice.environmentSlice'])'
The reason I want the separation between the two templates is the second one is a deployment template and I would like input from the first template in naming the environment in the second template. I.e. initial part of node-app-deploy.yml (2nd template) is:
jobs:
- deployment: Deploy
displayName: Deploy
# Because we use the environmentSlice to name the environment, we have to have it passed in rather than
# extracting it from the config file in steps below
environment: ${{ parameters.environmentPrefix }}${{ parameters.environmentSlice }}
Update:
The accepted solution does allow you to pass variables between separate templates, but won't work for my particular use case. I wanted to be able to name the 'environment' section of the 2nd template dynamically, i.e. environment: ${{ parameters.environmentPrefix }}${{ parameters.environmentSlice }}, but this can only be named statically since templates are compiled on pipeline startup.
The downside of the solution is that it introduces a hidden coupling between the templates. I would have preferred the calling pipeline to orchestrate the parameter passing between templates.
You can apply the depend on and dependency variable into templates.
See below sample:
To make sample more clear, here has 2 template files, one is azure-pipelines-1.yml, and another is azure-pipeline-1-copy.yml.
In azure-pipelines-1.yml, specify the environment value as output variable:
parameters:
  environment: ''
jobs:
- job: preDeploy
  variables:
    EnvironmentName: preDeploy-${{ parameters.environment }}
  steps:
  - checkout: none
  - pwsh: |
      echo "##vso[task.setvariable variable=EnvironmentName;isOutput=true]$($env:ENVIRONMENTNAME)"
    name: outputVars
And then, in azure-pipeline-1-copy.yml use dependency to get this output variable:
jobs:
- job: deployment
  dependsOn: preDeploy
  variables:
    EnvironmentNameCopy: $[dependencies.preDeploy.outputs['outputVars.EnvironmentName']]
  steps:
  - checkout: none
  - pwsh: |
      Write-Host "$(EnvironmentNameCopy)"
    name: outputVars
At last, in YAML pipeline, just need to pass the environment value
stages:
  - stage: deployQA
    jobs:
    - template: azure-pipelines-1.yml
      parameters:
        environment: FromTemplate1
    - template: azure-pipeline-1-copy.yml
Now, you can see the value get successfully in the second template job:
It is possible to avoid the dependency in the called template. However, as the OP says, the environment name cannot be created dynamically.
Here is an example of the "calling" template, which firstly calls another template (devops-variables.yml) that sets some environment variables that we wish to consume in a later template (devops-callee.yml):
stages:
- stage: 'Caller_Stage'
displayName: 'Caller Stage'
jobs:
- template: 'devops-variables.yml'
parameters:
InitialEnvironment: "Development"
- template: 'devops-callee.yml'
parameters:
SomeParameter: $[dependencies.Variables_Job.outputs['Variables_Job.Variables.SomeParameter']]
In the devops-variables.yml file, I have this:
"##vso[task.setvariable variable=SomeParameter;isOutput=true;]Wibble"
Then, in the "devops-callee.yml", I just consume it something like this:
parameters:
- name: SomeParameter
default: ''
jobs:
- deployment: 'Called_Job'
condition: succeeded()
displayName: 'Called Job'
environment: "Development"
pool:
vmImage: 'windows-2019'
dependsOn:
- Variables_Job
variables:
SomeParameter: ${{parameters.SomeParameter}}
strategy:
runOnce:
deploy:
steps:
- download: none
- task: AzureCLI#2
condition: succeeded()
displayName: 'An Echo Task'
inputs:
azureSubscription: "$(TheServiceConnection)"
scriptType: pscore
scriptLocation: inlineScript
inlineScript: |
echo "Before"
echo "$(SomeParameter)"
echo "After"
Output:
2021-04-10T09:22:29.6188535Z Before
2021-04-10T09:22:29.6196620Z Wibble
2021-04-10T09:22:29.6197124Z After
This way, the callee doesn't reference the caller. Unfortunately, setting the environment in the callee thus:
environment: "$(SomeParameter)"
doesn't work - you'll just get an environment with the literal characters '$(SomeParameter)'.