Saturday, August 25, 2007

Configurable deployment of deployable configuration

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.


Roehl said...

We learned our lesson the hard way, going through the pain of versioning multiple configuration files, just as you described. (Funny, I didn't get a lot of push back from the team when I originally suggested that inane process...)

But from that pain, we got to our current solution, which I'm sure is not new at all: use a single configuration file. As you said, placeholders have problems, since they need to be processed for the local environment before they work. So we just don't use placeholders.

Instead, the configuration file has all the correct parameters for the local box. The environment parameters on the other hand, are - somewhat unfortunately - in XML comments (I say unfortunately, because I think that processing instructions would be the correct implementation in the XML world, but PIs get flagged as errors in the web.config).

To do an environment-specific build, we execute the build script (or our build engine executes it for us) with a parameter indicating the environment we want. Our build script simply detects the comments and strips out anything that doesn't belong to the environment requested.

Michael Goldobin said...

Right, that's another way to consider - use the same configuration file as a storage of an alternative configurations. May grow a little bit messy, but it is more compact solution, than managing multiple files. The only downside I see is that developers have to change main web.config file for different environments, thus - they have to have access to it.

© 2008-2013 Michael Goldobin. All rights reserved