Replacing web.config attributes on release

Recently, my group has decided to move session over to Redis due to our agents occasionally bouncing servers and losing their session.  We opted, at least for the first pass, to move our session to Redis via the quick and simple web.config update.  It worked exactly as described.

The issue began when we tried to deploy.  We set up different Redis caches for the different environments (dev/qa, staging and prod).  We didn’t want to store the access key in source control, so we did our usual of putting the keys in our Azure Key Value and then pulling the keys in via release definition variable groups.  We’ve done this loads of times to swap out app settings, connection strings, etc.  I’ve written about web.config transforms before; the only added step is to also check “XML variable substitution” on your deployment task.  Variable substitution is well documented here.

Anyway, it became clear that I would not be able to swap out the Redis configuration for the access key that way.  Searching around on Google, I couldn’t find an easy or out of the box way to do this.  So I did it myself.

The release task takes your artifact zip, unzips it and pushes it to your Azure app.  It’s pretty simple, except when you need to modify artifacts INSIDE the zip.  Therefore, we have to do the following steps:

  1. Unzip the artifact
  2. Figure out where the web.config is inside of the unziped result
  3. Perform the replacement of the access key
  4. Re-zip the folder structure and replace the original zip

Fortunately, this can be cobbled together with a handful of tasks available on VSTS.  You will, however, have to install this guy so we can use the Tokenizer task found within.

Step 1: Unzip the archive

This is an easy step.  Add the “Extract files” task.

  • Archive file patterns – This should target your deployment zip artifact.  Should be something like $(System.DefaultWorkingDirectory)/<build pipeline name>/drop/<solution name>.zip
  • Destination folder – I just made this $(System.DefaultWorkingDirectory)/<build pipeline name>/drop/extracted

Step 2: Find out where web.config is

When the agent processes your build, it does so in a folder structure that contains a unique ID to that agent.  For instance, my one artifact, when extracted, resulted in this folder structure:


No only is that a lot of folders, but that “13” is the agent ID. That could be “1” or “3” or whatever.  So you need to find your web.config.  Enter Powershell.

Add a Powershell task to your pipeline and add this “inline” code to it:

$rootDir = (Get-Item -Path ".\").FullName
Write-Host $rootDir

$releaseWebConfigPath = Get-ChildItem -Filter web.release.config -Recurse -ErrorAction SilentlyContinue -Force | %{$_.FullName}
Write-Host $releaseWebConfigPath

$webRoot = (Get-Item -Path $releaseWebConfigPath).Directory.FullName
Write-Host $webRoot

Write-Host("##vso[task.setvariable variable=webRoot]$webRoot")

I targeted web.release.config because sometimes you’ll have web.config in subfolders of your project.  Web.release.config I know I’ll only have one of and it will be in the root of the web folder.

Once I have that value, stored in $webRoot, I then save that to a trans-task variable called webRoot via this line:

Write-Host("##vso[task.setvariable variable=webRoot]$webRoot")

Under Advanced, set the Working Directory to your extracted folder:

$(System.DefaultWorkingDirectory)/<build pipeline name>/drop/extracted

Step 3: Tokenize what you need to replace

We need to tokenize the values we want to replace throughout the web.config.  The way I did it was by using my web.config transform method I talked about earlier.  I created a web.tokenize.config that targeted my access key and replaced it with a token.  My transform file looks like this:

<?xml version="1.0"?>
<configuration xmlns:xdt="">
    <sessionState customProvider="Redis" xdt:Locator="Match(customProvider)">
        <add name="Redis" xdt:Locator="Match(name)" xdt:Transform="SetAttributes(accessKey)"

To run this transformation, add an XDT Transform task.

Note: $(webRoot) is using the trans-task variable output from the PS task in step 2.  Your variable name may differ.

  • Working folder – $(webRoot)
  • Transformations – Web.Tokenize.config => Web.config

Step 4 – Run a tokenizer

The tokenizer from the utility pack I had you install works pretty simply: it finds tokens like __token__ and tries to match it up with a value you provide.  You can specify these values via JSON configuration, but since I’m trying to use variables from my Azure Key Vault, I don’t need the JSON configuration.  Instead, I added a variable to my release definition called “redisAccessKey” that pulled from the Azure Key Vault.  Mind you, your variable should be named “redisAccessKey”, not “__redisAccessKey__”.

Add the Tokenizer task.

  1. Source filename – $(webRoot)/Web.config
  2. Destintation filename – $(webRoot)/Web.config

As you can see, we want to overwrite the web.config once the tokens have been replaced.

Step 5 – Re-zip it

Finally, we can re-zip it for deployment.  Add an “Archive files” task.

  1. Root folder or file to archive – $(System.DefaultWorkingDirectory)/<build pipeline name>/drop/extracted
  2. Prepend root folder name to archive paths – unchecked
  3. Archive type – zip
  4. Replace existing archive – checked

Your file pipeline should look something like this:


Then, obviously, your task to deploy your website.


Obviously, I’d love to get rid of this if the deployment task would include the ability to do this.  For now, this will work.  Also, this can clearly be applied to all sorts of things, not just web.config attributes.  Good luck!

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create a website or blog at

Up ↑

%d bloggers like this: