Conditional Approval in pipeline jobs in azure pipelines - azure

I want to customize environment attribute to choose programmatically environment for approval (dev, preprod, prod). When I try to launch pipeline, I see this error. Is there an alternative?
Job : Environment $(environment) could not be found. The environment does not exist or has not been authorized for use.
variables:
environment: dev
jobs:
- deployment: test
displayName: test
timeoutInMinutes: 0
# creates an environment if it doesn't exist
environment: $(environment)
strategy:
runOnce:
deploy:
steps:
- checkout: self
clean: true
displayName : Checkout repository
- task: NodeTool#0
inputs:
versionSpec: '16.x'
checkLatest: true

For this use case you should use parameters. This is because variables are not available during the initial parsing stage of the pipeline.
parameters:
- name: "environment"
type: string
default: "development"
And then environment: ${{ parameters.environment }}
Or if you want to get fancy, you could do something like this:
parameters:
- name: "environments"
type: object
default:
- name: development
param1: value
param2: value
- name: test
param1: value
param2: value
# This will look through the environment parameter and create a job for each environment.
- ${{ each environment in parameters.environments }} :
jobs:
- deployment: test
#read in vars from a file in variables/development.yml
variables: variables/${{ environment.name }}.yml
displayName: test
timeoutInMinutes: 0
# creates an environment if it doesn't exist
environment: ${{ environment.name }}
strategy:
runOnce:
deploy:
steps:
- checkout: self
clean: true
displayName : Checkout repository
- task: NodeTool#0
inputs:
versionSpec: '16.x'
checkLatest: true

Related

'Expected a mapping' syntax error when using 'parameters' section in Azure DevOps pipeline templates for .NET Core app build

My current pipeline consists of 2 files:
pr-validation.yml
resources:
- repo: self
queue:
name: NonProd
variables:
- name: 'NuGetFeedId'
value: '11111b1d-1111-1ecb-1dc1-11f111111f11'
steps:
- template: pr-validation-steps.yml
parameters:
UnitTestsProjectPaths:
- '1.Tests/1.Tests.csproj'
- '2/2.Tests/2.Tests.csproj'
- '3/3.Tests/3.Tests.csproj'
and actual steps in pr-validation-steps:
parameters:
- name: UnitTestsProjectPaths
type: object
default:
- '1.Tests/1.Tests.csproj'
- '2/2.Tests/2.Tests.csproj'
- '3/3.Tests/3.Tests.csproj'
steps:
- task: DotNetCoreCLI#2
displayName: 'NuGet restore'
inputs:
command: 'restore'
vstsFeed: '$(NuGetFeedId)'
projects: '**/*.sln'
- task: DotNetCoreCLI#2
displayName: 'Build solution'
inputs:
command: 'build'
projects: '**/*.sln'
arguments: '--no-restore'
- ${{ each UnitTestsProjectPath in parameters.UnitTestsProjectPaths }}:
- task: DotNetCoreCLI#2
displayName: 'Run unit tests in ${{ UnitTestsProjectPath }} and collect coverage'
inputs:
command: 'test'
projects: '${{ UnitTestsProjectPath }}'
arguments: '--configuration $(buildConfiguration) --collect "Code coverage" --no-restore'
As you see I'm on pre-build validation for .NET Core projects and the current fragment is about running several test projects from solution, but not all of them.
Azure DevOps is saying:
'pr-validation-steps.yml (Line: 2, Col: 1): Expected a mapping'
Basically saying that something's wrong in at the beginning of the line with a 'name' entry. I tried different syntax options but nothing worked for me.
I validated the files with Tom Austin's 'Azure Pipeline YAML Validator' and it says everything's fine.
What am I doing wrong? And in case you're using any kind of effective pipeline validator - please let me know, I really need it.
First you have this
queue:
name: NonProd
which I don't recognize and Azure DevOps also.
When you remove it you should be fine.
And speaking about validator. The only one good which I know is the one on the Azure Devops:
I tested this with following:
build.yml
trigger: none
pool:
vmImage: ubuntu-latest
variables:
- name: 'NuGetFeedId'
value: '11111b1d-1111-1ecb-1dc1-11f111111f11'
steps:
- template: pr-validation-steps.yml
parameters:
UnitTestsProjectPaths:
- '1.Tests/1.Tests.csproj'
- '2/2.Tests/2.Tests.csproj'
- '3/3.Tests/3.Tests.csproj'
pr-validation-steps.yml
parameters:
- name: UnitTestsProjectPaths
type: object
default:
- '1.Tests/1.Tests.csproj'
- '2/2.Tests/2.Tests.csproj'
- '3/3.Tests/3.Tests.csproj'
steps:
- ${{ each UnitTestsProjectPath in parameters.UnitTestsProjectPaths }}:
- bash: echo 'Run unit tests in ${{ UnitTestsProjectPath }} and collect coverage'
And it works.

