Web.config transformations - The definitive syntax guide

Back when Visual Studio 2010 was the coolest kid on the block, everyone talked about the new Web.config transformation feature. The intentions with transformations were a bit different back then, but still very relevant for ASP.NET, MVC, and Web API projects. In this post, I'll explain everything you need to know about Web.config transformations and go through the best (and worst) practices when implementing transformations in your web project.

Web.config transformations - The definitive syntax guide

So, what are Web.config transformations? Most applications need to run on multiple environments and in multiple configurations. Everyone must have at least a local web server, as well as a production environment. If you are connecting to a database, the connection string needs to be different for the two environments. You could use Web.config transformations to achieve that.

Using different environments to explain Web.config transformations isn't really the best example; most articles use this example, however, primarily because it's easy to understand and the articles are old. So much has happened in the .NET tool space since tranformations were introduced. Tools like GitHub, Azure DevOps, and Octopus Deploy heavily improved how we configure and deploy applications on multiple environments.

To explain transformations, I'll use another example: solution configurations. You probably already know the Debug and Release configurations available on all .NET projects. .NET uses these two configurations for a number of purposes, like optimizing your code and excluding source code information when running in Release mode. Solution configurations can be used for transformations, as well. Switching configuration based on configuration is a perfect use of transformations.

Web.config transformations are implemented using a markup language called XML Document Transform - XDT for short. XDT is an XML-based document format invented by Microsoft and used to describe changes to a Web.config file. Want to replace an element with a specified name? XDT can do that. Want to insert an element beneath another element? XDT is your friend. Want to replace an attribute value with...well, you get the point.

Let's look at some code. To transform a Web.config file, you specify a file named Web.*Configuration*.config alongside your existing Web.config file. You probably already have a file named Web.release.config in your project, so let's build from that:

<?xml version="1.0"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <system.web>
    <compilation xdt:Transform="RemoveAttributes(debug)" />
  </system.web>
</configuration>

All XDT documents need a namespace declaration in the root element. The example above removes the debug attribute from the compilation element located beneath the system.web element. This is done using the xdt:Transform attribute. The value can be one of many, but in this case, it is RemoveAttribute(debug). SetAttributes, Replace, and InsertBefore are also popular options.

In the example above, locating the attribute was easy by declaring the full XML structure (configuration -> system.web -> compilation). If we want to modify the existing markup with more complex structures, you can use additional XDT attributes. The most common example is replacing an appSettings value with another:

<?xml version="1.0"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <appSettings>
    <add key="isProd" value="true" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
  </appSettings>
</configuration>

The example XDT replaces the value of the isProd application setting with true. Understanding XDT can be a bit difficult at first. To explain how the transformation works, let's go through the XML, step-by-step.

  1. XDT looks for an configuration element in the XML to modify.
  2. It then looks for an appSettings element.
  3. It then looks for an add element (you probably have multiple of these).
  4. It then finds add element's which key attribute equals isProd.
  5. Finally, it updates the value of isProd with true.

I hope you have a better understanding of how the transformation works. For more examples of writing XDT transformations, the last chapter in the post is a compilation of common scenarios that I have used or am using on elmah.io.

Notice that with the built-in feature, transformations are only executed on publish. That means if you right-click a project in Visual Studio and click Publish or in other ways publish your project (like through MSBuild).

The best way to learn about transformations is to look through the common tasks in the last chapter in the post. Before we move on to the examples, there are a number of things that are good to know.

App.config files

Thus far we have only talked about Web.config files. So, App.config files are supported as well, right? Not everyone knows that Web.config and App.config files are actually two different file types, maintained by different people (at least that's what I've heard) but that share common elements. This means that App.config files are not supported out of the box.

Luckily, Sayed Ibrahim Hashimi wrote an extension to XDT transformations, called Slow Cheetah, that would transform App.config files as well. Actually, Slow Cheetah works on any XML file, which is nice for transforming other types of configuration files, like those from EntityFramework and NHibernate.

Would your users appreciate fewer errors?

➡️ Reduce errors by 90% with elmah.io error logging and uptime monitoring ⬅️

Since launching Slow Cheetah, Microsoft adopted the tool and made it available as an extension for Visual Studio. The tool also comes as a NuGet package you can use as part of your build pipeline. One thing to notice here is that Slow Cheetah runs on compile time, where Web.config transformations run on deploy time.

