Monday, October 3, 2011

Editing a web.config using PowerShell–changing a value

It has been some time since I have been working with XML using PowerShell.  I needed to refresh my brain and work through some issues.

In bunches of searching I ran across only one article that really tries to explain what is going on: http://www.codeproject.com/KB/powershell/powershell_xml.aspx

And coupling this understanding with Tobias Weltner’s chapter on XML starts to put the pieces together:  http://powershell.com/cs/blogs/ebook/archive/2009/03/30/chapter-14-xml.aspx

And, there are clues here: http://devcentral.f5.com/weblogs/Joe/archive/2009/01/27/powershell-abcs---x-is-for-xml.aspx and here: http://blogs.technet.com/b/heyscriptingguy/archive/2010/07/29/how-to-add-data-to-an-xml-file-using-windows-powershell.aspx

I finally sat down with a senior developer, who has always been very helpful, and he finally helped me wrap my brain around manipulating XML using PowerShell.  A bit of shared screen time and he could quickly recognize what PowerShell was doing to the XML document in handling each section.

This has prompted me to attempt to capture this understanding before I get pulled to a new project and I forget it all.

This is part 1 of a series of handling XML in PowerShell.  I decided to tackle something really simple.  Editing a value. 

The ‘partial’ XML kind of way.  I call this partial because I am only using XML handling to find the collection, not to modify it.  But this is a quick trick that I have used quite a bit to modify settings and configuration files.

This is an Azure related example, and in Azure there are no hard paths, so we keep things relative.

# Discover the path of the web.config file

$path = (( Get-Website | where {$_.Name -match ($role.Id)} ).PhysicalPath + "\Mine\Site\web.config")
$file = get-item -path $path

# Get the content of the config file and cast it to XML to easily find and modify settings.

$xml = [xml](get-content $file)

I want to edit the following line in the file:  <routingPolicy alternateFoo="off" />  I want to change the value to “on”

$routePolicy = $xml.GetElementsByTagName("routingPolicy")

This returns an object, $routePolicy.  And looking at that object I see no methods, but it does have a count property.  I actually got a collection back (or an array), not some XML element.

One way to modify this is to go along with this being an array and simply step through it taking advantage of the  dot tag type of XML browsing.

foreach ($e in $routePolicy) {
    if ($e.alternateFoo -eq "off" ) {
    $e.alternateFoo = "on"
    }
    $e
}

That is what I consider the really simple way.  It is quick and dirty and gets the job done.

Now, what I just did is not very “object oriented” which is the power of PowerShell.  So, how do I get in there and change this setting.  What is it really.

The way this setting is defined is that alternateFoo is an attribute of  <routingPolicy />.  It is not a node or an element within it.

Here is where things get really funky.  The following will modify that attribute without having to do the loop.  The important part is that each step of the way we are selecting each node as an individual object and therefore its own XML document within the larger XML document.  The difference here is that I have to know the path.  “routingPolicy” is a node under “system.web”, which is itself a node of the larger “configuration”. 

$xml.configuration.Item("system.web").Item("routingPolicy").SetAttribute("alternateFoo", "on")

Easier to code, but more difficult to understand what is really happening.  It is this stuff that is really not well written out there.

Oh, don’t forget to save the file back to the file system.

$xml.Save($file.FullName)

1 comment:

Anonymous said...

you could also do:
$policyNode = $xml.SelectSingleNode("/configuration/system.web/routingPolicy")
$policyNode.SetAttribute("alternateFoo", "on")