Activating Features in a SharePoint Farm

I have been pounding my head against a wall for the last few days trying to get a couple of features to activate in my projects new High Availability Environment (HA).  The environment consists of two MOSS Web Front Ends (WFE) named web1 and web2.  Our env. also includes web3 which runs the Central Admin site, but does not have any of the externally accessible sites (the ones that run on port 80 and 443).  This has lead to the following issues.

  1. Attempt to activate the feature fails when executed from the web app.  This issue was easily resolved when I read The Kid’s blog, basically the permissions proposed by Microsoft for the MOSS configuration does not allow this.
  2. Actual values never appear in the web.config for web1 or web2
    1. I have a theory about this issue.  In our development environment we have two WFEs, but one of them is also running Central Admin.  I believe that I need to have the web applications running on the server that is also running Central Admin in order for this to work.  As I work through this issue with our system admins I will update my findings.
    2. UPDATE: After activating the Microsoft SharePoint Web Application Services on the Central Admin server the Web Config modifications were propagated to all WFE’s in the farm.

Per the results here is my recommendation about configuring a SharePoint environment.  When you are running within a SharePoint farm ensure that the Central Admin server is also running the Web Application Service.  You do not necessarily have to allow it to be one of the Network Load Balanced WFE’s but having it running the Web Applications seems to be important to allow configuration changes through out the farm.

Since I have now gotten this to work consistently I feel comfortable with my code implementation, so here it is.  This IS how you can get a SharePoint Solution Feature Listener to actually modify the web.config file in a single server or multi-server SharePoint installation.

First we create the class that is going to be our feature listener.  To do this using Visual Studio create a new class file and add inheritance from SPFeatureReceiver.  After adding this inheritance add the appropriate "using" statement, this can be done using the Intelli-Sense menu.  Next, using the Intelli-Sense menu add the four abstract methods FeatureActivated, FeatureDeactivating, FeatureInstalled, FeatureUninstalling.  For the Installing, and Uninstalling functions we will just leave those blank. 

My personal preference is to now create a function that will return a list of SPWebConfigModifications, however I have seen other concepts on the web as to how best create the SPWebConfigModifications so it is up to you.  For this blog I am simply going to assume we have a function that returns a list of SPWebConfigModificaitons, the method you choose is up to you.

FeatureActivated

        public override void FeatureActivated(SPFeatureReceiverProperties properties)

        {

            List<SPWebConfigModification> modifications = CreateConfigModification();

            SPWebApplication webApp = null;

 

            if (properties.Feature.Parent is SPSite)

            {

                SPSite spSite = properties.Feature.Parent as SPSite;

                webApp = spSite.WebApplication;

            }

            else if (properties.Feature.Parent is SPWebApplication)

            {

                webApp = properties.Feature.Parent as SPWebApplication;

            }

 

            foreach (SPWebConfigModification mod in modifications)

            {

                webApp.WebConfigModifications.Add(mod);

            }

 

            webApp.Farm.Services.GetValue<SPWebService>().ApplyWebConfigModifications();

            webApp.Update();

        }

  1. To allow a more generic approach to the activation getting the SPWebApplication object is done so we can handle Web Application, Site, etc. feature activation scopes.
  2. Once we have created the list of SPWebConfigModifications we simply loop through it and apply them to the SPWebApplication object.
  3. Finally, call the SPWebApplication’s Farm Service to Apply the web.config modifications.  This causes the changes to setup across the farm.  ALWAYS follow this with the SPWebApplication’s update function, which essentially causes the modifications to "stick".

FeatureDeacting

        public override void FeatureDeactivating(SPFeatureReceiverProperties properties)

        {

            List<SPWebConfigModification> modifications = CreateConfigModification();

            SPWebApplication webApp = null;

 

            if (properties.Feature.Parent is SPSite)

            {

                SPSite spSite = properties.Feature.Parent as SPSite;

                webApp = spSite.WebApplication;

            }

            else if (properties.Feature.Parent is SPWebApplication)

            {

                webApp = properties.Feature.Parent as SPWebApplication;

            }

 

            foreach (SPWebConfigModification mod in modifications)

            {

                webApp.WebConfigModifications.Remove(mod);

            }

 

            webApp.Farm.Services.GetValue<SPWebService>().ApplyWebConfigModifications();

            webApp.Update();

        }

  1. To remove the modifications simply cycle through the same list of modifications we used orignally during activation.  I like this method because it is very clean and a bit more efficient.  However there is a problem with this approach!  If you do not name your modification correctly the modification will not be removed.  Therefore you may wish to cycle through the SPWebApplication’s WebConfigModification list first matching the owner name to this feature’s owner name and then cycle though that list removing the modifications as shown below

        public override void FeatureDeactivating(SPFeatureReceiverProperties properties)

        {

            List<SPWebConfigModification> modifications = new List<SPWebConfigModification>();

            SPWebApplication webApp = null;

 

            if (properties.Feature.Parent is SPSite)

            {

                SPSite spSite = properties.Feature.Parent as SPSite;

                webApp = spSite.WebApplication;

            }

            else if (properties.Feature.Parent is SPWebApplication)

            {

                webApp = properties.Feature.Parent as SPWebApplication;

            }

 

            foreach (SPWebConfigModification mod in webApp.WebConfigModifications)

            {

                if (mod.Owner.Equals(this.GetType().ToString()))

                    modifications.Add(mod);

            }

 

            foreach (SPWebConfigModification mod in modifications)

            {

                webApp.WebConfigModifications.Remove(mod);

            }

 

            webApp.Farm.Services.GetValue<SPWebService>().ApplyWebConfigModifications();

            webApp.Update();

        }

  1. I usually use the typename as the owner, since the typename’s must all be unique this ensures that all of my owners are unique.  I have also seen other developers use the Feature ID for the Owner property, that is up to you but make sure your’s is always going to be unique if you use this method.

Now you can activate and deactivate the feature for the farm.  I want to recommend adding The Kid’s Web Modification Manager page to your central admin site as well.  This will help you see if and when the modifications have been added, and will also allow you to remove ones that get orphaned.  If you are doing activation and deactivation especially in the development process, this will be a life (or sanity) saver.

Advertisements

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