Finally: THE way to add web.config modifications to SharePoint

As most of you (should) know, SharePoint farms can have more than 1 WFE (web front end) server, each with its instance of the web applications available in the farm. For example, if you have 3 WFE’s and 2 web applications, each WFE could provide either or both of the web applications. Each of those web application “instances” would then have a virtual directory in the c:\inetpub\wwwroot\wss\VirtualDirectories folder, typically denoted by its port number or its host header name. Each of these folders will have a web.config available.

There are many reasons to add stuff to the web.config, some of the (in most cases) wrong reasons include:
– for storing application settings: in SharePoint, consider using property bags or list items for your configuration settings instead
– for adding safe controls or safe assemblies: in SharePoint, consider using a SharePoint solution which can provide both in a declarative way

Other configuration settings that make more sense are entries for logging, caching, unity, appsettings required by 3rd party tools, …
For these, web.config modifications are needed. However as I noted before, there could be many web.config’s for the same web application, one on each WFE serving that web application.

The best and most automated way is to create a SharePoint feature and add custom code in the feature receiver to add and remove the web.config modifications. This is provided by default by the SharePoint object model, but the documentation lacks examples and a thorough discussion. I’ll do my best to guide you through it.

Web config modifications through the SPWebConfigModification class in the object model provide a way to add or remove atomic changes to all web.config files across a web application throughout a SharePoint farm.
Let’s have a look at the MSDN documentation of the SPWebConfigModification class first:
http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.administration.spwebconfigmodification%28v=office.14%29.aspx

The example in the article is correct, but the API is misleading.
Basically, what you need to do is create an instance of the SPWebConfigModification class, fill in the necessary attributes and add it to a SPWebApplication object corresponding to the web application you are trying to modify. After that, you need to apply the web config modifications and update the web application to save the new collection of changes. Every call to ApplyWebConfigModifications will try to apply all bindings that have not yet been applied.

The most tricky step is the first step, creating the SPWebConfigModification instance. Many have erred, so pay attention closely now.
There are several attributes that you need to get exactly right in order to succeed. We’ll go through each of them.

I’ll go through it with a basic example of adding an appSetting (untested code for reference only):

SPWebConfigModification mod = new SPWebConfigModification();
mod.Name = @"add[@key='tempFilesPath'][@value='c:\temp']";
mod.Path = "configuration/appSettings";
mod.Sequence = 0;
mod.Owner = Constants.OwnerModif;
mod.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
mod.Value = @"<add key='tempFilesPath' value'c:\temp' />";
webApp.WebConfigModifications.Add(mod);
webApp.WebService.ApplyWebConfigModifications();
webApp.Update();

Name: undoubtedly most mistakes occur here. This is NOT a pretty or internal name you give to the modification. This needs to be the tag name of the XML node you are modifying. Note that you need to provide EVERY parameter that you wish to set as well! Don’t add child nodes, that won’t work. Note that this is an XPath expression to identify the node. This seem silly at the time, but makes it possible for SharePoint to find the node for deletion afterwards. Failing to set this attribute correctly will result in not being able to remove the attribute afterwards.
Path: the XPath expression of the parent of the node you are trying to add/set. If multiple elements exist or if one node in the path has attributes defined, you need to specify each and every one of these attributes in XPath form. The more complex example below will show this.
Sequence: the order in which to execute the changes to the web.config. This may not seem relevant at the time, but as I explained for the “Name” attribute, you cannot add an entire XML tree at once, so you will need to break it up into baby steps, hence the need to work from the top of the tree down, and you will thus need a way saying which change to apply first.
Owner: very important for removal purposes, this is a unique name you give to your changes you make in this feature. Don’t use your first or last name or something like that, SharePoint wants to know who’s adding it, in this case the feature ID or feature folder name would be ideal, but if you’re not doing too many changes, you could just call it “appSettingsWebConfigModificationOwner” or something like that. But put it in a constant, you’ll need it when removing.
Type: you will need to use EnsureChildNode most of the time.
Value: the actual XML to insert into the web.config. This seems like it’s totally useless. I haven’t tested it through, but this tends to lean towards the fact that you could add an entire tree at once. I have not tested this in detail, so experiment a little to find out.

