Date posted: 17 Apr 2020, 4 minutes to read

Azure DevOps: Update release variables across stages

In Azure DevOps I needed to determine a variable in one deployment stage, and use it in another. I remember finding and implementing this solution before but couldn’t figure out how I did things, so this post is for me to find it easier next time 😉.

For example, in a stage I want to set a variable with the name ReleaseVariableName to a value. Searching online points you to an example on how to do this with for example the PowerShell command below. You first create a variable in the variable tab and then set/overwrite its value:

Write-Host "##vso[task.setvariable variable=ReleaseVariableName;]Value1.0"

Note that you don’t necessarily need to create variable, Azure DevOps does that for you. But it helps in figuring out what is happening later on.

Image of sandlike waves

unsplash-logoPhoto from Melissa Guzzetta

The issue

Testing the code above will prove that this works, but that the variable values are reset in a new Agent Job or another Stage. This stems from the fact that each job or stage can be run on a different Agent (and even in parallel) and that the values are not synced across.

The fix: use the REST API for Azure DevOps

The only way I found to update the variable value is to use the REST API for Azure DevOps, find the current release we’re in and then overwrite the variable value there. Then the next Stage / Job will pick up the new value and you can continue.

Do note that this updated value will not be available with this in the same stage as you’re updating it in! Handle that separately.
#region variables
$ReleaseVariableName = 'StageVar'
$releaseurl = ('{0}{1}/_apis/release/releases/{2}?api-version=5.0' -f $($env:SYSTEM_TEAMFOUNDATIONSERVERURI), $($env:SYSTEM_TEAMPROJECTID), $($env:RELEASE_RELEASEID)  )

#region Get Release Definition
Write-Host "URL: $releaseurl"
$Release = Invoke-RestMethod -Uri $releaseurl -Headers @{
    Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"

#region Output current Release Pipeline
Write-Output ('Release Pipeline variables output: {0}' -f $($Release.variables | ConvertTo-Json -Depth 10))

#region Update StageVar with new value
Write-Host "Updating release variable with name [$(ReleaseVariableName)] with new value [$(ReleaseVariableValue)]"
$release.variables.$(ReleaseVariableName).value = "$(ReleaseVariableValue)"

#region update release pipeline
Write-Output ('Updating Release Definition')
$json = @($release) | ConvertTo-Json -Depth 99
Invoke-RestMethod -Uri $releaseurl -Method Put -Body $json -ContentType "application/json" -Headers @{Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" }

#region Get updated Release Definition
Write-Output ('Get updated Release Definition')
Write-Host "URL: $releaseurl"
$Release = Invoke-RestMethod -Uri $releaseurl -Headers @{
    Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"

#region Output Updated Release Pipeline
Write-Output ('Updated Release Pipeline variables output: {0}' -f $($Release.variables | ConvertTo-Json -Depth 10))

Note: you will need to set the Job you are running this in to have access to the OAuth Access Token:

OAuth Setting

Even easier, download the Task Group definition

Making implementing this even easier, you can download my exported Task Group here and import it (after reviewing it for security issues of course!) into your own environment.

Authorization for the Service account you are using

Good to note that you need Manage Releases with the service you are running the deployment pipeline with, otherwise you will run into an error like this:

VS402904: Access denied: User Project Collection Build Service (AzDoServiceAccountName) does not have manage releases permission. Contact your  release manager.

Future Azure DevOps update

There is a new update for Azure DevOps on its way to make this even easier as noted by the Azure DevOps team here. You can see that the initial issues was created in 2017 and the solution is rolling out in 2020 😄.

Update for yaml pipelines

After reading this blog post, Sebastian Schütze knew about another way to fix this issue in a yaml pipeline: you have the option there to upload an artefact from the pipeline that can be downloaded in any subsequent stage/job.

You can read his post here:

Recreation in Classic Pipeline

I wanted to check to see if I could replicate the behavior in a classic pipeline and it all seemed good: there is a Publish Pipeline Artifact task available that is meant just for cases like this.

Screenshot of Publish pipeline artifact


You can then retrieve the file in the next stage/job and read it back in…. Or so was the plan:

Screenshot of Publish pipeline artifact

Screenshot of Publish pipeline artifact

The Upload Artifact task cannot be run in a release pipeline! 😠💩 It has been added to the documentation, but why they then show the task as being available and all, is beyond me. There have been more people who want this to work, as you can find in this GitHub issue.

There is an option to upload a file to the release pipeline, but then you cannot download it again:

Write-Host "##vso[task.uploadfile]$($file.FullName)"
Note: you can then download this file with the logs for the release pipeline.