Deploy website to Windows Azure with Web.Config transformations

Web.config transformation
is a simple and powerful inclusion in .NET 4.0 for generating
configuration files for different environments. If you publish from Visual Studio, web.config transformation is very easy and painless. Publishing from Visual Studio to a Windows Azure hosted website also presents no particular problem. However, if you want to deploy automatically to Windows Azure using source control integration, you may have a more troubled experience. At least, that was my experience.

First, I followed these steps to publish the website from TFS. It works great if you use the same Web.Config for both development, staging and production. But when you want to use Web.Config transformation in the build process, and you do a search for this scenario, you will run into countless instructions, how-to's, stack overflow questions, and potentially some answers. All or any of them may work, but I was hit by two problems when queuing a new build.

Upon various attempts to get it to work, these two error messages appeared:

  1. The process cannot access the file 'Web.Config' because it is being used by another process.
  2. Could not write Destination file: Access to the path '…Web.Config' is denied.

The first is related to a problem to a bug in MSBuild whereby Web.Config is locked while reading the file, so it cannot also write to the file for the transformation. The second problem is related to Web.Config being readonly because of it's checked in status. MSBuild does get the latest version from TFS, but keeps the readonly file attributes. 

In order to fix this, I had to add this little snippet to my Web project file. 

  <Target Name="BeforeBuild">
    <Exec Command="attrib -R Web.Config" />
    <Copy DestinationFiles="Web.Temp.Config" SourceFiles="Web.Config" />
    <TransformXml Source="Web.Temp.Config" Transform="Web.$(Environment).Config" Destination="Web.Config" />
  </Target>

Firstly, we make sure that the file is no longer read-only. Then we copy this file to a temporary file. The transformation is done from this temporary file back into the original Web.Config.

Note that I use the parameter "$(Environment)" in this snippet. The value is taken from a parameter set in the Build Definiton. Under "MSBuild Arguments" I entered: /p:Environment=Staging. And this means that the value for Enviroment is set to, you guessed it, Staging. You need to make sure that you do have a Web.Staging.config transformation file in your project of course. 

After a very long search, trying out all kinds of scenarios with different parameters, pre-build and post-build tasks, it boiled down to this simple fix.