Scripting XML Configuration Files

Those .NET XML Configuration files, you know the ones - web.config - app.config - thingy.exe.config - well i've never been happy with them.

First, XML is not very forgiving, miss a bracket or slash and your application does terrible things.

Second, when your application is in production, administrators have to use the highly acclaimed XML editor (notepad.exe)...

Recently I needed a way of changing some configuration parameters to automate some deployment, seemed like a job for PowerShell.

First I had a play with the fantastic XML support in Windows PowerShell. Try this:

 $xml = [Xml] (type c:\demo\web.config)
$xml.configuration.appSettings.add

You should see a list of app setting keys from your config file. Neat eh?

Too much typing, next task, turn it into a function:

 function Get-ConfigAppSetting
([string]$PathToConfig)
{
    $x = [xml] (type $PathToConfig)
    $x.configuration.appSettings.add
}

Which always works great until you miss of the parameter, or specify a file that does not exist. The following seemed to be a little more robust:

 function Get-ConfigAppSetting
([string]$PathToConfig=$(throw 'Configuration file is required'))
{
    if (Test-Path $PathToConfig)
    {
        $x = [xml] (type $PathToConfig)
        $x.configuration.appSettings.add
    }
    else
    {
        throw "Configuration File $PathToConfig Not Found"
    }
}

Now the parameter has a "default" value, so when you forget to supply one, it assigns the results of the expression to the right of the assignment operator. Since the expression throws an exception - the script will not execute.

In the funciton, we test for the existance of the file using the Test-Path CmdLet. If it exists we load it and display the values at add.

Of course to do the script correcly, we should also test to make sure the file did load into the variable x, and also that the configuration.appSettings.add works. I'll leave that up to you to figure out.

Next, what about setting the values. Enter the Set-ConfigAppSetting

 function Set-ConfigAppSetting
([string]$PathToConfig=$(throw 'Configuration file is required'),
         [string]$Key = $(throw 'No Key Specified'), 
         [string]$Value = $(throw 'No Value Specified'))
{
    if (Test-Path $PathToConfig)
    {
        $x = [xml] (type $PathToConfig)
        $node = $x.configuration.SelectSingleNode("appSettings/add[@key='$Key']")
        $node.value = $Value
        $x.Save($PathToConfig)
    }
} 

Note the 3 parameters are all required. We located the node we want to change using the XPath expression, change the Value, then save the configuration file. Now one thing to note about PowerShell is that if you change a system value, you should provide support for -whatif and -confirm switches. From the PowerShell blog, i updated the function to the following.

 function Set-ConfigAppSetting
    ([string]$PathToConfig=$(throw 'Configuration file is required'),
         [string]$Key = $(throw 'No Key Specified'), 
         [string]$Value = $(throw 'No Value Specified'),
         [Switch]$Verbose,
         [Switch]$Confirm,
         [Switch]$Whatif)
{
    $AllAnswer = $null
    if (Test-Path $PathToConfig)
    {
        if (Should-Process Set-ConfigAppSetting $Key ([REF]$AllAnswer) "`n***Are you crazy?" -Verbose:$Verbose -Confirm:$Confirm -Whatif:$Whatif)
        {
            $x = [xml] (type $PathToConfig)
            $node = $x.configuration.SelectSingleNode("appSettings/add[@key='$Key']")
            $node.value = $Value
            $x.Save($PathToConfig)
        }
    }
} 

You will also need the Should-Process function from the PowerShell Blog.

Now we can do things like

 set-configappsetting $f orderingServiceQueueName ".\private$\dinnernoworders" -confirm

Which is incredibly powerfull.

Only 2 more things to go

New-ConfigAppSetting and Remove-ConfigAppSetting

for New-ConfigAppSetting, we need to find the configuration.appSetting.Add node, then add a new element. The function should support the same parameters as Set-ConfigAppSetting and the Should-Process function, so we'll omit that part for clarity. The full scripts are attached to this post.

 $x = [xml] (type $PathToConfig)
    
$el = $x.CreateElement("add")
$kat = $x.CreateAttribute("key")
$kat.psbase.value = $Key
$vat = $x.CreateAttribute("value")
$vat.psbase.value = $Value
    
$el.SetAttributeNode($kat)
$el.SetAttributeNode($vat)
    
$x.configuration.appSettings.Appendchild($el)
$x.Save($PathToConfig)

Final thing to implement is the Remove-ConfigAppSetting which is a slight variation on the Set-ConfigAppSetting

 $x = [xml] (type $PathToConfig)
$node = $x.configuration.SelectSingleNode("appSettings/add[@key='$Key']")
$x.configuration.appSettings.RemoveChild($node)
$x.Save($PathToConfig)

So, now we have the ability to script the discovery, update, addition and removal of appSetting elements from our XML configuration files. Pretty neat. Hopefully you can see how this can be applied to ConnectionStrings and all those other configuration sections.

Now we can script changes to the configuration files, whatever next...

THIS POSTING IS PROVIDED "AS IS" WITH NO WARRANTIES, AND CONFERS NO RIGHTS 

ConfigFiles.ps1