Once you execute this code (in a feature receiver or a test console app) you’ll notice how the changes are (sometimes slowly) being propagated across the farm. After a while (could be 2min – a timer job does the job) you’ll find the new configuration settings across your web apps. If not, you probably get an exception saying some node is missing, read it and interpret it clearly.
I have had an occasion where I got the error “There are multiple root elements on Line 1” … This seemed to be unrelated and an inherent problem with the server I was working on. If in doubt, try it out on a small farm to see if your code works.

Finallly, for removal, it’s actually pretty simple, just iterate over all web config modifications and remove them based on the Owner property you set before:

var toRemove = new List(); 
foreach (var mod in webApp.WebConfigModifications) 
{ 
  if (mod.Owner == OwnerModif) 
    toRemove.Add(mod); 
} 
foreach (var mod in toRemove) 
{ 
  webApp.WebConfigModifications.Remove(mod); 
} 
webApp.Update(); 
webApp.WebService.ApplyWebConfigModifications();

And then, for a more complex example, we want to add the following tree to the web config to add basic logging support:

  <system.diagnostics>
    <trace autoflush="true" />
    <sources>
      <source name="Customer.Application" switchValue="Information">
        <listeners>
          <add name="traceListener" type="System.Diagnostics.XmlWriterTraceListener" initializeData="c:\users\public\trace.svclog" />
        </listeners>
      </source>
    </sources>
  </system.diagnostics>

The following statements need to be executed to get this to work, notice the exact order and sequence numbers (AddWebConfigModification is a custom method I’ve written that takes the webApp, Path, Name, Sequence and Value attributes for the web config modification):

AddWebConfigModification(webApp, "configuration", "system.diagnostics", 0,
                            @"<system.diagnostics></system.diagnostics>");
AddWebConfigModification(webApp, "configuration/system.diagnostics", "trace[@autoflush='true']", 1,
                     @"<trace autoflush='true' />");
AddWebConfigModification(webApp, "configuration/system.diagnostics", "sources", 2,
                    @"<sources></sources>");
AddWebConfigModification(webApp, "configuration/system.diagnostics/sources", "source[@name='Customer.Application'][@switchValue='Information']", 3,
                    @"<source name='Customer.Application' switchValue='Information'></source>");
AddWebConfigModification(webApp, "configuration/system.diagnostics/sources/source[@name='Customer.Application'][@switchValue='Information']", "listeners", 4,
                    @"<listeners></listeners>");
AddWebConfigModification(webApp, "configuration/system.diagnostics/sources/source[@name='Customer.Application'][@switchValue='Information']/listeners",
                    @"add[@name='traceListener'][@type='System.Diagnostics.XmlWriterTraceListener'][@initializeData='c:\users\public\trace.svclog']", 5,
                    @"<add name='traceListener' type='System.Diagnostics.XmlWriterTraceListener' initializeData='c:\users\public\trace.svclog' />");

0. Ensures the system.diagnostics node is available
1. Adds the trace tag.
2. Adds the sources tags (note the same Path tag).
3. Adds one source tag under sources.
4. Adds the listeners tags, note the XPath on the Path attribute here!
5. Adds one add tag with the rest of the configuration.

I hope this explains in more detail and with more background than typically found on the net. Feel free to ask questions in the comments.
I got lots of help from Reza Alirezaei’s post.

Good luck, you will need it 🙂

Advertisements

4 thoughts on “Finally: THE way to add web.config modifications to SharePoint

  1. Good article thank you, but where is Feature’s explanation? What kind of scope a Feature must have? We know that according to MSDN “Code that calls ApplyWebConfigModifications works only if it runs in the user context of an administrator on the front-end web server.”. And what problems and features does a deploying can have?

  2. Hi Denis,

    Thank you for your message. I would expect this code to either run as a console app or service on the front-end directly or in a feature indeed. I’m guessing site or site collection scoped features could work as well, as long as the user activating the feature is a web app admin as well. So I would go for a web application feature, which is exactly where you’d expect this behavior.
    Admitted though, it’s been a while since I’ve done on-premise stuff like this, in the light of the advancement of O365.

  3. I am fairly new to programming for SP. To my opinion SP is the subject where you should to know more but not understand more. Thus every detail has meaning. I try to deploy Feature with the WebApplication scope, but I can’t do it. Feature is empty but has event receivers. And when I try to activate this from the Central Administration I have an error: “The object does not reference on instance” – Unfortunately I can’t give the exact message becouse I see it in my native language.

    I don’t know all necessary Feature’s settings which are used for such purposes. Can you help me in some way?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s