AppHarbor supports applying Web.config transformations when applications are deployed on the platform. We also built a popular tool that you can use to test your config transformations live.
In addition to applying any transforms you push with your code, we use the Web.config transformation API to perform various other types of configuration transformations that have to happen for the platform to work. For example, for add-ons that require setup more sophisticated setup than simply injecting settings into the
The built-in vocabulary of transforms is somewhat limited, especially if you have to write transforms that must work correctly with all the varied Web.config files encountered by AppHarbor and not just with your own well-known config file. Fortunately, the Web.config transform engine can be extended to support additional transformations.
The API that external transforms have to implement is found in the
Microsoft.Web.Publishing.Tasks namespace. It's unpleasant and not documented by Microsoft but information can be found in this blog post and in this Stack Overflow answer.
A Transform that we needed was one that would insert an element if it's missing but leave the element alone if it's already present. Imagine, for example, that AppHarbor needs to be able to add connection strings to the
connectionStrings element. If we encounter a Web.config file without a
connectionStrings element, we need to first insert a new element and then add the connection string in question to that. If the
connectionStrings element is already present, we want to leave any connection strings it contains alone and just add the new one.
To accomplish this, we extended the transform engine with a
Merge transform that looks like this:
public class Merge : Transform
public Merge() : base(TransformFlags.UseParentAsTargetNode)
protected override void Apply()
public void Apply(XmlElement targetElement, XmlElement transformElement)
var targetChildElement = targetElement.ChildNodes.OfType<XmlElement>()
.FirstOrDefault(x => x.LocalName == transformElement.LocalName);
if (targetChildElement == null)
foreach (var transformChildElement in transformElement.ChildNodes
To use a custom transform, you have to import the relevant namespace in your transformation XML:
You can then use it to add the
connectionStrings element, if it's missing, like this:
<connectionStrings xdt:Transform="Merge" />
<add name="bar" connectionString="value" xdt:Transform="Insert"/>
We have added our custom transforms to the Web.config transformation tester which is open source (and we accept contributions). Check out the Merge and MergeBefore (which takes an XPath expression like
InsertBefore) transforms and the unit tests to get an idea of how custom transforms work. And give them a try live on the transformation tester running on AppHarbor.