Editing elements with periods in the name in PowerShell

Recently, a coworker asked me about a problem he was having with a PowerShell script that edited an app.config file. It was a simple enough fix for an experienced PowerShell programmer, but worth sharing the solution for those not as experienced.

For this article, I’ll use a very small example web.config that demonstrates the problem.

<?xml version="1.0" encoding="utf-8" ?>
    <add key="verbose" value="true"/>
    <compilation debug="true" />

The script my coworker was using worked something like this:

$scriptPath = $MyInvocation.MyCommand.Path
$dir = Split-Path $scriptPath

$configFile = (Get-Content "$($dir)web.config")
'Before: ' + $configFile.configuration.appSettings.OuterXml
$configFile.configuration.appSettings.add.value = 'false'
'After:  ' + $configFile.configuration.appSettings.OuterXml

Which of course prints out the following output:

Before: <appSettings><add key="verbose" value="true" /></appSettings>
After:  <appSettings><add key="verbose" value="false" /></appSettings>

The actual script saved the configuration file and didn’t print anything to the screen, but that’s irrelevant to the problem. The problem was he needed to edit the element inside the element. He tried this first:

$configFile = (Get-Content "$($dir)web.config")
'Before: ' + $configFile.configuration.system.web.OuterXml
$configFile.configuration.system.web.compilation.debug = 'false'
'After:  ' + $configFile.configuration.system.web.OuterXml

Which got him the following:

Property 'debug' cannot be found on this object; make sure it exists and is settable.
At C:UserszippyDocumentsdeletemeposh.configeditConfig.ps1:3 char:50
+ $configFile.configuration.system.web.compilation. <<<< debug = 'false'
    + CategoryInfo          : InvalidOperation: (debug:String) [], RuntimeException
    + FullyQualifiedErrorId : PropertyNotFound

The problem was the dot in system.web. PowerShell uses a period to separate an object from its members. However, all is not lost, because there is an alternate notation.

'Before: ' + $configFile.configuration['system.web'].OuterXml
$configFile.configuration['system.web'].compilation.debug = 'false'
'After:  ' + $configFile.configuration['system.web'].OuterXml

As you can see, we can reach into an xml element with indexer notation. Please note that you will get an error if you put a period before the opening square bracket, but a period is required after the closing square bracket. If you would like some more in depth knowledge of how indexers work, see this MSDN page. While that article is written from a C# perspective, the concepts can be applies to PowerShell as well.