Monday 18 October 2010

Config management with Rake for .net projects.

In pretty much every project you work on, you are going to need to control configuration files for multiple environments (perhaps Development, Test, UAT, Production, etc.)

In the .net world, configuration tends to be held in config files (app.config / web.config), usually in the root of your projects. There are various ways to deal with multiple config files – you can create custom build configurations in visual studio, or source control multiple configuration files, making use of the "file" attribute in .net config files. Both of these methods can result in a pile of different config files in your project which will need to be maintained as new items are added to your configuration.

Furthermore these methods mean that the developer has access to and ultimately, responsibility for updating and source controlling the configuration of each environment the application gets deployed to. This is not always desirable, as, if the test environment owner renames a url, or moves a database, the developer has to update their config files for them. Further more, it means the developer often has access to things that perhaps they have no business seeing (Live DB connections strings etc).


Configuration – a better way

It seems it would be better if environment owners, or configuration managers could own and maintain configuration settings for their own environments. This makes sense from a management perspective and a security perspective.

In an ideal world, a developer would create a build and distribute it to be deployed to any appropriate environments. The environment owners could then drop in their own configuration file, and in a simple step, configure the build for their own environment. The following solution allows for just that.

Enter Rake and Configatron


Installing ruby rake and configatron

First things first – we need to install ruby, rake and configatron on our machine. I recommend installing ruby with the one click windows installer:

http://rubyinstaller.org/

Once installed, open a command prompt and type

gem install rake

then

gem install configatron

And we're done.


It's all just find and replace…

Download a demo solution here

In the following example, we will set up rake and configatron to manage the config for a .net based web project. We are keeping it simple by keeping all configuration items in the web.config, but this is by no means a constraint – we could have separate email.config / anythingelse.config files if we wanted.

We're going to use the standard web config file that you get when you create a .net web (or MVC) project to hold our development settings. An excerpt from the web.config file, containing some settings that would change between different environments can be seen below:

The first thing we need to do is create a copy of our web.config file. We will rename it web.configatron.

This will hold variable names in place of the values that change between different environments. The same excerpt as that taken for the web.config file above can be seen for the web.configatron file, below:

The highlighted areas show the values from the web.config have been replaced by variables. These are place holders that will eventually be replaced by the various values for each environment.

The next step is to add a new file to the root of our solution called RakeFile.rb:

This file is going to contain our rake script, which will contain the code to configure our application. If we want we can also add code in here to build the projects, and even create single step deployments to actually push the final configured build out to the appropriate servers. For now, we will just concentrate on the code required to take care of the configuration for us.

We now need to add another file to our solution. We'll call it test.yaml. This will hold the values we want to push into our config file for the test environment.

This file will be in the yaml format, but you don't really need to know too much about that… It is simply a human friendly way to store data in a readable format.

Currently our web.config holds two values that are different in each environment: The supprtEmail app setting, and the ApplicationServices connection string. The yaml file to hold these settings looks as follows:

Writing our Rake script

When we are done, we will be in a situation whereby we can open a command prompt at the root of our build and run the command:

rake configure_all_projects environment=[[your environment name]]

Which will completely configure the build for the environment specified in the [[your environment name]] variable.

We need to define two Rake tasks in the RakeFile.rb. The first will allow us to pass in the environment variable in the command line specified above:

The script should be fairly simple to read, even if you've never seen any ruby before. Basically, we check to make sure the user entered an environment variable when they ran the command line. If they did, we check to make sure there is a corresponding yaml file containing settings for that environment. If there is, the task is complete.

Now our second Rake task:

The second line, where we define our task looks as follows:

task :config_all_projects => :setEnvironmentVariables

The little arrow sign points to the name of the first task we defined. This simply means that the setEnvironmentVariables task is a dependency of the config_all_projects task. In real terms, this means that if we run the config_all_projects task, it will run the setEnvironmentVariables task first.

Following this, the script searches for all files in the project with the file extension .configatron.

For each of these files, it reads through them, and matches any text in the format:

#{configatron.**.**}

And replace it with the corresponding value in the yaml file.

If at any point it comes across a value specified in the configatron file that does not have a corresponding entry in the yaml file, it will stop and tell us we have a missing value.

We now have everything we need in place to configure a build for a specific environment.

To test the configuration script, open a command prompt to the root of the solution and run the following command.

rake config_all_projects environment=test

If you now check your web.config file, it should contain the values from the test.yaml file, rather than the dev values that were in there before.

If we want a new environment, say UAT, take a copy of the test.yaml file and call it UAT.yaml. Edit it to contain the UAT email address and connection string. Now from the command prompt, run

rake config_all_projects environment=UAT

Your web.config should now contain the appropriate values for UAT…

You should be able to see from this, you as a developer can deliver a build. An environment owner can drop their own MyEnvironment.yaml file into the root of the build and run rake. This will completely configure the app with the settings in their own yaml file.

2 comments:

Dazzie P said...

Nice post man..

Used this on a recent project.. works a treat, makes releases really quick and simple..

Cheers..

Ali said...

Nice. Was just looking for a better solution for web.config files in older projects. The web.config Transform in VS2010 seems to do the job for new ones.

One thought, could you also run the rake command using the Pre-Build Event command line and the $(ConfigurationName) variable (provided you .ymal files follow that naming convention) as described in the Scott Hanselman post you mentioned.