How to parameterize agent pool name in azure pipeline with yaml file

Hi I am using agent pool in azure pipeline, so i have multiple agents so i want to parameterize the pool name like shown below but when i do this i get an error as (Line: 11, Col: 9): A template expression is not allowed in this context
parameters:
- name: pool_name
displayName: agent pool location
type: string
default: test-ashish
trigger:
- none
pool:
name: ${{parameters.pool_name}}
demands:
- Location -equals EASTUS2
steps:
- script: |
echo "This job is only for testing"
echo $()
name: Test_Job
displayName: Test Job
This issue can be solved by using the answer in this issue: Azure Devops: Is it possible to nest yaml templates inside another yaml template?.
Copyed from link:
refer code:
parameters:
buildArtifactName: "build"
solution: ""
jobs:
- job: 'BuildSolution'
pool:
vmImage: ${{parameters.vmImage}}
continueOnError: false
variables:
artifactName: ${{ parameters.buildArtifactName}}
steps:
- task: NuGetCommand#2
displayName: 'Restore NuGet packages'
inputs:
restoreSolution: ${{ parameters.solutionDir }}/${{ parameters.solution }}
configuration: ${{parameters.buildConfiguration}}

Job condition evaluates to "null" after it is used in a previous stage

The structure of my PR pipeline looks like this:
trigger: none
resources:
- repo: self
variables:
- template: templates/variables.yaml
pool:
vmIMage: $(vmImageName)
stages:
- template: templates/changed.yaml
parameters:
comparedTo: origin/production
- template: templates/unitTests.yaml
- template: templates/build.yaml
parameters:
services:
- api
- admin
- admin-v2
- client
tag: test
tagVersion: latest
- template: templates/integrationTests.yaml
- template: templates/seleniumTests.yaml
The change.yaml basically determines what microservices have been changed so that it can avoid building, testing, and deploying services if there have been no changes to them.
parameters:
- name: comparedTo
default: ''
stages:
- stage: Changed
displayName: Checks for changes in services and configs...
jobs:
- job: Changes
displayName: Checking for changes in services and configs...
steps:
- bash: |
mapfile -t servicesChanged < <(git diff HEAD ${{ parameters.comparedTo }} --name-only | awk -F'/' 'NF!=1{print $1}' | sort -u)
echo "Total Changed: ${#servicesChanged[#]}"
if [[ ${#servicesChanged[#]} > 0 ]]; then
echo "Any services changed: True"
echo "##vso[task.setvariable variable=anyServicesChanged;isOutput=true]true"
fi
name: detectChanges
There are then job conditions in subsequent stages that determine if the job should run, or not, if anyServicesChanged = true.
What I am currently testing with, this true always.
It gets to the unitTests.yaml and runs normally (it passes):
stages:
- stage: UnitTests
displayName: Run unit tests on service...
condition: succeeded()
dependsOn: Changed
jobs:
- job: UnitTests
condition: or(eq(stageDependencies.Changed.Changes.outputs['detectChanges.anyServicesChanged'], true), eq(variables['Build.Reason'], 'Manual'))
displayName: Running unit tests...
steps:
- bash: |
echo "Running unit tests..."
Then it gets to build.yaml and it skips it:
parameters:
- name: services
type: object
default: []
- name: tag
default: ''
- name: tagVersion
default: ''
stages:
- stage: BuildAndPush
displayName: Build and Push Docker images of services...
condition: succeeded()
dependsOn: UnitTests
jobs:
- job: BuildAndPush
condition: or(eq(stageDependencies.Changed.Changes.outputs['detectChanges.anyServicesChanged'], true), eq(variables['Build.Reason'], 'Manual'))
displayName: Building and Push Docker images of services...
steps:
- ${{ each service in parameters.services }}:
- task: Docker#2
displayName: Build and Push ${{ service }} Docker image
inputs:
command: buildAndPush
repository: $(imageRepository)-${{ service }}
dockerfile: $(dockerfilePath)/${{ service }}/Dockerfile
containerRegistry: $(dockerRegistryServiceConnection)
tags: |
${{ parameters.tag }}-${{ parameters.tagVersion }}
The job condition: is exactly the same, but this time it says it is NULL:
6 queue time variables used
Expanded: or(eq(Null, True), eq('PullRequest', 'Manual'))
Result: False
I'm not sure why this is as I thought it would persist where every I need to use that variable.
Suggestions for why this is happening and how to fix it?
In build.yaml, you need to add depend on Changed stage. Otherwise, the variable set in the Changed stage cannot be obtained in the BuildAndPush stage.
stages:
- stage: BuildAndPush
displayName: Build and Push Docker images of services...
condition: succeeded()
dependsOn:
- UnitTests
- Changed

How to access pipeline variables in conditions

I have pipeline where running template as one of steps.
I need execute template with two different scopes of parameters depends on value of one of pipeline variable.
For example, if pipeline variable 'mode' has value 'dev', I need run template with one scope, if value is 'stage', another scope.
- ${{ if eq(parameters.mode, 'dev') }}:
- template: ../template.yaml
parameters:
mode: dev
namespace: dev-namespace
fqdn: dev-fqdn
- ${{ if eq(variables.mode, 'staging') }}:
- template: ../template.yaml
parameters:
mode: staging
namespace: staging-namespace
fqdn: staging-fqdn
You should use this syntax ${{ if eq(variables['mode'], 'staging') }}:
It depends at what time the variables will be read in at and used. There are more then possible answer.
If using these for the future execution of a template I'd recommend using condition and defining the parameters you have in separate yml variable files.
condition: eq('${{ variables.mode}}', 'staging)
That way the call would be just
job:
condition: eq('${{ variables.mode}}', 'staging')
variables: staging.yml
- template: ../template.yaml
parameters:
mode: ${{ variables.mode }}
namespace: ${{ variables.namepsace }}
fqdn: ${{ variables.fqdn }}
And staging.yml would look like:
variables:
mode: staging
namespace: staging-namespace
fqdn: staging-fqdn
This way it is a little less decoupled and scalable not having to hard code it every time.
1.If you're using runtime parameters, you can use this format:
trigger:
- master
parameters:
- name: mode
displayName: Choose mode
type: string
default: dev
values:
- dev
- staging
pool:
vmImage: 'windows-latest'
steps:
- ${{ if eq(parameters.mode, 'dev') }}:
- template: template.yml
parameters:
mode: dev
- ${{ if eq(parameters.mode, 'staging') }}:
- template: template.yml
parameters:
mode: staging
For more details you can check Parameters to select a template at runtime.
2.If you're using variables defined in yaml directly, check my another sample:
trigger:
- master
variables:
mode: staging
pool:
vmImage: 'windows-latest'
stages:
- stage: Dev
condition: eq(variables.mode, 'dev')
jobs:
- job:
steps:
- template: template.yml
parameters:
mode: dev
- stage: Staging
condition: eq(variables.mode, 'staging')
jobs:
- job:
steps:
- template: template.yml
parameters:
mode: staging
I would recommend runtime parameters way cause it's more convenient and much more flexible.
Update1:
Here's my simple template.yml file which is just for test:
parameters:
- name: mode
type: string
default: default
steps:
- script: echo ${{ parameters.mode }}

Azure ARM template : Setting parameter value using a variable

I'm trying to pass the environment parameter to a template and then to another template (line webAppName: 'my-webapp-${{parameters.environment}}')
I get the error : A template expression is not allowed in this context. Is it not possible to do that ?
main pipeline (just the necessary code for clarity)
- stage: deploy
displayName: 'Deploy Dev Stage'
dependsOn: build
condition: succeeded()
jobs:
- deployment: deploymentjob
displayName: deployment job
environment: dev
variables:
- template: variables/dev.yml
strategy:
runOnce:
deploy:
steps:
- template: templates/pipeline-deploy/master.yml
parameters:
environment: 'dev'
master.yml
parameters:
environment: ''
steps:
- template: webapp.yml
parameters:
webAppName: 'my-webapp-${{parameters.environment}}'
then webapp.yml
parameters:
webAppName: ''
steps:
- task: AzureRmWebAppDeployment#4
displayName: 'Deploy my app'
inputs:
azureSubscription: '(#some id)'
WebAppName: ${{parameters.webAppName}}
packageForLinux: '$(workFolder)/my-package.zip'
I don't believe you're allowed to use variable substitution in ARM template parameters. But you ARE allowed to do so in variables.
So in master.yml, define a variables section, and compose your webAppName as a variable. Then pass that variable into webapp.yml instead of using a parameter.
The problem was the indentation in one of my YAML files, which was not correct. Adding an extra tab solved it.

Resources