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

    Published by on January 4th, 2013 11:59 am under SharePoint

    No Comments

    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();
                    }
                }
            }
        }
    }