Setting a Custom Variable in an Azure DevOps Pipeline with PowerShell

on September 2, 2019

Here’s a quick post on something simple which stumped me for a while, in the hopes that search engines help someone else who gets confused in the same way.

Recently, I was doing a bit of work in Azure DevOps Services, preparing a demo for an upcoming webinar. I ran into a simple but frustrating problem.

Part of the demo does the following magic, using a branch policy and pull request automation trigger, combined with some of Redgate’s extensions:

  • Builds/validates database code
  • Creates a lightweight clone of the “production” database (I’m using a copy of StackOverflow, thanks Brent & the folks at Stack)
  • Creates a release artifact summarizing the changes that’ll be deployed to the clone, exports it, then deploys the changes

This combination of actions is lovely – reviewers of the pull request have validation that the code builds, they know it deploys successfully, and they can even look at deployment timings. Plus, they have a real environment to review the change.

I was working on improving something simple about my demo: giving the cloned database a clear, accurate name that ties to the Pull Request Id.

Previously, I’d been using the default $(Release.ReleaseId) variable in the database name, but I decided that I would prefer to identify this with the PR number, and for the cloned database to be replaced when re-running the automation for a PR (if code in it changes, etc).

There wasn’t a default variable that does exactly what I wanted

Looking at my options in the default variables, the closest thing to what I was going for is $(Release.{alias}.SourceBranch)". My alias for my build artifact is _StackOverflow-CI, so I referenced that as $(Release.Artifacts._StackOverflow-CI.SourceBranch).

That outputs a path like refs/pull/34/merge. For a variety of reasons, I don’t want a bunch of forward slashes running around in my database names. But no problem - looking at the documentation, I saw that I can set a custom variable easily in a PowerShell task step in my pipeline. Maybe there’s an even simpler way to do this, but that looked like a fast and easy way to replace those slashes with underscores, so I went for it.

A key piece of info: new variables are only available in downstream steps

This ended up being a bit time consuming for me, because there’s one important piece of the documentation which I didn’t notice. I skipped to the sample code and missed this:

To set a variable from a script, you use the task.setvariable logging command. This does not update the environment variables, but it does make the new variable available to downstream steps within the same job.

Azure DevOps Services - Variables doc - emphasis mine

Having missed that fact, I struggled with my code for a good while, because I was trying to set the variable and then read it for validation in the same task. That didn’t work, so I thought I wasn’t actually setting the variable properly. Whoops.

Some simple sample code

Let’s say you have a PowerShell script task in your Azure DevOps release pipeline. You set it to “inline” type, and you have the following code in there:

$path="$(Release.Artifacts._StackOverflow-CI.SourceBranch)"
Write-Host "$path"
$path= $path -replace "/", "_"
Write-Host "No problem reading $path"
Write-Host "##vso[task.setvariable variable=DUCKS]$path"
Write-Host "Trying to read $env:DUCKS"

This will produce something like this:

2019-08-14T20:56:19.8412596Z refs/pull/34/merge
2019-08-14T20:56:19.8424967Z No problem reading refs_pull_34_merge
2019-08-14T20:56:19.8431236Z ##[debug]Processed: ##vso[task.setvariable variable=DUCKS]refs_pull_34_merge
2019-08-14T20:56:19.8545546Z Trying to read 

Totally looks like our $env.DUCKS variable didn’t get set.

However, if you add an additional PowerShell script task to the same job, also set to “inline type”, which contains that same final line:

Write-Host "Trying to read $env:DUCKS"

That will do what you expect and produce:

2019-08-14T20:56:21.4502122Z Trying to read refs_pull_34_merge

I guess reading is fundamental

This all makes perfect sense to me, now that I think about it.

In my defense, it’s been a loooong time since I’ve thought about environment variables in Windows! I couldn’t tell you how many times I looked at that doc and missed this important detail – it was more than a few times.

Hopefully this helps someone else out there who misses the same thing I did.