When adding custom development functionality to MOSS/SharePoint 2007, making changes to Web.Configs is not as straightforward as a regular ASP.NET application. The reason for this is that MOSS uses a multiple-server model, and even if you have only one server, it’s still considered a Web Farm.
So, in the same way that MOSS automatically configures IIS sites, host names, authentication, and more, MOSS also has a centralized concept for modifying Web.Config settings.
A SharePoint site, as glamorous as it may make itself out to be, is nothing more than an ASP.NET web site running on top of IIS. Sure, there’s tons of functionality in it, but at the end of the day, it’s just an ASP.NET web site. With that in mind, it too will have a web.config file, where certain settings can be adjusted, custom application settings can be added, and so on. To give one example, assembly bindings can be specified (and even re-mapped to alternate versions of assemblies) through web.config file to make your application work.
Let’s say you have 5 front-end web servers that serve up your SharePoint site. That means you have 5 copies of IIS, and 5 copies of the SharePoint web application. In turn, you have 5 web.config files to keep up with. If you were to manage this manually, you would have to make sure that whenever you made a change, you manually copy web.config files to each server.
Now, let’s say you have 10 SharePoint web applications running on each of those servers. You then have 10 web.configs per server, so a total of 50 web.configs to manage across your farm.
And finally, let’s say you add a 6th server to your farm. You would have to make sure your 10 web.configs were added to the 6th server, on top of now having 60 web.configs to manage and keep in sync.
Microsoft has solved this problem with the centralized concept of web.config changes.
The general idea of this concept revolves around the fact that a Web.Config is nothing more than XML, and that with XML XPath support, certain blocks of HTML can be found with accuracy and reliability, and then changed.
So, if there were just one place where you could store your “changes”, the rest could be automated and kept in sync with the other servers. And when a new server was provisioned, it could automatically be sync’d with this copy of the changeset. If we only had a SharePoint concept, called, oh, I don’t know…
The SPWebConfigModification class in SharePoint (as of WSS version 3.0) allows you to specify changes with the following information:
- What “ownership” group should this settings change go under? (To group certain settings together for easy management.)
- What is the XML Node path you want to start at? (for example, /configuration/system.web)
- What is the XPath to the specific node you want to match?
- What is the block of XML that you wish to go under the matched node?
These changes are stored in a centralized repository per-web application, and are automatically sync’d with all SharePoint web applications in the farm at regular intervals.
Below is a screenshot of what this “central repository” looks like when viewed through an internal tool I developed for this purpose (blurred to exclude possible sensitive info):
How Changes are Applied
Changes are applied one of two ways:
- Manually (on-demand), through the SharePoint object model.
- Automatically, at a regular interval.
When a web.config changeset synchronization occurs, the following things happen:
- A timer job is created to synchronize web.config settings, specified to run on all farm servers.
- Within 30 seconds, each server picks up its timer job to synchronize its own web.config.
- Each server reads its own web.config, compares the XML nodes with the central modification repository entries, and ensures that all values match up. If a repository entry doesn’t exist in the web.config, it will be created, and if it does exist in the web.config with a different value, the web.config value will be updated.
- Depending on what was changed, this can cause a recycle of the SharePoint web application.
WARNING: SharePoint, by default, schedules this to happen automatically at regular intervals (at least once a day). This is why it is important NOT to make “manual” changes to web.config files (i.e. by editing them in a text editor). If your manual changes happen to match a repository entry’s XPath, your manual change will be overwritten as soon as this synchronization occurs.
Additionally, if the XPath is only ‘halfway’ correct, it can cause multiple entries of the same node. For an example of this, please read the previous post MOSS Web.Config Modification – Beware the XPath.
Managing Web.Config Changes for Structured Deployments
Unfortunately, there is not a good way within SharePoint’s out of the box Solution Deployment to handle web.config change deployments. As far as I know, you will need to roll your own. However, the team working on this particular project came up with a pretty clever way of managing these changes, and pushing them out in a structured manner.
First, a XML file that is more or less referred to as a web.config Changeset File:
<?xml version="1.0" encoding="utf-8" ?> <WebConfigChanges xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <!-- This change set is for unit test web applications --> <ChangeSet Name="UnitTesting" Status="Active" CanUpdate="True"> <ChangeItem Name="trust" Path="configuration/system.web" Sequence="0" Environment="Development"> <trust level="Full" originUrl="" /> </ChangeItem> </ChangeSet> <!-- This change set is for development web applications --> <ChangeSet Name="Development" Status="Active" CanUpdate="True"> <ChangeItem Name="compilation" Path="configuration/system.web" Sequence="2" Environment="Development"> <compilation batch="false" debug="true"> <assemblies> <add assembly="Microsoft.SharePoint, Version=22.214.171.124, Culture=neutral, PublicKeyToken=71e9bce111e9429c" /> </assemblies> <expressionBuilders> <remove expressionPrefix="Resources" /> <add expressionPrefix="Resources" type="Microsoft.SharePoint.SPResourceExpressionBuilder, Microsoft.SharePoint, Version=126.96.36.199, Culture=neutral, PublicKeyToken=71e9bce111e9429c" /> <add expressionPrefix="SPHtmlEncodedResources" type="Microsoft.SharePoint.SPHtmlEncodedResourceExpressionBuilder, Microsoft.SharePoint, Version=188.8.131.52, Culture=neutral, PublicKeyToken=71e9bce111e9429c" /> <add expressionPrefix="SPSimpleFormattingEncodedResources" type="Microsoft.SharePoint.SPSimpleFormattingEncodedResourceExpressionBuilder, Microsoft.SharePoint, Version=184.108.40.206, Culture=neutral, PublicKeyToken=71e9bce111e9429c" /> <add expressionPrefix="SPUrl" type="Microsoft.SharePoint.Publishing.WebControls.SPUrlExpressionBuilder, Microsoft.SharePoint.Publishing, Version=220.127.116.11, Culture=neutral, PublicKeyToken=71e9bce111e9429c" /> </expressionBuilders> </compilation> </ChangeItem> </ChangeSet> <!-- Used to configure error log page--> <ChangeSet Name="ErrorLogging" Status="Active" CanUpdate="True"> <ChangeItem Name="SafeMode" Path="configuration/SharePoint" Sequence="0"> <SafeMode MaxControls="300" CallStack="true" DirectFileDependencies="10" TotalFileDependencies="50" AllowPageLevelTrace="false"> <PageParserPaths> </PageParserPaths> </SafeMode> </ChangeItem> <ChangeItem Name="customErrors" Path="configuration/system.web" Sequence="1"> <customErrors mode="On" defaultRedirect="~/_layouts/error.aspx" /> </ChangeItem> </ChangeSet> </WebConfigChanges>
This file encapsulates what will go into a SPWebModification repository entry, and specifies a couple critical things:
- The changeset name (translated into the “Owner” in SPWebConfigModification)
- The environment that should be applied to each item (this provides a way to specify different settings for Development, Staging, Production, All, etc, for example, connection strings).
- The XPath and Sequence of the modification entry, directly translated into the SPWebModification entry.
A straightforward deployment tool parses this XML, takes a Web Application as an argument, identifies the current environment (via a MOSS Farm-level Property Bag setting), and translates relevant entries into SPWebModification entries for the specified web application.
This provides a structured way to deploy and manage web.config changes, specific to the environment. Although you can still get in a great deal of trouble if XPaths are not absolutely perfect, this provides a great way to eliminate human error to deploy web.config changes in a similar fashion as normal MOSS .WSP package deployments.