harleygreen.com
.net development

Harley Green

Sharepoint web.config updates via PowerShell

OK - I know I'm slow to be blogging about SPWebConfigModification, but figured that I'd add a little value with a healthy dose of powershell.

There are a number of approaches to using SPWebConfigModification to update the web.config (see here, here and here), but all of these essentially require deploying / retracting a feature to modify / retract the config modifications. It may just be that I'm a little picky, but I really don't want to have to deploy code to make a configuration change - kinda defeats the point of configuration IMHO. Of course, I really don’t want to log onto a bunch of different servers, and make manual updates to each, as the chance of my fat-fingering something is pretty high.

Of late - whenever I'm dealing with something painful, I invariably turn to powershell and this was no exception. A quick translation of this example from Mark Wagner's blog:

 
  1. // Get an instance of my local web application   
  2. SPWebApplication webApp = new SPSite("http://localhost").WebApplication;   
  3.   
  4. // Create my new modification to set the mode attibute to "Off".  
  5. // Example: <customErrors mode="Off">   
  6. SPWebConfigModification modification = new SPWebConfigModification("mode""system.web/customErrors");  
  7. modification.Owner = "SimpleSampleUniqueOwnerValue";   
  8. modification.Sequence = 0;   
  9. modification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureAttribute;   
  10. modification.Value = "Off";   
  11.   
  12. // Add my new web.config modification.   
  13. webApp.WebConfigModifications.Add(modification);   
  14.   
  15. // Save web.config changes.   
  16. webApp.Farm.Services.GetValue<SPWebService>().ApplyWebConfigModifications();   
  17.   
  18. // Serialize the web application state and propagate changes across the farm.   
  19. webApp.Update();  

Yields:

 
  1. $site = new-object Microsoft.SharePoint.SPSite -argumentList "http://localhost"  
  2. $webApplication = $site.WebApplication  
  3. $modification = New-Object -TypeName "Microsoft.SharePoint.Administration.SPWebConfigModification" -ArgumentList "mode""system.web/customErrors"  
  4. $modification.Value = "Off"  
  5. $modification.Owner = "SimpleSampleUniqueOwnerValue"  
  6. $modification.Sequence = 0  
  7. $modification.Type = "EnsureAttribute"  
  8. $webApplication.WebConfigModifications.Add($modification)  
  9. $method = [Microsoft.Sharepoint.Administration.SPServiceCollection].GetMethod("GetValue", [Type]::EmptyTypes)  
  10. $closedMethod = $method.MakeGenericMethod([Microsoft.Sharepoint.Administration.SPWebService])  
  11. $services = $webApplication.Farm.Services  
  12. $service = $closedMethod.Invoke($services, [Type]::EmptyTypes)  
  13. $service.ApplyWebConfigModifications()  
  14. $webApplication.Update()  
  15. $site.Dispose()  

This is atypically more complex than the equivalent c# code above - almost entirely due to the generic "GetValue" method being called.

Now, the only issue outstanding is to wrap it all up in a reusable script. I started down the path of using xml to define the configuration values, basically using Ryan's xsd . This does, unfortunately, get rather verbose due to the need to define each attribute in a given node. Once again - this was beginning to feel painful and my thoughts turned again to powershell - why not define the variables using powershell itself? If the configuration is a powershell script, I can simply dot-source it, and I'm good to go.

Of course - I still need a convention to define the configuration, and eventually I settled on this:

 
  1. $mods =  
  2.     ,@{  
  3.     "name" = 'add[@key="TestConfig"]';  
  4.     "value" = '<add key="TestConfig" value="1000" />';  
  5.     "owner" = "hgreen";  
  6.     "path" = "/configuration/appSettings";  
  7.     "sequence" = "0";  
  8.     "type" = "EnsureChildNode";  
  9.     }  

$mods is a list of Dictionary objects - each of which holds the relevant values to be used in the construction of a SPWebConfigModification. Defaults are provided for owner and sequence, the rest should all be provided by the configuration script. The driving force here was to keep the configuration script purely configuration.  This defines the modifications, and nothing else.

The final script:

 
  1. param(  
  2.     $application = "$(throw 'application is a mandatory parameter.')",  
  3.     $fileName = "$(throw 'fileName is a mandatory parameter.')",  
  4.     [switch]$retract  
  5. )  
  6.   
  7. trap [Exception] {  
  8.       Write-Error $("TRAPPED: " + $_.Exception.GetType().FullName);  
  9.       Write-Error $("TRAPPED: " + $_.Exception.Message);   
  10.       if ($site -ne $null)  
  11.       { $site.Dispose() }  
  12.   
  13.    }  
  14.   
  15. function Coalesce-Args  
  16. {  
  17.     ([object[]]($args | Where-Object {$_}) + ,$null)[0]  
  18. }  
  19.   
  20. [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Sharepoint")   
  21.   
  22. $mods = $null  
  23. . (Resolve-Path $fileName)  
  24. $site = new-object Microsoft.SharePoint.SPSite -argumentList $application  
  25. $webApplication = $site.WebApplication  
  26.   
  27. #$mods should be initialized in input file  
  28. foreach($mod in $mods)  
  29. {  
  30.     $name = $mod.name  
  31.     $value = $mod.value  
  32.     $principal = [Security.Principal.WindowsIdentity]::GetCurrent()  
  33.     $owner = Coalesce-Args $mod.owner $principal.Name  
  34.     $path = $mod.path  
  35.     $sequence = Coalesce-Args $mod.sequence 0  
  36.     $type = $mod.type  
  37.       
  38.     $modification = New-Object -TypeName "Microsoft.SharePoint.Administration.SPWebConfigModification" -ArgumentList $name$path  
  39.     $modification.Value = $value  
  40.     $modification.Owner = $owner  
  41.     $modification.Sequence = $sequence  
  42.     $modification.Type = $type  
  43.       
  44.     if ($retract)  
  45.     {  
  46.         $webApplication.WebConfigModifications.Remove($modification)  
  47.     }  
  48.     else  
  49.     {  
  50.         $webApplication.WebConfigModifications.Add($modification)  
  51.     }  
  52.       
  53. }  
  54.   
  55. $method = [Microsoft.Sharepoint.Administration.SPServiceCollection].GetMethod("GetValue", [Type]::EmptyTypes)  
  56. $closedMethod = $method.MakeGenericMethod([Microsoft.Sharepoint.Administration.SPWebService])  
  57. $services = $webApplication.Farm.Services  
  58. $service = $closedMethod.Invoke($services, [Type]::EmptyTypes)  
  59. $service.ApplyWebConfigModifications()  
  60. $webApplication.Update()  
  61.   
  62. $site.Dispose()  

This is available as a download here, this includes the sample configuration script shown above.

2/7/2009 11:27:00 PM Published by Harley Green Category Sharepoint Comments 1

Comments
  • Harley,

    This is a great post.

    Thank you very much.

    n.o.

    by Nasser - 1/15/2010 2:20:31 PM
Name

Web site
Comment


Back