automated changes in xml files

In my enterprise I use pxexec to run scripts remotely on multiple servers. I have created several batch scripts to perform actions across multiple servers.
I would like to  create a script which can run using psexec remotely across servers  and  perform bulk xml changes across multiple servers.

All the remote servers have xml files which I need to edit using an automated script.

Attached is an example of such an xml.

I want to change values like databaseName= or password= to a new value.

For a start, I would like to find a way to perform a change wherein I want to automatically change the password=efgh to a say new value password=lmno without loosing the consistency of the xml file.

I tried to do it using batch script but does not work as expected.

Any help would be much appreciated.

Best Regards
Amrith
DevSupportAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

oBdACommented:
Batch is not the right language to process XML.
And to bulk change files, I don't see the need for psexec, either. You can just connect through an admin share on the remote server and edit the file directly.
That said, you can change XML easily and, more important, without worrying about consistency, using Powershell.
But if you want further help with this, you need to provide the promised attachment (and ideally information on which platform the editing script would run, and its PS version - enter $Host in a PS console) ...
0
DevSupportAuthor Commented:
Thank You for your quick response. Sorry I missed the attachment.Please find the xml attached.
context.xml
0
DevSupportAuthor Commented:
The PS version is 3.0
0
Ultimate Tool Kit for Technology Solution Provider

Broken down into practical pointers and step-by-step instructions, the IT Service Excellence Tool Kit delivers expert advice for technology solution providers. Get your free copy now.

oBdACommented:
This should do the trick; save it Context.ps1 or whatever.
You can pass it a single file or an array of files to process with -XmlFile, or you can pipe the output of a Get-ChildItem or "Get-Content C:\Temp\fileList" to it.
It's currently in test mode and will only return the XML content (as string; change line 64 to return an xml object), but not write the changes back to the file.
To run it for real, uncomment line 62. Even after uncommenting that, you'll still be able to call it with -WhatIf to run a test without actually writing anything.
[CmdletBinding(SupportsShouldProcess=$True)]
Param(
	[Parameter(ValueFromPipeline=$True, Position=0)]
	[string[]]$XmlFile,
	[string]$DataBase,
	[string]$Username,
	[string]$Password
)
Begin {
	If ([string]::IsNullOrEmpty($DataBase) -And [string]::IsNullOrEmpty($Username) -And [string]::IsNullOrEmpty($Password)) {
		Throw "Argument(s) missing: Found no property to set in the command line!"
	}
	$PathList = @()
	$XPath = "Context/Resource"
}
Process {
	ForEach ($Path In $XmlFile) {
		If (-Not (Test-Path -Path $Path -PathType Leaf)) {
			"Document not found: '$($Path)'!" | Write-Error
		} Else {
			$PathList += $Path
		}
	}
}
End {
	If ($PathList.Count -eq 0) {
		Throw "Argument missing: No file(s) to process specified!"
	}
	ForEach ($Path In $PathList) {
		"Processing '$($Path)' ..." | Write-Host -ForegroundColor White
		Try {
			[xml]$xml = Get-Content -Path $Path -Encoding UTF8
			Try {
				$NodeResource = $xml.SelectSingleNode($XPath)
				If ($NodeResource) {
					$SaveChanges = $True
					If (-Not [string]::IsNullOrEmpty($DataBase)) {
						$RegExUrl = "\A(?<Head>.*;databaseName=)(?<databaseName>[^;]*)(?<Tail>;.*)\Z"
						$Url = $NodeResource.GetAttribute("url")
						If ($Url -match $RegExUrl) {
							$NewURL = $Matches["Head"] + $DataBase + $Matches["Tail"]
							"`t- Replacing database '$($Matches["databaseName"])' with '$($DataBase)'." | Write-Host -ForegroundColor White
							"New URL: '$($NewURL)'." | Write-Verbose
							$NodeResource.SetAttribute("url", $NewURL)
						} Else {
							"Unable to parse the database name from the URL '$($Url)'!" | Write-Error
							$SaveChanges = $False
						}
					}
					If (-Not [string]::IsNullOrEmpty($Username)) {
						"`t- Replacing username '$($NodeResource.GetAttribute("username"))' with '$($Username)'." | Write-Host -ForegroundColor White
						$NodeResource.SetAttribute("username", $Username)
					}
					If (-Not [string]::IsNullOrEmpty($Password)) {
						"`t- Replacing password '$($NodeResource.GetAttribute("password"))' with '$($Password)'." | Write-Host -ForegroundColor White
						$NodeResource.SetAttribute("password", $Password)
					}
					If ($SaveChanges) {
						If ($WhatIfPreference) {
							"What if: Performing operation 'save' on Target '$($Path)'." | Write-Host
						} Else {
#							$xml.Save($Path)
						}
						$xml.InnerXml | Write-Output
						"... OK." | Write-Host -ForegroundColor Green
					}
				} Else {
					"Unexpected XML format; node '$($XPath)' not found!" | Write-Error
				}
			} Catch {
				$_ | Write-Error
			}
		} Catch {
			"Not a valid XML file: '$($Path)'!" | Write-Error
		}
	}
}

