Deploying .NET config files
One tricky aspect of deploying a .NET application to a variety of environments (development, test, integration, production) is managing the config files. The more environments and the more config files you have, the bigger your problem.
I have seen a couple techniques used to manage config files:
- Maintain different versions of the config files for each environment
If you have 4 environments and 20 config files, you have 80 files to keep in sync. If this is left to a human, good luck. - Maintain a templated version of each config file and generate real ones
In this technique there is some type of transform performed on a template config file, by doing some type of token replacement using regular expressions, or XPath. This technique often introduces some type of token placeholders or XSL in the template that render it unusable during development. Put another way, the developers have a "live" development config file that they make changes to and test against, and then have to go to the master template to make the official changes. This is better than making the change 4 times as in the previous technique, but it still causes risk by leaving it up to a human to keep the template up to date. It would be nice to have the developer config files be THE source of truth.
My goals were to:
- Not have to maintain multiple configuration files or templates of configuration files for different environments. In other words, to have a single source of truth.
- Keep the original source of truth configuration file fully operational for development. Developers should be able to get the file directly from source control and run without having to make changes to it.
We accomplished this by decorating our config files with special preprocessor comments that contain instructions for an XML preprocessor. Then we use an XML preprocessor command line tool to process a configuration file and deploy it into a target environment making appropriate environment specific substitutions.
For example if you want to be able to specify a different app setting for different environments, you might do something like this:
<configuration>
<appSettings>
<!-- ifdef ${active_directory_path} -->
<!-- <add key="active.directory.path" value="${active_directory_path}" /> -->
<!-- else -->
<add key="active.directory.path"
value="LDAP://corp.foo.com/DC=corp,DC=foo,DC=com" />
<!-- endif -->
</appSettings>
</configuration>
The development setting will work fine for an unprocessed file, but when this file is deployed using the XML preprocessor tool with the ${active_directory_path} property defined, the ifdef condition will be tested, and because the property is defined, the comments around its body will be removed, and the else branch will be omitted entirely. This will render the following:
<configuration>
<appSettings>
<add key="active.directory.path" value="my new value" />
</appSettings>
</configuration>
We feed the XML preprocessor a set of properties defined in an external XML file and/or passed on the command line. We have tied the XML preprocessor into our MSI's as a custom action so the files get processed at deployment time with the environment settings file specially tailored for that environment. We have a property file for each environment, and these are managed by an Excel spreadsheet tool which gives a nice unified view of all properties for all environments side-by-side and generates the settings files.
Unfortunately I won't be able to share the code for the preprocessor, but I wanted to blog about our solution and ask how others have solved the config file management challenge
Find XmlPreprocess here: http://xmlpreprocess.sourceforge.net/