Transfer build tags in Azure Pipelines

Did you ever need to transfer some kind of information from the build into the release pipeline but you didn’t want to store the information in the artifact folder? Did you ever think what can I do and how can I use the build tags in my release pipeline? If your answer is yes then you’re at the right place because this topic is all about how to set the tags in the build pipeline and how to read those tags and put them into release variables.

Let’s imagine a situation where we have a Windows Server environment (on-prem or in cloud VM) with several sites hosted in IIS. A common feature for all of IIS Websites is a Website name and a Physical path. Also under the Physical path, we will have a version subfolder (ex. v1.2.0.0) for each deployed version and each IIS Website will have a separate database. The purpose of such an environment is internal testing and QA and we are dealing with the legacy .net (full-framework or core) application that has either environment .json application settings transformation (in case of the .net core) or web.config transformation (in case of the full-framework) that target each IIS site (database connection string, environment settings, etc…). The requirement is to create one build and one release in Azure Pipeline that will target the site hosted in IIS on the environment without the need to change the codebase.

First, let’s set some ground rules. From the previous description, we can conclude that we have four variables: Website name, Physical path, subfolder, and database. All website names have a naming convention Test1, Test2, Test{N}. All physical path follows the same convention as website names. The subfolder inside the physical path convention is version number like in example v1.2.0.0 and the database name convention is test1db, test2db, test{N}db. In the following example, we will deal with the .net core application and the assumption is that we have a build and release process for one of the environments. Great, now that we have set up the playground let’s begin.

First, we will add some steps to the existing build process. Since we will have one build for all of the environments we need to add some build pipeline variables settable at queue time. We will need a Database variable for setting the database in the connection string and to pass the database name to the release pipeline for the database script deployment and Environment name for choosing the right application settings .json file in the build process and to deploy the package to the appropriate website name and physical path under the IIS. 

Next, we will add a Run Inline Powershell task that will change the database connection string in the application settings. The purpose of this step is to have an arbitrary database paired with the environment. For example, the Test1 environment could use the test5db database.

You will need to pass pipeline variables into the script as Arguments:

-defaultDir $(Build.Repository.LocalPath) -environment $(Environment) -database $(Database)

Build.Repository.LocalPath is predefined variables in Azure Pipelines that target the local path on the agent where your source code files are downloaded (more about the predefined variables). This parameter is used to target the appsettings.json file path that will be changed in the task. Here is the complete PowerShell script:

Based on the $environment parameter that is linked with the Environment build variable we will determine the old database name because each for each environment the default database is associated with the connection string (ex. Test1 is associated with the test1db in application settings). Therefore in the script, we will create the default database name:

$oldDatabase = $($environment).ToLower() + 'db'

Based on the $environment parameter and Build.Repository.LocalPath we will read the appropriate JSON application settings file (ex. appsettings.Test1.json):

$inputLocationSettings = $defaultDir + "\WebApp\appsettings.$($environment).json"
$jsonResult= Get-Content -Raw -Path $inputLocationSettings | ConvertFrom-Json

Next thing we will replace the old database name with the new one based on the $database parameter:

$connectionString = $result -replace "$($oldDatabase)", "$($database)"

And the last step we need to update the appropriate JSON application settings file:

$jsonResult..AppSettings.ConnectionString.DefaultConnection = "$($connectionString)"
$jsonResult | ConvertTo-Json -depth 32 | set-content $inputLocationSettings

Keep in mind that the structure. AppSettings.ConnectionString.DefaultConnection matches the JSON structure of your application settings file. The first step is done and the connection string is configured. Also in this way, you can change any parameter in your settings file based on the environment just add additional build variables that will be set in queue time.

The last step (after the build step) is to set-up the build tags. For this purpose, we will add another Run Inline Powershell task into our build pipeline. The task Assign tags to build will add for example the following build tags: [version]v1.2.0.0, [env]Test1, [db]test5db

Remark: You need to add a prefix [version] so that you can easily read the tags in the release pipeline because the order of the tags can be random so you don’t have the guarantee that the order of writing the tags in PowerShell will match the order of tags in the finished build.

First, we will read the version that is set-up in the application assembly:

$defaultDir+="/WebApp/bin/Release/netcoreapp3.1/WebApp.dll"
$appVersion = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($defaultDir).FileVersion
$version = "v$appVersion"

And then we will write the tags in the build:

Write-Host "##vso[build.addbuildtag][version]$version"
Write-Host "##vso[build.addbuildtag][env]$environment"
Write-Host "##vso[build.addbuildtag][db]$database"

Write-Host ##vso[area.action property1=value;property2=value;…]message are Logging commands in Azure Pipelines and they represent the way how tasks and scripts communicate with the agent. (More about the Logging command). Here is the complete script:

We have set the build pipeline and once we run the build we will get the tags once upon the build is finished.

Now let’s jump to the release process. From the release perspective, everything should be straight forward. We need to read build tags, set them into release variables, and use them in deployment step tasks. The first thing that we need to set on the Deployment group job is checking Allow scripts to access the OAuth token option. This will allow us to use the special environment variable $env:SYSTEM_ACCESSTOKEN in our PowerShell script.

Obviously, the next step is to add Run Inline Powershell task into our release pipeline:

As you can see we will use the Azure DevOps REST API to invoke the build definition route and collect the tags from the build.  $(Build.BuildId) will take the build associated with the current release. The next pair of commands will iterate through the $tags variable and based on the prefix of the build tag will match the value for each transfer tag value from the build and set the values into appropriate release variable by using the Logging commands like in the build pipeline: 

Write-Host "##vso[task.setvariable variable=Environment]$envValueTrim"

After you set the release variables you can use them in the IIS web app deploy task as parameters to configure the Website Name, the path to the package or folder of the artifact, database name in the database deployment script. In this way, you can transfer any variable or parameter as a build tag into your release pipeline.