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"
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 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.
#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) )
#endregion
#region Get Release Definition
Write-Host "URL: $releaseurl"
$Release = Invoke-RestMethod -Uri $releaseurl -Headers @{
Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"
}
#endregion
#region Output current Release Pipeline
Write-Output ('Release Pipeline variables output: {0}' -f $($Release.variables | ConvertTo-Json -Depth 10))
#endregion
#region Update StageVar with new value
Write-Host "Updating release variable with name [$(ReleaseVariableName)] with new value [$(ReleaseVariableValue)]"
$release.variables.$(ReleaseVariableName).value = "$(ReleaseVariableValue)"
#endregion
#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" }
#endregion
#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"
}
#endregion
#region Output Updated Release Pipeline
Write-Output ('Updated Release Pipeline variables output: {0}' -f $($Release.variables | ConvertTo-Json -Depth 10))
#endregion
Note: you will need to set the Job you are running this in to have access to the OAuth Access Token:
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.
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.
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 😄.
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.
Just created a new blog post: Variables Cross Stage in Azure DevOps with YAML #AzureDevOps #DevOps #AzurePipelines #AzureDevOps #Cross-Stage #Variables #YAML AzureDevOps AzureDevOps https://t.co/CI94l1K8yG
— Sebastian Schütze 🚀☁️ (@RazorSPoint) April 18, 2020
You can read his post here: www.razorspoint.com.
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.
You can then retrieve the file in the next stage/job and read it back in…. Or so was the plan:
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)"