Doing automated builds you will inevitably encounter a problem deploying an application to different environments. You can only avoid it in the case if you live in the happy world of single box, but then you most likely do not bother with continuous integration in the first place. The worst of deployment configuration is unleashed during the "death race", when all those nasty wrong configuration bugs are discovered during the client presentation.
If application is built the smart way, all environment-specific details are encapsulated. In case with ASP.NET application it is most likely the web.config file. It is no doubt that the solution is to handle a deployment task to the automated script. Aside from technical details, the trick is to have some kind of configuration template in one hand and environment-dependable variables in the other and smelt them together when time comes, rather automatically than manually. What are our choices to settle the
shit configuration once and live happily ever after?
The easiest way seems to have multiple instances of the configuration file, so the relevant one is pulled to the deployment environment. There is a huge disadvantage, though, as you will become a victim of the main copy-paste curse - synchronization problem. It will quickly go out of hands if you have more than one project and more than two developers to worry about. Changes have to be tracked and reproduced scrupulously - that defeats the whole idea of laziness.
Another, a little bit exotic, but viable method, is to create a single config file with placeholders inside:
<add name="MainConnectionString" connectionString="[[MainConnectionString]]" providerName="System.Data.SqlClient" />
Then the deployment heavily relies on the build script, which will hold all necessary variables for different environments. The disadvantage is that the raw web-config file is unusable and you can not run the application without deploying it properly (even to the local environment) or tweaking the project build events to run it from the Visual Studio. On the bright site, the configuration lays in the hands of the
Jedi build master and the real production settings are hidden from the rest of the team (attention, Sarbanes-Oxley-compliant companies!).
The third choice is to create a custom configuration section which will be controlled by a single key, changeable by the build script. It maybe a full-scale class or something more lightweight. The first approach will give you all flexibility you may need, but would require some kind of common library if you have multiple projects. Second approach, would require developers to learn the new way of retrieving configuration values.
Whatever way we choose, we should keep in mind: it is the laziest way that will be favored by your fellow developers. If we will be able to access the configuration through the ConfigurationManager class (which is 100% customary) and to create relevant configuration sections just slightly different from what we used to (let's say - 80% customary) - it will be the preferred combination. Finally, developers spend more time using the configuration, than creating or changing it.
I would love to hear about the other ways to automate configuration deployment.
UPDATED: Another approach is to have most environment-dependable sections (e.g. connectionsStrings and appSettings) "outsourced" to the satellite files grouped by environment, using "file" attribute:
<appSettings file="config\production\connectionStrings.config" />
The build script is sent over the web.config file and depending on a parameter it just replaces the middle part of URI - from "production" to "bat" or "dev". It is a relatively small amount of automatic changes and deployment error is visible right away. This approach we are using now and it seems to work well. At least we can be sure that once we perfected the web.config, it is unlikely that somebody will mess it up with environment changes, which are encapsulated and independent from each other. The production settings can be SOX-friendly isolated and hidden. The downside - the good'ol synchronization problem.