Thursday, April 17, 2008

Managing Environment-Specific Settings In Your Projects

Having an automated build and deployment process is a key and sometimes overlooked component to building software. Part of that process is implementing a mechanism that will manage the changes to environment specific settings that need to be applied to your configuration files when deploying to a specific environment. And if your current mechanism is using notepad to edit your web.config/app.config once your code is deployed....please continue reading :-).

A few months ago, I was turned on to the Enterprise Library Configuration Tool, a new component introduced in Microsoft's Enterprise Library. David Hayden has a good post about how to use this tool. Patterns and Practices explains it here. It addresses this very issue, and provides a user interface to view your settings and apply environmental-specific overrides. I started using this tool, and found that I enjoyed using that UI because I could easily see all the values for a setting across my environments.

I was able to automate the merging of the delta files created by the configuration tool into my build process with a executable provided by EntLib, called MergeConfiguration. I updated my MSBuild project file, TFSBuild.proj, with a target to run this executable after my code was dropped.

The downfall to this method was a limitation I discovered in the Configuration Tool. The settings I was allowed to manage and override were limited to Enterprise Library configuration sections, connection strings, and application settings. I could not change values of settings in sections such as System.Web and System.Net. Hmmm. My honeymoon phase with this tool was officially over.

Another solution to this problem that I like is a solution that Scott Hanselman talks about here. It is pretty simple and here are the steps...
  1. Create a new configuration in Visual Studio for each of your environments, such as Staging, Production, etc. You can model each one after the existing Release configuration for starters.
  2. Create a separate configuration file (i.e. web.config) for each of your environments. So you would end up having web.config.staging, web.config.production, etc.
  3. Create a Pre-Build Event that swaps the environmental-specific web.config for the existing one based upon the configuration that is being built. You can get a version that does this in Scott's post.
After trying this method, I found a couple of things I did not like. First of all, I didn't like having to create environment-specific configurations within Visual Studio. In some solutions, I have multiple configurations for code that will be deployed to different locations. I don't like idea of duplicating configurations for all those existing configurations, that all are essentially the same. Secondly, in larger solutions, you will have to copy that "pre-build" event into each project that has a configuration file. This spreads this very similar logic across my solution in different places I need to keep track of.

So...I have been using a hybrid approach that addresses these issues for me...
  1. I create a separate configuration file for my different environments (web.config.staging, etc).
  2. I add a target to my MSBuild project file (TFSBuild.proj when using TFS) to copy the correct configuration file to the root of my application after it is built. This way, if I have to copy multiple files for multiple configurations, all of the statements are right here.
  3. (Optional) I modify the properties on the configuration files in the root of my projects to set their Build Action to "None". This means that they will not be included in the build. I like the settings in those files to be local development settings, and know that they will not be replicated to other environments. So what is deployed is either the correct environment-specific config copied in Step 2, or none at all.
The downside with this method (and the one Scott describes) is that you have multiple web.config files to manage. Some people really do not like this. I don't mind it. If you make use of the fact that you can split out configuration sections that do not change across environments into separate includes, you can eliminate a lot of the redundancy. To me, you are either going to have multiple config files, or multiple delta files. It probably just depends on preference.

There are definitely other ways to solve this problem. If you have other good solutions, I would love to hear about them....

No comments:

Post a Comment