Overview
The intent of this document is to lay out some very simple practices and some basic knowledge to make application configuration as straight forward as possible. The outcome of this knowledge is that you will be able to successfully build your application for different environments without having to edit the web.config file for each build.
Only the most trivial applications avoid the need for configuration files. Almost every application I have dealt with has the need for some type of configuration. In the normal course of development I’ll need to be able to test out my application while developing it and the testers will need a different database to do their job. To do this I use a configuration file. This is the simplest case but the need for configuration gets more complex as you add environments, features, and the need to integrate with other systems.
Environments
The typical custom application development project will have five environments that will be used to control the release of the finished work product. The names of the environments can change depending upon the particular customer but the following are common environment names.
Self-hosted
This is your workstation or laptop where all of your work is done. The major difference between all of the other environments is that this should be the only environment that has Visual Studio installed. This is also the environment that the application should be able to immediately run in as soon as a new person gets the source code from whatever repository is being used by the project (TFS, subversion, etc.) and presses F5 to run the application in the debugger.
Development
This is an environment where everybody’s code comes together and is built and deployed so it can be tested by the developers in an environment similar to what will be seen in production. While Visual Studio shouldn’t be installed here it often ends up here so debugging can be performed.
Test
This is the QA environment where the product is built and deployed so that QA testers can perform complete functional testing in an environment that duplicates production. Visual Studio should absolutely not be installed anywhere in this environment. Developers do not have access to this environment, deployment is done either via an automated script, a QA administrator, or a build master.
Staging
This is essentially a production environment. The product is deployed here for final acceptance and testing before the product goes live. Visual Studio is not here and developers are not permitted access to this environment*. The deployment to this environment is done through automated scripts or performed by system administrators.
Production
This is the live environment that end users access. The deployment to this environment is through automated scripts or performed by system administrators. It should be obvious that developers are absolutely not permitted in this environment.*
*In reality developers end up with access to these environments but I personally think they are foolish accepting this access. It only transfers risk from the administrators who are responsible for operating the environment to the developer.
Build Configurations
Inside your Visual Studio project you will need to create a build configuration for each of the above environments. By default when a new solution is created Visual Studio automatically creates Debug and Release build configurations. Luckily you can rename them and add additional configurations as needed.
When you create a new build configuration you can add a configuration transform to any web project in the solution. Visual Studio will do this through the project’s context menu. For instance an ASP.NET MVC project by default will have a web.config file along with a web.debug.config and web.release.config. If you create a new build configuration called “Staging” you can add web.Staging.config to the project by right clicking on the project in solution explorer and selecting “Add Config Transforms”. Visual Studio will add a skeleton transform file to your project for you.

Recommendations:
The self-hosted environment should be in the base configuration file (web.config) and should have the correct settings for a new developer to get going without needing to make any changes.
Everybody on the team should have the same settings and the web.config file should not need to be changed unless the change affects everybody on the team. Nobody should need to remember to exclude web.config from the change set because they have a different database connection string or something else equally silly.
All of the other environments should have their own configuration transform and should be setup so that developers do not need to make any changes when they perform a build for that environment.
Configuration Transform
The configuration transform allows you to change the configurations depending upon the build being performed. Unfortunately without hacking Visual Studion and MSBuild only perform transforms for web.config. There are solutions to transform app.config but they are outside the scope of this article.
The configuration transform capability in Visual Studio offers a wide range of capabilities. Below are the capabilities that are most commonly used. For more information on Configuration Transforms refer to this article: Web.config Transformation Syntax for Web Project Deployment Using Visual Studio
For information on how to do transformations for other configuration files refer to this Code Project article: Transform app.config and web.config
Replace
Replaces the selected element with the element in the transform file. For example, we can turn off debug output for the compilation tag in our web.config using the following transform
<system.web>
<compilation debug=”false” tragetFramework=”4.5” xdt:Transform=”Replace” />
</system.web>
Notice that there is no locator specified. In this case it is not needed. However, in app.setting or other sections where you need to target a specific setting in a dictionary you’ll need to specify a locator. In this case I’ll use the Condition locator which uses an XPath expression. Here is an example for appSettings:
<appSettings>
<add key=”ErrorMode” value=”” xdt:Transform=”Replace” xdt:Locator=”Condition(@key=’ErrorMode’)” />
</appSettings>
Generally the Condition locator is the only one you’ll need, however there are other locators you can use (see the MSDN documentation).
Insert
Insert jams in the element from the transform. This is useful for turning on authentication in production for instance. A simple example:
<system.web>
<authorization>
<deny users=”?” xdt:Transform=”Insert” />
<authorization>
</system.web>
Visual Studio Support
Visual Studio will help you test out your configuration transform without having to build your solution for each build type. To see the results of your transform:
- Right click on the transform file in the Solution Explorer

- Select “Preview Transform”
- The editor window will be split in half. On the left is the original web.config and on the right is the configuration file after the transforms have been applied.

