Evaluating Azure Devops Expressions within Powershell - powershell

I want to be able to invoke the Counter expression within a template but I am unsure how to do so; my current template yml file looks like this:
parameters:
- name: major
type: string
default: '1'
- name: minor
type: string
default: '0'
steps:
- task: PowerShell#2
displayName: Set NuGet package version
inputs:
targetType: 'inline'
script: |
$isMain = ('$(Build.SourceBranch)' -eq 'refs/heads/main')
$minor = ${{ parameters.minor }}
$revision1 = $[counter($minor, 1)]
Write-Host $revision
exit 0
But I get:
The term '$[counter' is not recognized as the name of a cmdlet, function, script file, or operable
program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
I am guessing there is way I can invoke the function via Powershell.
The reason for me wanting to do this in the template as opposed to passing it in as a parameter from the consumer of the template is because seemingly parameters are not evaluated when passed into a template.

Evaluating Azure Devops Expressions within Powershell
Just as Daniel said that:
The powershell script you're trying to run is getting finalized
during compile time. This line: $revision1 = $[counter($minor, 1)]
cannot possibly work. You are trying to take the results of a
powershell script expression and use it in a YAML function.
That is reason why it is not work for you.
And personally think you can go the easier way, just define the variable in the main YAML file:
azure-pipelines.yml:
variables:
- name: minor
value: 0
- name: revision1
value: $[counter(variables['minor'], 0)]
stages:
- stage: Run
jobs:
- job:
steps:
- template: Template.yml
And we could use the the Counter in the template directly:
Template.yml:
steps:
- task: PowerShell#2
displayName: Set NuGet package version
inputs:
targetType: 'inline'
script: |
Write-Host '$(revision1)'
The test result:

Related

how to get and set array inside the ADO pipeline Powershell from variable?

I have below code in yaml file:
trigger:
branches:
include:
- maventest
pool:
vmImage: ubuntu-latest
steps:
- task: PowerShell#2
inputs:
filePath: './1.ps1'
Below is 1.ps1 file
$user1=#($(user))
Write-Host $(user)
write-host $user1[0]
As I can pass the value of variable inside while run pipeline but user is not getting from variable.
I had also used the $(env:user) but it didn't get value from variable.
From your YAML sample, the cause of the issue is that the pipeline variable is not able to directly expand in PowerShell file.
To solve this issue, you can pass the pipeline variable to ps file via argument of PowerShell task.
Here is the example:
Ps file sample:
param($user)
$user1=#($user)
Write-Host $user
write-host $user1[0]
Pipeline sample:
steps:
- task: PowerShell#2
inputs:
filePath: './1.ps1'
arguments: '-user $(user)'
Result:

Set Azure DevOps pipeline variable to array of values

I am trying to run a set of tests across a set of ADO builds.
I can retrieve the list of ADO builds using PowerShell. However, once I have the list, I need to export that to an ADO variable and then iterate across the values.
I've seen how to export values from Powershell to ADO using logging, but that appears to export the value as a string, not a list.
Is there a way to export variables so that I could iterate across them; e.g., using ${{ each foo in exportedVars }}?
First, for the usage you mentioned:
${{ each foo in exportedVars }}
This is a compile-time usage, it is expanded at the beginning, and you can't get the variables generated by the pipeline runtime through it.
Second, the pipeline can output variables through the logging command, but the variables set in this way can only be strings. This is by design, and the documentation has said it very clearly:
https://learn.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops&tabs=yaml%2Cbatch#set-variables-in-scripts
All variables set by this method are treated as strings.
It is not difficult to parse a string and put it into an array, just use the function(split()) that comes with the string type to split and restore.
Here is an example:
trigger:
- none
# 1
stages:
- stage: s1
displayName: setvars
jobs:
- job: testJob
steps:
- task: PowerShell#2
name: setvar
inputs:
targetType: 'inline'
script: |
# logic here. For example you get the vars and put it into this format:
$testvars = "testvar1,testvar2,testvar3"
Write-Host "##vso[task.setvariable variable=outputvars;isOutput=true]$testvars"
# 2
- stage: s2
displayName: getvars
dependsOn: s1
variables:
vars: $[ stageDependencies.s1.testJob.outputs['setvar.outputvars'] ]
jobs:
- job:
steps:
- task: PowerShell#2
inputs:
targetType: 'inline'
script: |
$varsArr = "$(vars)".Split(',')
foreach ($var in $varsArr)
{
Write-Host "$var`r`n"
}
Result:

Accessing variables in Azure loops using templates

