Pre-Transforming Web.Configs For Deployment

When your approach to automated deployment is to build a deployment package, then later deploy that to successive test/staging/live environments, you need to manage configurations. Unfortunately, Microsoft’s web.config approach is designed to work only as part of a configure-build-deploy process. For configuring at deployment time, you need a better approach (parameters files). Or a hack… There are better ways to manage configurations, but you might not have the ability to move away from web.configs.

A quick note: I originally included this section as part of a larger post on MsDeploy, but really this is a separate subject.

Transforming Web.Configs at Build Time

I’m going to manually add a target to my MsBuild file to transform my web.configs. I don’t know at build time where my deployment will end up, so I just include all of the possible configs.

We need a solution with an MsBuild file, and the necessary build configurations and web.config transform files setup. When building the deployment package, we also transform all the possible configs (and include in the deployment artifact). When deploying, we MsDeploy the entire site (from zip package), then do an additional MsDeploy of a single web.config file.

Now add a “_TransformWebConfigs” target and a reference to the “TransformXml” task that I’m going to borrow.

Right at the top of your build file, inside the Project element and before the PropertyGroup, add this:

<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll"/>

For now I’ll just go process each configuration to create all possible web.configs in our package, transformed and ready to deploy. I’ve named the files “web.configurationName.config”, you could store them in “ConfigurationName\web.config” instead.

<Target Name="_TransformWebConfigs">
<TransformXml Source="$(WebApp)\Web.config" Transform="$(WebApp)\Web.Debug.config" Destination="$(PackageAssemblyDir)\Web.Debug.config" StackTrace="true" />
<TransformXml Source="$(WebApp)\Web.config" Transform="$(WebApp)\Web.Test.config" Destination="$(PackageAssemblyDir)\Web.Test.config" StackTrace="true" />
<TransformXml Source="$(WebApp)\Web.config" Transform="$(WebApp)\Web.Release.config" Destination="$(PackageAssemblyDir)\Web.Release.config" StackTrace="true" />

And to call this, extend your “BuildPackage”, between building the package and copying to the deployment directory, add this:

<CallTarget Targets="_TransformWebConfigs" />

Now we add a “_DeployWebConfig” target that applies the correct config based on the $(Configuration) property (call MsBuild script with “/t:DeployPackage /p:Configuration=Test”). This is almost like the previous MsDeploy command, except:

    1. For the source, it really doesn’t like relative paths, so I grab the current working directory
    2. I’m explicitly setting the destination, because we’re renaming the file as we deploy
    3. You don’t set the “IIS Web Application Name”, MsDeploy complains if you do
<Target Name="_DeployWebConfig">
<Exec Command='$(MsDeployExe) -source:contentPath="$(WorkingDir)$(PackageDeploymentDir)\Web.$(Configuration).config" -dest:contentPath="$(AzureSite)\web.config",ComputerName="https://$(AzureUrl)/msdeploy.axd?site=$(AzureSite)",UserName="$(AzureUsername)",Password="$(AzurePassword)",IncludeAcls="False",AuthType="Basic" -verb:sync -disableLink:AppPoolExtension -disableLink:ContentExtension -disableLink:CertificateExtension -retryAttempts=2 -verbose -userAgent="VS12.0:PublishDialog:WTE12.3.51016.0"'/>

And you just insert the call to deploy the config at the end of your “DeployPackage” target:

<CallTarget Targets="_DeployWebConfig"/>

Note that your “web.X.config” gets renamed to just “web.config” on the target server during the deployment.