Custom Configuration Sections
The usual practice in most project is to jam every possible configuration need in to the app.settings section. It works but quickly becomes unmaintainable as your application grows and requires additional configuration capabilities. However, as the application’s complexity grows and its configuration needs grow the app.settings section becomes difficult to manage. At some point along this path the use of custom configuration sections will come up.
If you search the internet for information about custom configuration sections you’ll find information about how to write a custom configuration section class so you can make your own. However, writing code for a custom configuration section is unnecessary. There is an easy solution to this that does not involve writing a custom configuration section class. The class behind the app.settings section is actually just NameValueSectionHandler, which you can use for your sections.
As an example our application needed to send email to users. We needed to store the URL for our mail server, the user name and password to access the mail server, which port to use, and a few other settings. Instead of just putting this in app.settings we setup our own section called Messaging and then added Messaging to our configuration.
To set this up we added a new section called Messaging to the configSections of our configuration file:
<configSections>
<section name=”Messaging” type=”System.Configuration.NameValueSectionHandler” />
</configSections>
Then we added Messaging to the configuration file:
It looks just like App.Settings, but keeps our setting separated and neatly organized. To access our settings we just have to use one extra line of code as follows:
NameValueCollection settings = ConfigurationManager.GetSection("Messaging") as NameValueCollection;
if(null == settings) {
throw new ConfigurationErrorsException("Missing Messaging configuration");
}
string smtpUser = settings["SmtpUser"];
Technically the above code should include a check to make sure settings is not null, but this is just a sample. It is not difficult to wrap this in a class to encapsulate everything above to avoid writing the same lines of code repeatedly.
External Configuration Sections
In some cases because of the volume of configuration information needed it makes sense to divide the configuration file in to multiple files. Unfortunately the configuration transforms done by visual studio will not be done to the external configuration files but you can just store each environment’s configuration information in a separate file. However, keep in mind in the case of a web application all of the configuration files have to be marked as content so they’ll be deployed to all environments which is not really a good idea. You’ll need to add some custom build steps to remove unneeded configuration files from the deployment package.
In our web application we’ll add an App_Config folder to store our external configuration files. We’ll then add whatever configuration files we want:
In our web.config file we will setup our connection strings as follows:
<connectionStrings configSource=”App_Config\connectionStrings.config” />
The cool part to this approach is that the code that is written to access the configuration information does not change. The application is not aware that the configuration information it is using is actually stored outside of the .net configuration file.
Encrypting Configuration Sections
There are occasions where sensitive information needs to be stored in configuration files. User names and password are a common example. Having plaintext usernames and passwords laying around a file system is generally not considered secure. Further I as a developer really do not want to know the usernames and passwords for the production environment. Happily there is a solution to this problem that does not require your application to encrypt and decrypt configuration information for itself.
In this case we are going to use the Pkcs12ProtectedConfigurationProvider which Microsoft released as sample code. The provider makes use of an X509 certificate for encryption and decryption which means you’ll need to install the certificate on the build server (or workstation) and in the production environment where the application will run.
The code for Pkcs12ProtectedConfigurationProvider can be found here: Pkcs12ProtectedConfigurationProvider 1.0.1
Add the provider to your solution. In your web.config file (not in the transform) add the following:
<configProtectedData>
<providers>
<add name=”Pkcs12Provider” thumbprint=”XXXX” type=”Pkcs12ProtectedConfigurationProvider.Pkcs12ProtectedConfigurationProvider, PKCS12ProtectedConfigurationProvider” />
</providers>
</configProtectedData>
The thumbprint value comes from the certificate. To get the thumbprint use the certificate manager plug-in.
Now that you can read encrypted configuration information you need encrypted configuration information. The process for creating the encrypted sections of the configuration file are as follows:
- Create a plaintext version of the configuration file.
- This can done either by doing a build and getting the web.config file or using VisualStudio to produce the configuration file. This web.config file will contain the production (or stagin) user names and passwords that we want to secure.
- Use the ASPNET_REGIIS utility to encrypt secure sections of the configuration file
- In order to use ASPNET_REGIIS to encrypt your configuration you have to do the following:
- Start either powershell or the command prompt
- Copy the Pkcs12 assembly to the .net framework folder (c:\windows\microsoft.net\v4.0.30319\). Also add that path to your path (PATH %PATH%;….)
- Create a temp directory and 1 sub directory for each config file (c:\temp\prod, c:\temp\staging)
- Cd in to one of the config directories (c:\temp\prod)
- Enter aspnet_regiis –pef “connectionStrings” “.” –prov “Pkcs12Provider”
- Repeat for any other sections that need to be encrypted
After they have encrypted everything that needs to be encrypted they return the configuration files to development. You extract the encrypted sections and put them into the corresponding configuration transform using the Replace transform. For example:
We now have secured our configuration information and our application does not have to make any code changes.
When the production version (or staging version) is built the above encrypted connection strings will be transformed in to the web.config file.
One limitation of encrypting configuration information is that you may not encrypt app.settings.
Also keep in mind that the web.config file cannot be more than 250Kb in size. For most applications this is not an issue. However, if you find your web.config file growing too large you can use the external configuration file feature previously discussed to move parts of the configuration information out of web.config.
Conclusion
Creating and maintaining configuration information for applications can be made easier by using the previously discussed features of the .NET platform and Visual Studio. Configuration Transforms allow the web.config file to change depending upon the type of build being performed. Using the same class that App.Settings uses allows you to easily create custom configuration sections without writing extra code. You can move configuration sections in to external files to make it easier to manager. Sensitive information can be encrypted in your configuration files without needing to write additional code.
Making use of this knowledge will reduce errors and make deployment to production easier.