## Monday, August 6, 2012

### Multi-Site Continuous Integration with TeamCity and MSDeploy: Parameters.xml

 (JD Hancock)
I love continuous integration. CI servers such as TeamCity let developers see their changes against an example deployment immediately, as well as have a example site for demonstrations. However, what's the best way to CI a project with multiple client builds? For example, a framework project that will be used by multiple clients, each with their own server, database, and asset images?

For that use case, CI makes even more sense. Visual Studio makes it easy to develop on a single build configuration at a time, but would require a host of deployments to test changes side by side. Luckily, TeamCity is great at doing a host of deployments.

The goal for this particular project was to get TeamCity to deploy a solution with four web applications to multiple sites, each of which had it's own set of app settings and specific web.config setup. To do this, we needed to have a way to change these settings, preferably after build. Luckily for us, Microsoft has this functionality built in, though a special file called parameters.xml.

Parameters.xml lets us replace any node in an XML file on deployment. This is similar to web.config transforms, but is customizable for every deployment, can be used for any XML file, and does not require a rebuild. To specify a parameter, follow the format below:

<?xml version="1.0" encoding="utf-8" ?>
<parameters>
<parameter name="ConnectionString" defaultValue="DefaultSite\ConnectionStrings.config">
<parameterEntry kind="XmlFile" scope="\\web.config\$" match="/configuration/connectionStrings/@configSource" />
</parameter>
...
</parameters>

Each parameter must have a name and default value. Inside the parameter node, the XML file is specified (scope), along with an XPath location to modify (match). In this example, we have already set up ConfigSource on the connectionStrings in web.config. The parameter will replace the value of ConfigSource with a client specific value.

Along with the parameters.xml file, each site we deploy to will need to have it's own XML file with the specific site settings.

<parameters>
<setParameter name="ConnectionString" value="Site1\ConnectionStrings.config"/>
...
</parameters>


Using MSDeploy is generally straightforward. To deploy these changes to a client site, we will first need to perform a msbuild with a target of "package", and the desired build configuration. We also set the location of the package to a specific location, which will let us find it later on:

msbuild "/path/to/project.csproj" /t:Package /p:Platform=AnyCPU;Configuration=Release;OutputPath=bin;CreatePackageOnPublish=True; PackageLocation=bin\Deploy\project.zip


And in teamcity:

Once the package has been created, we can deploy it to as many sites as we want without having to rebuild. For a quick example, here is a sample msdeploy batch file to use custom parameters when deploying to a local IIS website. The gory details are here, but I'll try to explain the code after giving you a peek :-)

"C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe" -source:package=%1 -setParamFile:%2 -dest:auto -verb:sync -disableLink:AppPoolExtension -disableLink:ContentExtension -disableLink:CertificateExtension -setParam:name="IIS Web Application Name",value="%3"


This batch file takes three parameters: %1 is the zip file package specified in msbuild. %2 would be the client-specific XML file. %3 is the local web site we deploy to. MSDeploy will take the deployment package, inject the parameters into it, and then uploads it to the local IIS instance.

%3 is actually an automatic parameter added when packaging, so we could have added it to the client specific XML as follows:

<parameters>
<setParameter name="IIS Web Application Name" value="ClientSite1" />
<setParameter name="ConnectionString" value="Site1\ConnectionStrings.config" />
...
</parameters>


However, in this case, I wanted the ability to reuse the deployment package artifacts created by TeamCity. Since the client web sites might have different names then their TeamCity versions, I left it out of the XML. We can use the -setParam command line parameter to set any parameter that is not a constant for each client.

If we run this batch file for each site, we can easily deploy customized releases to multiple IIS applications from a single build:

deploy.bat \path\to\package.zip path\to\site1\variables.xml ClientSite1

deploy.bat \path\to\package.zip path\to\site2\variables.xml ClientSite2

deploy.bat \path\to\package.zip path\to\site3\variables.xml ClientSite3