Thursday, December 28, 2006

Using SPWebConfigModification to modify a SharePoint web application's web.config for all servers in the farm


At some point in your career as a SharePoint developer you may find it necessary to push out a change to a SharePoint web application's web.config file for all servers in the farm. In the simplest of scenarios, it may be realistic to make the change to web.config manually, but if you are creating a packaged application for redistribution or there are many servers in the SharePoint farm, a better approach might be to make the web.config modifications programmatically. The WSS v3 API contains the SPWebConfigModification object which allows you to do just that. For example, if you have been following Daniel Larson's excellent series of articles on implementing AJAX in WSS v3, you know that AJAX requires an additional HttpHandler be added to a SharePoint web application's web.config in order to function properly. Here's the HttpHandler in question:
<add verb="GET" path="ScriptResource.axd" type="Microsoft.Web.Handlers.ScriptResourceHandler, Microsoft.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="false"/>


To push out the AJAX HttpHandler declaration programmatically, the following example console application calls SPWebConfigModification. For the purposes of illustration, I've hard coded the web application's name ("Default Web Site"), but this could have just as easily been passed in as a command line argument:
12/28/2006 - MODIFICATION: Updated code listing with bug fix and added logic to remove the web.config modifcation.

Copy Code
using System;using System.Globalization;using Microsoft.SharePoint.Administration; public class Program { static void Main(string[] args) { string webAppName = "Default Web Site"; // Add AddAjaxHandlerToWebConfig(webAppName); // Remove //RemoveAjaxHandlerFromWebConfig(webAppName); } /// <summary> /// Adds the Ajax HttpHandler to a web application's web.config for all servers in the farm /// </summary> /// <param name="webAppName"></param> private static void AddAjaxHandlerToWebConfig(string webAppName) { AddOrRemoveAjaxHandlerToWebConfig(webAppName, false); } /// <summary> /// Removes the Ajax HttpHandler from a web application's web.config for all servers in the farm /// </summary> /// <param name="webAppName"></param> private static void RemoveAjaxHandlerFromWebConfig(string webAppName) { AddOrRemoveAjaxHandlerToWebConfig(webAppName, true); } /// <summary> /// Adds or removes the Ajax HttpHandler to a web application's web.config for all servers in the farm /// </summary> /// <param name="webAppName">Name of web application</param> private static void AddOrRemoveAjaxHandlerToWebConfig(string webAppName, bool removeModification) { string assmDetails = string.Format(CultureInfo.InvariantCulture, "Microsoft.Web.Handlers.ScriptResourceHandler, Microsoft.Web.Extensions, Version={0}, Culture=neutral, PublicKeyToken={1}", new object[] { "1.0.61025.0", "31bf3856ad364e35" }); SPWebConfigModification modification = new SPWebConfigModification("add[@path='ScriptResource.axd']", "configuration/system.web/httpHandlers"); modification.Owner = "Ajax"; modification.Sequence = 0; modification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode; modification.Value = string.Format(CultureInfo.InvariantCulture, "<add verb=\"{0}\" path=\"{1}\" type=\"{2}\" validate=\"{3}\"/>", new object[] { "GET", "ScriptResource.axd", assmDetails, "false" }); SPWebApplication webApp = SPWebService.ContentService.WebApplications[webAppName]; if (webApp != null) { if(removeModification) webApp.WebConfigModifications.Remove(modification); else webApp.WebConfigModifications.Add(modification); SPFarm.Local.Services.GetValue<SPWebService>().ApplyWebConfigModifications(); } } }

Real world use cases where this functionality may prove useful include calling SPWebConfigModification from a Window's Installer package Custom Action or perhaps extending stsadm with a custom operation.



12 comments:

Daniel Larson said...

Thanks for the tip, Tony! I added similar code to the SharePoint.Ajax project on Codeplex in the Feature Reciever. Check out Change Set 15970 (or newer) over at http://www.codeplex.com/sharepointajax/SourceControl/ListDownloadableCommits.aspx.

Anonymous said...

Thanks for the tip!!

I think you need to change the following:

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

To

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

So you get the correct SPWebService instance.

Thanks
/Jonas

Anonymous said...

Great post, this is very usefull

Al said...

You can't remove web.config modifications in such way. After you apply modifications, webApp.WebConfigModifications.Count will be 0, and it doesn't matter if you call webApp.WebConfigModifications.Remove(modification). This just does nothing. So the question is - how to remove modifications after you have added them...

Daniel Larson said...

Holy smokes-- only now did I read Al's comments. This is something we should complain to MS about VERY LOUD. This is a HUGE gap in the feature set!!!!

I blogged more about this here: http://daniellarson.spaces.live.com/blog/cns!D3543C5837291E93!954.entry Hopefully someone at MS will answer us!

Tony Bierman said...

Make sure that the parameters you are passing into the SPWebConfigModification constructor are valid XPath for the XML fragment you want to modify.

Anonymous said...

We are trying to add or remove web modification on multiple site. when we add modification on one site, it automatically remove entries on another web application. Is there any specific reason why other application get affected when we call this method SPFarm.Local.Services.
(....).ApplyWebConfigModifications(

dcoolsaet said...

Hello,

Thank you for the example.
But I've got a problem.
I want to add a new node to my web.config. For example:
Configuration/system.web/Pages/(new node "Controls")
Is there any way to get this done?

Kind regards
Davy Coolsaet

Xavid said...

this is how you remove it

See this line here

modification.Owner = "Ajax";

And then look at this code

Collection < SPWebConfigModification > oCollection = webApp.WebConfigModifications;

int iStartCount = oCollection.Count;
for (int c = iStartCount - 1; c >= 0; c--)
{
SPWebConfigModification oModification = oCollection[c];
if (oModification.Owner == 'Ajax')
oCollection.Remove(oModification);
}

if (iStartCount > oCollection.Count)
{
webApp.Update();
SPFarm.Local.Services.GetValue< SPWebService >().ApplyWebConfigModifications();
}


Use the owner to find the inserted tag. works like a charm

Brad Stein said...

This is an anwer to anonymous (oringinal post copied below)

I ran into the issue as well and diccovered that it had to do with the fact that I was using the same owner name for the SPWebConfigModification in both web sites.

In order to fix it I needed to do 2 things.

1. Clear the "old" mods out by recreating them with the old owner name and using Remove rather than Add.

2. Change to using a combo of featureid & sitename as the owner name.

It did start working after than, but it still was changing both web.config files (maybe it is some sort of refresh that gets triggered by calling the update at all), just not messing them up anymore.

We decided to exclude the code from the project since we would not feel comfortable releasing it without first doing extensive testing under all possible circumstances.

Did i mention that i hate the factthat it touches other web.config files?

FYI - I also used the following to get a list of old values to compare to so I re-write existing values rather than wiping them out.

Configuration config = WebConfigurationManager.OpenWebConfiguration(site.ServerRelativeUrl);
AppSettingsSection appSettings =
(AppSettingsSection)config.GetSection(_WEBCONFIG_SECTION_APPSETTINGS);

If anyone knows a better way to avoid overwriting existing values every time this thing runs, i would love to know about it since I am still fairly new to this fucntionality.

*************************

We are trying to add or remove web modification on multiple site. when we add modification on one site, it automatically remove entries on another web application. Is there any specific reason why other application get affected when we call this method SPFarm.Local.Services.
(....).ApplyWebConfigModifications(

Anonymous said...

Hi,

Do you guys know the way to modify only the web.config from the desired site? I am running into the same problem, when I modified the web config from a site, it modifies all the web configs.

Best regards

Fahad Zia said...

I think its missing webApp.Update(); after the following code:

if(removeModification)
webApp.WebConfigModifications.Remove(modification);
else
webApp.WebConfigModifications.Add(modification);


to update the modification counter.