Open in new window

0
DevSupportAuthor Commented:
Hello Sir,

Thank You so much for building this script. Appreciate your quick response. I tried executing by passing a single file context.xml in this way,  $XMLFile = Get-Content "C:\scripts\context.html"
But it doesn't seem to recognise it and gives the error "Found no property to set in the command line!".

Sorry I am a newbie to PS, please let me know which line should I add it.

Thank You!
Best Regards
Amrith
0
oBdACommented:
$XmlFile expects a file path or a list of file paths (which you probably won't find in an html file), and then to actually do anything, you need to pass it at least one of the $DataBase, $UserName, or $Password arguments it should set.
Something like this, where context.xml is the file you posted:
C:\scripts\context.ps1 -XmlFile 'C:\scripts\context.xml' -Username 'ijkl' -Password 'mnop'

Open in new window

Or with a UNC:
C:\scripts\context.ps1 -XmlFile '\\Server\Share\Folder\context.xml' -Username 'ijkl' -Password 'mnop'

Open in new window

Or with a text file built like this:
C:\scripts\context.xml
\\Server1\Share\Folder\context.xml
\\Server2\Share\Folder\context.xml

Open in new window

To be called for example like this:
C:\scripts\context.ps1 -XmlFile (Get-Content -Path 'C:\scripts\FileList.txt') -Username 'ijkl' -Password 'mnop'

Open in new window

If you require server specific user names and passwords, and want to do it all in one go, you need a csv as input file; leave any properties not to be set empty (with or without surrounding quotes):
"Path","Database","User","Password"
"\\Server1\Share\Folder\context.xml","db1","user1","pass1"
"\\Server2\Share\Folder\context.xml","","user2","pass2"
"\\Server3\Share\Folder\context.xml","db3",,

Open in new window

Then use the following line
* The csv header defines the property names for the rows (for demonstration purposes, I made them different from the argument names, but you can of course use a header with "XmlFile","Database","Username","Password" as well)
* The "%" is short for "ForEach-Object"
* The "$_" is the loop variable (that is, the row currently being processed)
* As mentioned, you can always add -WhatIf to only generate output and not save, even if line 62 with "$xml.Save($Path)" is active.
Import-Csv 'C:\scripts\context.csv' | % {'C:\scripts\Context.ps1' -XmlFile $_.Path -DataBase $_.Database -Username $_.User -Password $_.Password -WhatIf}

Open in new window

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
DevSupportAuthor Commented:
Thank You for your response, sorry was on vacation and did not get a chance to test it. I'll test it an let you know.

appreciate your details response and scripting efforts

Thanks
0
DevSupportAuthor Commented:
Great Solution, still testing on pilot basis and  I am sure it works well for Production!
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Shell Scripting

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.