Updating the Web.config in SharePoint Applications (The Right Way)

Some time ago, I needed to create and register a custom HttpModule in a SharePoint Web Application. As you probably, know registering the module implies adding an entry in the <modules> section of the Web.config. Reviewing SharePoint Best Practices, I’ve learned that the recommended way of doing this in a SharePoint Application is through the use of the SPWebconfigModification class. If used correctly, this class allows you to update the Web.config automatically while deploying the solution, without needing to modify the Web.config manually in each Web Front End (WFE).

To have SharePoint perform the update during deployment, you have to use the SpWebconfigModification class within a Feature Receiver of your SharePoint solution. The Feature Receiver allows you to write code to be executed when your WSP (SharePoint Solution Package) is installed/uninstalled.

Some comments from the Community show that there are some known issues with the SpWebconfigModifications class under certain conditions. However, don’t get discouraged! So far, I’ve tested it for registering a new HttpModule and it has worked successfully, reducing deployment times considerably.

Some Links

I won’t get into the details of the SpWebconfigModifications class, but for a deeper insight, here are some links I’ve collected:

Some Pictures

As a quick reference, in the following picture you can see the SPWebConfigConfiguration Class within the SharePoint FeatureReceiver:

event.receiver

The SpWebconfigModification is applied in the FeatureActivated event of the Feature Receiver.

event.receiver.2

The following pictures show the FeatureReceiver within the SharePoint solution and how to add it:

event.receiver.3

image

The Code

Here you can find the code of the FeatureReceiver using the SPWebconfigModification class to register an HttpModule. Some considerations:

  1. As with any other SharePoint assembly the HttpModule needs to be signed. I somehow struggled ‘a bit’ on how to get the HttpModule assembly strong name. I ended using PowerShell with this line:

    [System.Reflection.AssemblyName]::GetAssemblyName({assemblyPath}).FullName

  2. Make sure that you are modifying the Web.config of an SPSite and not an SPWebApplication. In that case, replace this line:

    SPSite site = properties.Feature.Parent as SPSite;

    with this one:

    SPWebApplication webApp = properties.Feature.Parent as SPWebApplication;

  3. In other posts I’ve seen the following line:

    SPFarm
    .Local.Services.GetValue<SPWebService>(webApp.Parent.Id).ApplyWebConfigModifications();

    Instead of this one:

    site.WebApplication.Farm.Services.GetValue<SPWebService>().ApplyWebConfigModifications();

    I’m still not sure what the difference is, but the only one that worked for me is the last one (site.WebApplication…) . I suspect that it depends on whether or not you are running SharePoint within a farm.

Here comes the code…

namespace GSK.RD.EDX.AccessImprovement.Features.TrainingAppFeature
{
    using System;
    using System.Collections.ObjectModel;
    using System.Runtime.InteropServices;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Administration;

    [Guid("3ed0ded5-8203-4fc1-a372-e021af403237")]
    public class TrainingAppFeatureEventReceiver : SPFeatureReceiver
    {
        private const string cWebConfigModificationOwner = "TrainingAppFeature.AuthenticationRedirectHttpModule";

        SPWebConfigModification spWebConfigModification = new SPWebConfigModification()
        {
            // The owner of the web.config modification, useful for removing a 
            // group of modifications
            Owner = cWebConfigModificationOwner,
            // Make sure that the name is a unique XPath selector for the element 
            // we are adding. This name is used for removing the element
            Name = "add[@name="AuthenticationRedirectHttpModule"]",
            // We are going to add a new XML node to web.config
            Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode,
            // The XPath to the location of the parent node in web.config
            Path = "configuration/system.webServer/modules",
            // Sequence is important if there are multiple equal nodes that 
            // can't be identified with an XPath expression
            Sequence = 0,
            // The XML to insert as child node, make sure that used names match the Name selector
            Value = "<add name="AuthenticationRedirectHttpModule" type="RD.EDX.AccessImprovement.AuthenticationRedirectModule.AuthenticationRedirectHttpModule, GSK.RD.EDX.AccessImprovement.AuthenticationRedirectModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=526edf2f87bc2a9e" />"
        };

        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {            
            SPSite site = properties.Feature.Parent as SPSite;            
            if (site != null)
            {                
                site.WebApplication.WebConfigModifications.Add(spWebConfigModification);

                // Commit modification additions to the specified web application
                site.WebApplication.Update();

                // Push modifications through the farm
                site.WebApplication.Farm.Services.GetValue<SPWebService>().ApplyWebConfigModifications();               
            }
        }

        public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
        {
            SPSite site = properties.Feature.Parent as SPSite;
            if (site != null)
            {
                Collection<SPWebConfigModification> modificationCollection = site.WebApplication.WebConfigModifications;
                Collection<SPWebConfigModification> removeCollection = new Collection<SPWebConfigModification>();

                int count = modificationCollection.Count;
                for (int i = 0; i < count; i++)
                {
                    SPWebConfigModification modification = modificationCollection[i];
                    if (modification.Owner == cWebConfigModificationOwner)
                    {
                        // collect modifications to delete
                        removeCollection.Add(modification);
                    }
                }

                // now delete the modifications from the web application
                if (removeCollection.Count > 0)
                {
                    foreach (SPWebConfigModification modificationItem in removeCollection)
                    {
                        site.WebApplication.WebConfigModifications.Remove(modificationItem);
                    }

                    // Commit modification removals to the specified web application
                    site.WebApplication.Update();

                    // Push modifications through the farm
                    site.WebApplication.Farm.Services.GetValue<SPWebService>().ApplyWebConfigModifications();
                }
            }
        }
    }
}



Leave a Reply