Update specific line in xml file

by Rodge at 2012-08-21 03:12:35

Hi all,

I’ve always managed to find an answer for my (generally simple) powershell issues within the old forums, now I’ve got a query that has me stumped :slight_smile:

I have an xml config file which sits on a large number of servers. It’s similar on each server, but not identical.
An example of the file is:


<Targets AGENT_TOKEN="1234567890">
<Target TYPE="db_emd" NAME="db1234:1234" />
<Target TYPE="host" NAME="server1234" />
<Target TYPE="listener" NAME="listener1234">
<Property NAME="ldir" VALUE="c:\listenerloc" />
<Property NAME="LName" VALUE="LISTENER1234" />
<Property NAME="Machine" VALUE="server1234" />
<Property NAME="Home" VALUE="c:\listenerloc" />
<Property NAME="Port" VALUE="1234" />
</Target>
<Target TYPE="database" NAME="db1234">
<Property NAME="dbHome" VALUE="c:\db1234" />
<Property NAME="User" VALUE="dbuser" />
<Property NAME="MachineName" VALUE="server1234" />
<Property NAME="Port" VALUE="1234" />
<Property NAME="dbid" VALUE="dbid1234" />
<Property NAME="dbname" VALUE="db1234" />
<Property NAME="password" VALUE="encpassword" encrypted="TRUE" /> – Line I’m interested in
<Property NAME="Role" VALUE="NORMAL" />
</Target>
</Targets>

The indicated line is the one I’m interested in.
Initially, I’ll put a password in this field and set encrypted=FALSE
When I start the app it reads this file, sees encryption is FALSE, encrypts the password, overwrites the VALUE field with the encrypted version and updated ENCRYPTED to TRUE.
This always works fine.

My issue is that some weird process is changing the encrypted value from TRUE to FALSE randomly after the password is encrypted and the app started.
I’ve raised this with the software vendor and we’re working on trying to figure out what is doing this.
In the meantime, every time the app restarts - it reads this file again, sees the encrypted value is FALSE and attempts to encrypt the already encrypted password.
Thus the app can’t login correctly, stops working and requires a manual reset of the password.


My workaround: I have the following block of code which runs in powershell and resets the password to the correct value.
When I restart the app, it encrypts this password and stores it correctly. For a while anyway…

$file = c:\config.xml
$newpassword = ‘abcdefg’
$xml = [xml](get-content $file)
$password = $xml.targets.target[3].property[6]
$password.value = $newpassword
$xml.save($file)


After all that - what’s the question you may ask?

In 90%+ of cases, the password value is correctly held in the 4th Target block and the 7th Property line - so my block of code alters it correctly.
In the other cases, the password line is strangely (and also randomly) on a different line.
And of course, my code in this case will alter the wrong property - resulting in me having to manually update this file on a number of servers…


So my issue is that I can’t figure out how to get Powershell to dynamically find and update my password line (password will be the same in all cases).
I want it to look for the property line with name = "password" and allow me to replace what’s contained in the VALUE.


I’m SURE this is probably pretty simple and I’ve probably been pretty close with the mountains of non-working efforts I have.
I’ve done enough manual changes with this problem, I’d love to be able to completely automate it with a single script!
So any help with this would be much appreciated!
by DonJ at 2012-08-21 06:23:24
Actually, no, it’s not all that simple. You COULD use a ForEach loop to enumerate through the properties until you kind the one you wanted to change. The "proper" way would probably be to issue a XPath query to get just the element with password in it. Sadly - I suck at XPath. Let me see if I can rustle up some help.
by willsteele at 2012-08-21 07:00:43
Here’s an XPath expression you should be able to use to find that single node:
$xml.SelectSingleNode("//Targets/Target/Property[@NAME=‘password’]")

Use this as shown below and it should accomplish the same thing regardless or element ordering:
$file = c:\config.xml
$newpassword = ‘abcdefg’
$xml = [xml](get-content $file)
$password = $xml.SelectSingleNode("//Targets/Target/Property[@NAME=‘password’]")
$password.value = $newpassword
$xml.save($file)


Please test on a sample machine before using in production. Alternately, you could use this approach to test directly within a variable.
$xml = [xml] @"
<Targets AGENT_TOKEN="1234567890">
<Target TYPE="db_emd" NAME="db1234:1234" />
<Target TYPE="host" NAME="server1234" />
<Target TYPE="listener" NAME="listener1234">
<Property NAME="ldir" VALUE="c:\listenerloc" />
<Property NAME="LName" VALUE="LISTENER1234" />
<Property NAME="Machine" VALUE="server1234" />
<Property NAME="Home" VALUE="c:\listenerloc" />
<Property NAME="Port" VALUE="1234" />
</Target>
<Target TYPE="database" NAME="db1234">
<Property NAME="dbHome" VALUE="c:\db1234" />
<Property NAME="User" VALUE="dbuser" />
<Property NAME="MachineName" VALUE="server1234" />
<Property NAME="Port" VALUE="1234" />
<Property NAME="dbid" VALUE="dbid1234" />
<Property NAME="dbname" VALUE="db1234" />
<Property NAME="password" VALUE="encpassword" encrypted="TRUE" /> – Line I’m interested in
<Property NAME="Role" VALUE="NORMAL" />
</Target>
</Targets>
"@
by Rodge at 2012-08-21 08:37:51
[quote="willsteele"]Please test on a sample machine before using in production.[/quote]
I think we’ve all made the mistake of running a test on production - but you only do that kind of thing once… :slight_smile:

Will, I’m incredibly happy to say that your solution works on my test system through a couple of test cases.
A simple one line change that I wasn’t even close to getting on my own.
Thanks a lot for the help :slight_smile:

DonJ, going the foreach route was what I had been attempting as it seemed to me the way to do it too.
Unfortunately, I have piles of failed efforts where it just didn’t work (for me anyway).
Thanks for the suggestion though!
by willsteele at 2012-08-21 08:55:35
Glad it helped. I’ve been immersed in XML lately, so, it just happened to be fresh on my mind. Hopefully I can share more of the XML information I have gathered up here shortly.