One of the questions I get asked fairly regularly is: "how can I can easily change different configuration settings in my web.config file based on whether my application is in a dev, qa, staging or production mode?" The most common scenario for this is one where an application uses different database connection-strings for testing and production purposes.

It turns out you can easily automate this configuration process within the Visual Studio build environment (and do so in a way that works both within the IDE, as well as with command-line/automated builds). Below are the high-level steps you take to do this. They work with both VS 2005 and VS 2008.

Open the VS Configuration Manager and create new "Dev", "QA", "Staging" build configurations for your project and solution

Add new "web.config.dev", "web.config.qa", and "web.config.staging" files in your project and customize them to contain the app's mode specific configuration settings

Add a new "pre-build event" command to your project file that can automatically copy over the web.config file in your project with the appropriate mode specific version each time you build the project (for example: if your solution was in the "Dev" configuration, it would copy the web.config.dev settings to the main web.config file).

Once you follow these steps, you can then just pick the mode your solution is in using the configuration drop-down in the VS standard toolbar:

The next time you build/run after changing the configuration mode, VS will automatically modify your application's web.config file to pick up and use the web.config settings specific to that build configuration (so if you select QA it will use the QA settings, if you select Deploy it will use the Deploy settings).

The benefit with this approach is that it works well in a source control environment (everyone can sync and build locally without having to make any manual changes on their local machines). It also works on a build server - including with scenarios where you are doing automated command-line solution builds.

27 Comments

An interesting solution; we've been looking at this very problem and had the copy of a 'web.config.release' done by a msbuild target. However we also need to deal with instance based configurations (for example to a client as opposed to deployment type) where this style of configuration management starts to get a little but unwieldly.

We use a similar concept except but use a seperate project (CommonConfig) for all config files spilt by build configurations then by project. We have 5 projects which have there own web.config/app.config files but each project then pulls in relevant AppSettings.config, MailSettings.config, ConnectionStrings.config and LoggingConfiguration.config from the CommonConfig in the post-build event based on build and project.

The problem we have, and most likely the same in your scenario, is that when you create a Setup Project the created files are not part of the primary output/content files as they are not part of the project. Adding them to the project is an option but that means that they will need checking out whenever a developer builds.

Scott, this does mean you have to maintain several config files and keep them in-sync.

We use tags inside our configs to denote variables. In our teambuilds, we use msbuild to change them, according to the build-type.
For day-to-day development, we are going to use the same msbuild statements in the projectfiles to change them to the correct values pre-build.

Step 1) states use a web application...is there nothing you can do to perform this same functionality in a web site project? I'm not overly excited about converting several older web sites to applications just so we can automate our web.config settings ;)

If I log in with Administrator account to the virtual pc, I can see the Configuration Manager. But I log in using my domain account, eventhough I added the domain account name to Administrators group, the Configuration Manager is invisible.
How can I open it?
Thanks.

A bit different to our approach.
We use one config file, with a setting in the main configuration area denoting "Development", "Staging", "Live", etc. and add configsections for each of these environments.
All of our config setting calls are routed through a handler which returns the value from the correct configsection based on the environment set (and defaults back to the main configsection if not found). Only one file to maintain and one setting to change as a compiled release moves through the environments. The only drawback is the connectionstrings section which is handled completely seperately to standard config settings :/

Why go through the build process for each environment if all that is changing is the config file? Seems like tedious work to me, especially if you have many projects in the solution and it takes a while to build. We are in that situation (large solution) and have multiple environments (dev, qa, staging, prod) but we also have to make client specific changes to the configs depending (appSettings, machineKey, authetication settings etc).

So what we do is build once in release mode and post-build call a very simple deployment tool we wrote. The utility creates the config files for each environment and client (using the solution one as a template), makes the client specific and environment specific changes, encrypts the appropriate sections, puts them in an appropriate folder structure and zips them up (i.e. prod_deployment_20070921.zip). Then we just xcopy deploy the whole thing to the appropriate environment.

Sure we took the time to create the utility, but not effort was required really. The advantage is we build once and immediately have all environments covered, rather than building 4 times.

For example, I would like to be able to have a production build that has two different deployed users - 'user1' and 'user2'. Every user has his own connection string. I would like to be able to add this as a parameter to the build so I can easily build a user-specific environment.

There is another to way to acomplish it, if MSI installer are build for the projects.
1. In the Primay Output Properties u can click the Filter to exclude the files that u dont want.
2. By using the Add->File command the specific production config can be added from a separate directory.

The MSI generated will include the production config instead of the developer config.

There is no IDE GUI for step 4, which is why I find Web Deployment Projects a better choice. WDP are great because you can treat your site as a website - which it is - and not a DLL project. I'm saddened to see MS take away this option in VS2008 - hopefully this along with the other WDP posts in this thread will get MS to take notice.

Now that the .net framework has a web.config file for each version on the machine, why not just store the values in the framework web.config on that machine. The values can be localized to specific applications if necessary, but more often it would simplify management as there is a centralized repository for things like connection strings across multiple applications. I'm not sure how to encrypt in the framework level web.config though...

I have really liked the method as described by dave rhodes's comment where you have the configSource set on specific sections of the web.config to point to seperate config files for that section (a ConnectionStrings.config file for example). This allows me to seperate out specific sections of the web.config that are actually different in each environment and then maintain them in source control. It is too bad though that you can't have the ConnectionStrings.config file be a symbolic link (like Linux can) to a Dev, Production, etc config file (this would be like having a second redirect).

>>>>> Is there a way of doing this for a project with a website in it rather than a web application?

You can use a Web Deployment Project for Web Site Projects to achieve the same effect and deployment time. I talk a little about how web deployment projects work here: http://weblogs.asp.net/scottgu/archive/2005/11/06/429723.aspx

We are looking at doing things a little differently at my company, though, and I'd like some feedback from the community at large if possible.

We have an environment in which we own the servers being used and so control them completely. Rather than use build dependencies, we're looking at creating a list of well known connection string names that reside in the Machine.config file. While the names never change, the connection string values do based on the environment that a particular server is used in, so dev machines always point to dev DBs, etc. As new servers are added, they receive the appropriate connection strings for their environment.

In this way programmers don't have to care too much about the current environment in code and deployments stay simple. Now we just need to create a tool to update the config files on multiple machines if a change is needed. :-)

Personally, I find the configuration section replacement feature of web deployment projects far superior to this technique. I allows to keep certain sections that are common to all deployment targets in the master web.config file, thus reducing maintenance headaches.

Hi Scott,
We have a bit of a problem with our environments. Whilst we make changes like everyone else here for particular environments we also have a problem with a growing user base exceeding the connections to the database that are available in the pool. Having to alter this and then rebuild is not an option for us as we have restricted access to the Live server and the facilities we provide our software too are quite critical so they can't go without within their working day.
Is there no longer a way to simply edit these settings on the fly?
Rowan