What is the web.config Anyway?
This is simply a configuration file that provides detailed configuration information that ASP.NET, IIS,
and the webserver need to successfully load and run your website application.
The file itself usually contains a ton of configuration information for the website application, as you
might imagine. The one in the root folder of your website application contains the majority of the
configuration details you need to know about. Any others usually just override the main configuration
file for that specific folder.
When it comes to the web.config, you definitely don’t want to lose it and the details it contains. Doing
so will almost always be detrimental to your website application. Over time, this file will become quite
verbose and recreating it is nearly impossible unless you have a backup of it.
What’s more, is that you’re almost certainly going to have more than one copy of your website in various
places around the server room, office, and various developers across the world. The versions of your
website may include staging, UAT, testing, and another one for each developer that writes code for your
website. Even your testing team, if you have one, will likely have a local copy of the source code of
your website to properly test updates before they get deployed into other environments.
You might be thinking, now, "How in the world do I safely keep track of that across every single
environment?" It’s an outstanding question. It only takes a single developer making a change to the
web.config to get the website working in their own development environment before the risk of it getting
accidentally deployed into your testing, staging, and production environments.
Can I Commit the web.config to Source Control?
The short answer is generally yes, but this presents a ton of complications moving forward. Every
environment where the web.config is restored to and used in will require slightly different versions to be
in place. In the very least, your database connection strings will need to be different. So, committing
this file to source control is something that needs to be done carefully.
When it comes to the information in the web.config, most use cases will determine that the majority of the
configuration details be the same from environment to environment.
How Can I Make the web.config Safe to Commit to Source Control?
The answer is surprisingly simple and elegant. It’s also a built-in feature that any ASP.NET application
can take advantage of. Most of this is inspired by a blog post by the one and only, Scott Hanselman.
First, you need to identify the sections that need to be unique per environment. Then, you need to come up
with a common pattern for your development, testing, and deployment teams.
In the case of DNN-based websites, there are two sections that tend to be the most unique in each
environment. They are the <connectionStrings>
and <appSettings>
sections.
The <connectionStrings>
section probably sounds self-explanatory. Every instance of the website will
likely require a different database connection string for each database connection the application needs.
The <appSettings>
section contains global settings for the application. While much of it will be the
same across deployment locations and environments, there tends to be one or more values that need to be
different in each location the website runs from. This may also include legacy connection strings, API
details for external systems that are integrated, and nearly anything else.
Updating the web.config for Multiple Environments
Since we’ve identified the <connectionStrings>
and <appSettings>
sections in our example, we’ll
continue to work with that. A development pattern that we’ve found to work fairly universally so far is to
add two new files to the App_Data folder. (If your application doesn’t have this folder, create it. It’s a
protected application folder for ASP.NET websites.)
Create the following two files in your App_Data folder (note that they’re still .config files, for security
purposes):
- appSettings.local.config
- connectionStrings.local.config
The naming convention you see above is as follows:
- Section name as it’s seen in the web.config
- Environmental context (e.g., a local-only configuration)
- Configuration file extension
In each file, you’ll want to add the respective section from your original web.config file. An example of
the contents of your connection strings configuration file may look like the code example below.
<connectionStrings>
<add name="SiteSqlServer" connectionString="Data Source=(localhost);Initial Catalog=MYDATABASE;Integrated Security=True" providerName="System.Data.SqlClient" />
</connectionStrings>
Your application settings may look like this example:
<appSettings>
<add key="InstallTemplate" value="DotNetNuke.install.config" />
<add key="AutoUpgrade" value="false" />
<add key="UseInstallWizard" value="true" />
<add key="InstallMemberRole" value="true" />
<add key="ShowMissingKeys" value="false" />
<add key="EnableCachePersistence" value="false" />
<add key="HostHeader" value="" />
<add key="RemoveAngleBrackets" value="false" />
<add key="PersistentCookieTimeout" value="0" />
<add key="EnableServicesFrameworkTracing" value="false" />
<add key="UpdateServiceUrl" value="https://dnnplatform.io" />
<add key="PreserveLoginUrl" value="true" />
<add key="loginUrl" value="~/Login.aspx" />
<add key="ValidationSettings:UnobtrusiveValidationMode" value="None" />
<add key="ImprovementProgram.Endpoint" value="https://dnnapi.com/beacon" />
<add key="InstallationDate" value="12/13/2019" />
<add key="InstallVersion" value="09.04.04" />
<!-- additional values omitted from this code sample -->
</appSettings>
Next, you’ll need to update your web.config file to (1) remove the original section, and (2) point to the
new location, at the same time. The example below shows what that looks like.
<connectionStrings configSource="App_Data\connectionStrings.local.config" />
<appSettings configSource="App_Data\appSettings.local.config" />
That’s it, from the highest level. If the website was already successfully running, you can save all of
these files and run the website to verify it’s working right away. (It’s worth noting that you can mix the values between the local config files and the root web.config, but we’re trying to keep the code samples simple here.)
You can now safely begin committing the web.config into source control. From this point forward, every
commit to the web.config should reflect changes that are required across every instance where the website
runs, just like any other code updates you might be working on.
Update the .gitignore File
(This is obviously assuming you’re using Git for source control...)
Now that you’ve safely updated the web.config for distributed teams and environments, you may need to
update your .gitignore file to allow the web.config file to be committed, if it’s not already being done.
Another update you’ll need to do now, though, is to update it to also ignore the new local instances of
the configuration files we just created. This may look like the following example:
# local configuration files
*.local.config
Update your README for Default Values
When you have new developers, testers, and consultants come aboard, they’re going to need to set-up their
own environments successfully. So don’t forget to update your solution’s README and/or your organization’s
developer documentation to help them know how to get started with a newly restored environment. They need
to add the local configuration files with the correct details that everyone else has.
You can, of course, have existing developers send their respective copies of the local configuration files,
but it would be much more secure and consistent with processes if they were to get those from a secured
and safe location in your organization, such as your intranet/extranet, or a shared secure file-sharing
server. Anything requiring at least one-level of authentication will do.
As any of the default values change, you’ll need to ask all stakeholders to update their local copies.
When this occurs, you should just point them to the centralized location where the default values are
maintained to keep things streamlined and secured.
Closing Summary
It really is that simple. How do you do this in your own projects and teams? Do you have similar tricks,
or do you do something else entirely? Let’s chat about it in the comments below.