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/

6 Comments

  • One solution I use is to use

    &lt;appsettings file=&quot;Dev.config&quot;&gt;.



    The main configuration file contains settings appropriate for the production environment. Dev.config overrides these with settings for the dev environment. Dev.config is not deployed, so is silently ignored in the production environment.



    Not entirely satisfactory as (a) it only automatically supports two environments and (b) it only handles appSettings.

  • You can probably use nant xmlpeek and xmlpoke reading properties from another nant file using include nant task.





  • We honestly haven't come up with a good way yet. This really comes up most with a Connection factory class we use. We decided to move connection strings to machine.config and that has eliminated that issue mostly, but as far as application specific stuff, it's been a pain.



    In general our environments are pretty identical, including machine names, luckily. This has actually been our major roadblock for automated pushes to integration servers post CC.Net builds.

  • Nice... I have run into such issues in the past. The caveat, i think, with the approach of &lt;appsettings file=&quot;Dev.config&quot;&gt; is that you still have to come up with an approach where you can specify/modify values of other configuration elements e.g. different impersonation account, or different server name/port for remoting entries(i.e. different app servers in dev, test, prod environments).

  • Hi has any body considered creating a DB to control the configuration files by running a build command that then builds the config file on demand from the DB? This way a lot of checks and test can be put in place and it is also easy to change

  • Hi John,
    One of the issues you would need to sort out if you used a database would be version control. It is nice to be able to checkout/checkin/rollback/diff/merge etc... these settings, and keeping them as plain text, checked in to source control works well. If you used a database, you'd either have to invent a scheme & tooling to do this, or forgo it. One project I work on recently replaced the XmlPreprocess Excel file with a simple WinForms tool that edits the settings files directly so it even takes the Excel binary file out of the equation.

    By the way, this post is incorrect when it says I can't share the code. The tool can be found at htthttp://xmlpreprocess.sourceforge.net

Comments have been disabled for this content.