I want to loop through the pipeline artifacts and pass them as variables to a task.
Followed this answer here, but no luck:
https://stackoverflow.com/a/59451690/5436341
Have this powershell script in the build stage to get the artifact names and store them in a variable:
- task: PowerShell#2
inputs:
targetType: 'inline'
script: |
# Write your PowerShell commands here.
Write-Host "Fetching value files"
cd $(Build.ArtifactStagingDirectory)\MSI
$a=dir -Filter "*.msi"
$List = $a | foreach {$_}
Write-Host $List
$d = '"{0}"' -f ($List -join '","')
Write-Host $d
Write-Host "##vso[task.setvariable variable=MSINames;isOutput=true]$d"
name: getMSINames
And passing them as parameters to a template from another stage as below:
- stage: deployPoolsStage
displayName: Deploy Pools
dependsOn:
- Build
jobs:
- job: CloudTest_AgentBased_Job
displayName: 'CloudTest AgentBased Job'
timeoutInMinutes: 120
variables:
MSIFiles: $[dependencies.Build.outputs['getMSINames.MSINames']]
steps:
- template: TestPipeline.yml
parameters:
files : $(MSIFiles)
Now, my template looks like this:
parameters:
files : []
steps:
- ${{ each filename in parameters.files }}:
- task: SomeTask
inputs:
Properties: worker:VsTestVersion=V150;worker:MSIFile=${{ filename }}
displayName: 'tests'
Now this is failing with an error saying: "Expected a sequence or mapping. Actual value '$(MSIFiles)'". It's the same error even without using the template and directly accessing the variable in the original yml file.
Please let me know of a way to loop through my pipeline artifacts and pass them to my task.
You are exporting the variable MSINames from a task in Build stage that needs to be inside a job (say BuildJob). Also, you are trying to access the exported variable from another stage but inside a job. Thus, you should use the format to read variable from another stage but within a job. What you have used is a wrong format in deployPoolsStage stage. Try correcting the format as below inside CloudTest_AgentBased_Job job,
variables:
MSIFiles: $[stageDependencies.Build.BuildJob.outputs['getMSINames.MSINames']]
NOTE: I have assumed that the getMSINames task is defined inside the BuildJob job under Build stage according to what you have provided.
See docs related to this here.

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

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.

Azure DevOps yaml: use a powershell task output parameter to generate a loop in dependent job

I have the following yaml as used in an Azure DevOps pipeline (this is not the full pipeline - it's just a portion of yaml that is in a template):
jobs:
- job: CheckExcludedWorkspaces
displayName: Check Excluded Workspaces
pool:
name: DefaultWindows
steps:
- task: PowerShell#2
name: GetWorkspaces
displayName: Check Excluded Workspaces
inputs:
filePath: "$(System.DefaultWorkingDirectory)/pipelines_v2/powershell/checkExcludedWorkspaces.ps1"
targetType: FilePath
errorActionPreference: 'stop'
arguments: -environmentFolder "$(rootFolderPrefix)\${{parameters.environmentFolder}}" -excludeFolderList "${{parameters.tagOutList}}"
pwsh: false
# - ${{ each folder in dependencies.CheckExcludedWorkspaces.outputs['GetWorkspaces.WorkspaceList'] }}:
- job: NewJob
dependsOn: CheckExcludedWorkspaces
variables:
testVar: $[ dependencies.CheckExcludedWorkspaces.outputs['GetWorkspaces.WorkspaceList'] ]
pool:
name: DefaultWindows
steps:
- powershell: |
Write-Host "Test var = $(testVar)"
displayName: Test workspaces output
this works correctly in that the second job retrieves a variable from a powershell task in the previous job and outputs that variable value. The task in the second job outputs a list of apps using variable testVar. The output contains:
app1,app2,app3,app4 etc
I would like to take this the next stage which is I would like to create a loop of jobs that repeated runs for this application list. Something like:
jobs:
- job: CheckExcludedWorkspaces
displayName: Check Excluded Workspaces
pool:
name: DefaultWindows
steps:
- task: PowerShell#2
name: GetWorkspaces
displayName: Check Excluded Workspaces
inputs:
filePath: "$(System.DefaultWorkingDirectory)/pipelines_v2/powershell/checkExcludedWorkspaces.ps1"
targetType: FilePath
errorActionPreference: 'stop'
arguments: -environmentFolder "$(rootFolderPrefix)\${{parameters.environmentFolder}}" -excludeFolderList "${{parameters.tagOutList}}"
pwsh: false
- ${{ each folder in dependencies.CheckExcludedWorkspaces.outputs['GetWorkspaces.WorkspaceList'] }}:
- job: NewJob
dependsOn: CheckExcludedWorkspaces
variables:
testVar: $[ dependencies.CheckExcludedWorkspaces.outputs['GetWorkspaces.WorkspaceList'] ]
pool:
name: DefaultWindows
steps:
- powershell: |
Write-Host "Test var = ${{folder}}"
displayName: Test workspaces output
This code gives me an error:
Unrecognized value: 'dependencies'. Located at position 1 within expression: dependencies.CheckExcludedWorkspaces.outputs['GetWorkspaces.WorkspaceList']
Is there a way i can use a powershell task output variable, to create a list of jobs in a dependent job? The problem is that i don't know at design time what the list of applications will be (the pipeline should ideally find this out when it runs). The list of applications is based on the list of folders that are created within a repository - which changes over time..
In current situation, we cannot use the 'each' key word for the variables. The 'each' keyword is used for the Obj type, but the variable is String.
For more details, you can refer the doc: Each keyword