To start transforming XML files (not Web.config files since they are already built-in), install the SlowCheetah extension. After restarting Visual Studio, right-clicking a App.config file will now show the Add Transform option:

Add Transform

SlowCheetah needs to install a NuGet package in order to transform the file on build. A nice feature of installing the Visual Studio package first is this:

Install SlowCheetah

Nice! By clicking Yes, SlowCheetah automatically installs the required NuGet package and modifies the project file:

<ItemGroup>
  <None Include="App.config">
    <TransformOnBuild>true</TransformOnBuild>
  </None>
  <None Include="App.Debug.config">
    <DependentUpon>App.config</DependentUpon>
    <IsTransformFile>true</IsTransformFile>
  </None>
  <None Include="App.Release.config">
    <DependentUpon>App.config</DependentUpon>
    <IsTransformFile>true</IsTransformFile>
  </None>
</ItemGroup>

Notice the IsTransformFile element, which is where the magic happens. Using the installed NuGet package, SlowCheetah automatically transforms the App.config file depending on the chosen configuration.

The two new XDT files are nested beneath the App.config file:

Nested config files

IntelliSense

Visual Studio is known for providing great IntelliSense for commonly used source file types. XDT transformations are Web/App.config files with a xdt namespace within, meaning that Visual Studio will provide the same experience in transform files. This even includes IntelliSense for xdt: attributes:

IntelliSense in transform files

Sadly, IntelliSense won't help with filling out the values of the xdt attributes. For this, you will need experience, documentation, and tutorials like this one. A good tip to help debug XDT problems (believe me - you will experience those) is to install SlowCheetah as explained above. SlowCheetah ads a preview command to Visual Studio when right-clicking the transform file. Previewing an XDT file shows exactly how this file will modify the existing XML file:

Preview transform file

An alternative to previewing changes in Visual Studio is using an online tool we've built: Web.config Transformation Tester. The tool generates a diff similar to the one in SlowCheetah:

webconfig-transformation-tester

Visual Studio plugins

We already touched upon a great plugin for helping with XDT transformations. There are a couple of other helpful plugins available.

Configuration Transform - https://marketplace.visualstudio.com/items?itemName=GolanAvraham.ConfigurationTransform

Golan Avraham offers an alternative to SlowCheetah that may work better in situations where you have a configuration per environment.

File Nesting - https://marketplace.visualstudio.com/items?itemName=MadsKristensen.FileNesting

Mads Kristensen wrote this excellent plugin that lets you nest files under each other in Visual Studio. I've experienced some problems getting XDT transform files correctly nested and fixing it was a piece of cake with File Nesting.

Common tasks

This is a collection of common tasks achievable with Web.config transformations.

Add appSetting/connectionString element

<configuration xmlns:xdt="...">
  <appSettings>
    <add key="Hello" value="world" xdt:Transform="Insert" />
  </appSettings>
</configuration>

Update appSetting/connectionString value

<configuration xmlns:xdt="...">
  <appSettings>
    <add key"Hello" value="World" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
  </appSettings>
</configuration>

Insert complex element

<configuration xmlns:xdt="...">
  <system.webServer>
    <rewrite>
      <rules xdt:Transform="Insert">
        <rule name="RedirectToHTTPS" stopProcessing="true">
          <match url="(.*)" />
          <conditions>
            <add input="{HTTPS}" pattern="off" ignoreCase="true" />
          </conditions>
          <action type="Redirect" url="https://{SERVER_NAME}/{R:1}" redirectType="Permanent" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

Remove attribute

<configuration xmls:xdt="...">
  <system.web>
    <compilation xdt:Transform="RemoveAttributes(debug)" />
  </system.web>
</configuration>

Replace element

<configuration xmlns:xdt="..."> 
  <system.web xdt:Transform="Replace"> 
    ... 
  </system.web> 
</configuration>

elmah.io: Error logging and Uptime Monitoring for your web apps

This blog post is brought to you by elmah.io. elmah.io is error logging, uptime monitoring, deployment tracking, and service heartbeats for your .NET and JavaScript applications. Stop relying on your users to notify you when something is wrong or dig through hundreds of megabytes of log files spread across servers. With elmah.io, we store all of your log messages, notify you through popular channels like email, Slack, and Microsoft Teams, and help you fix errors fast.

See how we can help you monitor your website for crashes Monitor your website