by Eurisko at 2013-02-20 13:58:49
After picking up PowerShell 3, writing over a hundred functions for several different module projects, I keep running back into one of the same challenges for me, XML.by poshoholic at 2013-02-22 05:59:16
I’m trying to use PowerShell to make changes to a web.config file, and I’m having issues referencing specific nodes/values, and updating just those items. I have found quite a few documents that discuss edits to different web.config files, but none seem to address handling my situation. Any help is greatly appreciated.
Here is my config file with a few values tweaked:<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="InstanceConfiguration" type="System.Web.Security.Stuff, System.Web.Security.Stuff, Version=1.0.0.0, Culture=neutral, PublicKeyToken=123123231, Custom=null"/>
<sectionGroup name="system.web">
<section name="stuffgoeshere"
type="System.Web.Security.Stuff, System.Web.Security.Stuff, Version=1.0.0.0, Culture=neutral, PublicKeyToken=123123231, Custom=null" />
</sectionGroup>
<sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=1453452345425" >
<section name="Stuff.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561dd4e089" requirePermission="false" />
</sectionGroup>
</configSections>
<InstanceConfigurationData>
<InstanceConfiguration>
<add Realm="test1"
Name="12354"
SignInType="MANL"
EventLogSourceName="Logon"
DebugMode="True"
DebugModeUser=""
LogEvents="True"
UseUserNameAsEmailAddress="False"
UseFullIdentity="True"
InstanceUrl="https://mytest.com/default.aspx"
RedirectUrl="https://mytest.com/"
SamlAuthUrl=""
CookieDomain=".mytest.com"
ApiUserName="apiusertest"
ApiKey="j9da2"
SamlCertificateName="" />
<add Realm="test2"
Name="65854"
SignInType="MANL"
EventLogSourceName="Logon"
DebugMode="True"
DebugModeUser=""
LogEvents="True"
UseUserNameAsEmailAddress="False"
UseFullIdentity="True"
InstanceUrl="https://mytest.com/default.aspx"
RedirectUrl="https://mytest.com/"
SamlAuthUrl=""
CookieDomain=".mytest.com"
ApiUserName="apiusertest2"
ApiKey="key25"
SamlCertificateName="" />
<add Realm="test3"
Name="78564"
SignInType="CUS3"
EventLogSourceName="Logon"
DebugMode="True"
DebugModeUser=""
LogEvents="True"
UseUserNameAsEmailAddress="False"
UseFullIdentity="False"
InstanceUrl="https://www.mytest.com/default.aspx"
RedirectUrl="https://www.mytest.com/"
SamlAuthUrl=""
CookieDomain=".mytest.com"
ApiUserName="apiusertest3"
ApiKey="key7bad"
SamlCertificateName="" />
<add Realm="test4"
Name="68426"
SignInType="MANL"
EventLogSourceName="Logon"
DebugMode="True"
DebugModeUser=""
LogEvents="True"
UseUserNameAsEmailAddress="False"
UseFullIdentity="True"
InstanceUrl="https://mytest.com/default.aspx"
RedirectUrl="https://mytest.com/"
SamlAuthUrl=""
CookieDomain=".mytest.com"
ApiUserName="apiusertest4"
ApiKey="nokeyneeded"
SamlCertificateName="" />
<add Realm="test5"
Name="12354"
SignInType="MANL"
HandOffCookieTimeoutMinutes="3"
EventLogSourceName="Logon"
DebugMode="True"
DebugModeUser=""
LogEvents="True"
UseUserNameAsEmailAddress="False"
UseFullIdentity="True"
InstanceUrl="https://www.mytest.com/default.aspx"
RedirectUrl="https://www.mytest.com/"
SamlAuthUrl=""
CookieDomain=".mytest.com"
ApiUserName="apiusertest5"
ApiKey="sp4ubRupe"
SamlCertificateName="" />
</InstanceConfiguration>
</InstanceConfigurationData>
<system.web>
<sessionState mode="Off" />
<compilation defaultLanguage="c#" debug="false">
<assemblies>
</assemblies>
</compilation>
<customErrors mode="On" defaultRedirect="/errors/error.aspx" >
</customErrors>
<authentication mode="None" />
<httpModules>
<add
name="My.Module"
type="Stuff.deleted" />
</httpModules>
</system.web>
<system.diagnostics>
<switches>
</switches>
<trace autoflush="true" indentsize="3">
<listeners>
</listeners>
</trace>
</system.diagnostics>
<appSettings>
<add key="Timeout" value="-10"/>
<add key="DebugOverRide" value=""/>
<add key="OverRideRule7" value=""/>
<add key="KilTime" value="7" />
</appSettings>
</configuration>
The key items I’m trying to work with is the Realm, Name & ApiKey. I need to find the Realm (always unique) , and be able to change either/or both the Name (not always unique) & ApiKey values. I have tried referencing it several different ways, but I can’t seem to get where I can just change those in the secific section without hosing the rest of it.
Any suggestions are greatly apprectiated.
Hi there,by Eurisko at 2013-02-22 18:00:37
You ask some great questions. Let me first respond by saying that I really don’t like the XML support in PowerShell. I’ve been using it for a long time, and every time I come back to it I am reminded that I don’t like how PowerShell supports XML. I don’t like it because:
a) XML is case-sensitive, PowerShell is not, and that’s a detail that personally I don’t like having to deal with in my scripts; and
b) PowerShell seems to try to make XML work easy, but either it’s not intuitive enough or I’m just not getting it. Judging from the number of questions that come up about PowerShell and XML, I tend to think it’s in the not-very-intuitive category.
That said, I can help you with your XML problem here. Recently any time I have to work with XML I opt to use XPath and the built-in support for XPath rather than PowerShell XML-related cmdlets or the XML object properties. This seems to fit the mold that XML comes in and makes for easier work once you understand XPath well enough.
Here’s how I solved your particular problem:
Step 1: Save your shared XML document (thank you for sharing that by the way, that really made helping you out much easier) as C:\app.config.
Step 2: Invoke the following PowerShell commands to modify the Name and ApiKey values for one Realm entry:# Read in the contents of the XML file
$xml = [xml]${C:\app.config}
# Look at the current values for the Realm called "test4" by using an XPath query
# The // prefix before add identifies that we want to find the add element anywhere in the document
# The @Realm="test4" index identifies that we specifically want the add element that has a Realm value of "test4"
$xml.SelectSingleNode('//add[@Realm="test4"]')
# Update the Name value of the Realm called "test4" to "98765"
# Note that all values you assign to XML must be strings; PowerShell won't auto-convert numbers to strings for you here
$xml.SelectSingleNode('//add[@Realm="test4"]').Name = '98765'
# Update the ApiKey value of the Realm called "test4" to 'keymissing'
$xml.SelectSingleNode('//add[@Realm="test4"]').ApiKey = 'keymissing'
# Verify our updates are set properly in memory
$xml.SelectSingleNode('//add[@Realm="test4"]')
# Now save our document to disk
$xml.Save('C]
That’s it. After following these steps, the document is updated with the changed values that you requested.
Note that instead of making multiple calls to $xml.SelectSingleNode, you could make that call once, store it in a variable, then change the properties of that variable and that would actually change the xml document in memory. Then you could save the document to disk afterwards. That would simplify the logic by keeping your XPath search string on one line. For more information about XPath syntax, see http://www.w3schools.com/xpath/xpath_syntax.asp.
Note that the format for the document changes to the default format used by the Save method. I know there are ways to define how the XML should be formatted when it is saved, but that is for another day.
The more times I come back to PowerShell and use XPath to do the work I need to do, the more I enjoy that method of managing XML in PowerShell.
You sir, are my hero! Didn’t realize XPath was so flexible. That’s MUCH easier than the other attempts I was making with this. Thanks again!!!by poshoholic at 2013-02-22 19:51:39
I’m glad I was able to help out.