shew01
November 24, 2016, 9:48pm
1
I currently have a flat file that I use to release source code changes to a number of servers, and I would like to convert that flat file to XML. A while back, I ran across this article http://www.powershellmagazine.com/2013/08/19/mastering-everyday-xml-tasks-in-powershell/ that illustrates an easy way to generate XML using PowerShell.
I’d like to get feedback from two areas:
Here is the XML file that I generated using the concepts found in the link above. I’m not an XML expert by any means; so, if there is a better way to represent the “one source code file to many servers” relationship for DestinationComputer_NME, I’m open to suggestions.
https://gist.github.com/shew01/b856c835ae5417848e3068bd1478c7c5#file-c-cron-dba-jps-xml_question-release_code_source_code-xml
When I need to release a change to a piece of source code, I would like to read the above XML document for the file group and then release (i.e., copy) all of files within that file group to all of the servers that are applicable to that file group. (I want to copy all of the files for a given file group so that I can be assured that all of the individual files for each file group are of the same “version.”) For example, if I make a change to any file in the “FileGroup_C” file group, I would like to loop through the XML (for lack of a better term) and determine that I need to copy “my_script_d.ps1” and “my_script_e.ps1” to “WORKSTATIONA” and “WORKSTATIONB”.
Here is the code that I have so far, which partially works. What I need is a way to “drill down” into the DestinationComputers element and display the individual names for DestinationComputer_NME. (Currently, DestinationComputers does not return correctly.)
$XMLFilePath_NME = "C:\cron\dba\jps\xml_question\release_code_source_code.xml"
$XML_OBJ = New-Object -TypeName XML
$XML_OBJ.Load($XMLFilePath_NME)
[array]$Loop_OBJ = $XML_OBJ.SourceCodeFiles.FileGroup_C.SourceCodeFile | Select-Object -Property File_NME, OverwriteFile_IND, SourceLocation_DIR, DestinationLocation_DIR, DestinationComputers, Comment_DSC
foreach ($Element in $Loop_OBJ)
{
$a = $Element.File_NME
$b = $XML_OBJ.SourceCodeFiles.FileGroup_C.SourceCodeFile.DestinationComputers.DestinationComputer_NME
write-output "$a $b"
}
exit
Here is the output that I currently get. It does not contain the appropriate computer names–it appears to repeat all of the computer names.
my_script_d.ps1 SERVER MANAGEMENT REPORTING WORKSTATIONA WORKSTATIONB SERVER MANAGEMENT REPORTING WORKSTATIONA WORKSTATIONB
my_script_e.ps1 SERVER MANAGEMENT REPORTING WORKSTATIONA WORKSTATIONB SERVER MANAGEMENT REPORTING WORKSTATIONA WORKSTATIONB
shew01
November 24, 2016, 9:58pm
3
Please excuse the extra posts. I finally found a way to point to the XML on GitHub Gist instead of having the browser attempt to render it and make it unreadable.
Any help is appreciated on the above scenario for reading XML child elements using PowerShell.
In the foreach loop you reference the $XMLObject every time and not the specific item in the $Loop_Obj.
So if you change the $b line to:
$b = $Element.DestinationComputers.DestinationComputer_NME
You should not get the repeated behaviour.
As you are looking at the current item in the Loop_Obj.
shew01
November 25, 2016, 7:40am
5
Thanks, that fixed the repeat behavior.
Hmmm… I just noticed another problem. I need to find a way to “search” the XML for the appropriate file list group, for example, “FileGroup_C.” My example has “FileGroup_C” hard coded. I presume that I need to use a “where-object” command, but I’m not sure how to put it into the following line.
[array]$Loop_OBJ = $XML_OBJ.SourceCodeFiles.FileGroup_C.SourceCodeFile | Select-Object -Property File_NME, OverwriteFile_IND, SourceLocation_DIR, DestinationLocation_DIR, DestinationComputers, Comment_DSC
Maybe there is a better way but if it’s not a problem I would use an attribute on the Filegroup tag.
So instead of: (xml chevrons removed)
FileGroup_A /FileGroup_A
FileGroup_B /FileGroup_B
I would use:
FileGroup group=“A” /FileGroup
FileGroup group=“B” /FileGroup
Then you could reference them by using:
$groupLetter = 'A'
$XML_OBJ.SourceCodeFiles.FileGroup | Where {$_.Group -eq $groupLetter}
Edit: XML seems to be rendered
shew01
November 25, 2016, 8:51pm
7
I’m still doing something wrong, but I am don’t know what it is. Here is a link to the revised XML file. https://gist.github.com/shew01/134f4f0919f5b517e2a803ed1c93f1dd#file-release_code_source_code-xml
Here is the revised code, but the output is not correct.
$XMLFilePath_NME = "C:\cron\dba\jps\xml_question\release_code_source_code.xml"
$groupLetter = 'A'
$XML_OBJ = New-Object -TypeName XML
$XML_OBJ.Load($XMLFilePath_NME)
# [array]$Loop_OBJ = $XML_OBJ.SourceCodeFiles.FileGroup_C.SourceCodeFile | Select-Object -Property File_NME, OverwriteFile_IND, SourceLocation_DIR, DestinationLocation_DIR, DestinationComputers, Comment_DSC
[array]$Loop_OBJ = $XML_OBJ.SourceCodeFiles.FileGroup | Where {$_.Group -eq "A"} | Select-Object -Property Group, File_NME, OverwriteFile_IND, SourceLocation_DIR, DestinationLocation_DIR, DestinationComputers, Comment_DSC
# [array]$Loop_OBJ = $XML_OBJ.SourceCodeFiles.FileGroup | Where {$_.Group -eq "A"}
$Loop_OBJ.count
$Loop_OBJ | gm
$a = $Loop_OBJ.File_NME
$a
foreach ($Element in $Loop_OBJ)
{
$a = $Element.File_NME
$b = $Element.DestinationComputers.DestinationComputer_NME
write-output "$a $b"
}
exit
Here is the output that I am getting.
1
TypeName: Selected.System.Xml.XmlElement
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Comment_DSC NoteProperty object Comment_DSC=null
DestinationComputers NoteProperty object DestinationComputers=null
DestinationLocation_DIR NoteProperty object DestinationLocation_DIR=null
File_NME NoteProperty object File_NME=null
Group NoteProperty string Group=A
OverwriteFile_IND NoteProperty object OverwriteFile_IND=null
SourceLocation_DIR NoteProperty object SourceLocation_DIR=null
shew01
November 25, 2016, 9:53pm
8
This version of the script is close, but all of the destination computers are displaying on a single line. I’m trying to find a way to break them out into separate lines so that I can loop through them.
$XMLFilePath_NME = "C:\cron\dba\jps\xml_question\release_code_source_code.xml"
$groupLetter = 'C'
$XML_OBJ = New-Object -TypeName XML
$XML_OBJ.Load($XMLFilePath_NME)
# [array]$Loop_OBJ = $XML_OBJ.SourceCodeFiles.FileGroup_C.SourceCodeFile | Select-Object -Property File_NME, OverwriteFile_IND, SourceLocation_DIR, DestinationLocation_DIR, DestinationComputers, Comment_DSC
# [array]$Loop_OBJ = $XML_OBJ.SourceCodeFiles.FileGroup | Where {$_.Group -eq $groupLetter} | Select-Object -Property Group, File_NME, OverwriteFile_IND, SourceLocation_DIR, DestinationLocation_DIR, DestinationComputers, Comment_DSC
[array]$Loop_OBJ = $XML_OBJ.SourceCodeFiles.FileGroup | Where {$_.Group -eq $groupLetter} | Select-Object -Property Group, SourceCodeFile
$GroupHit_CNT = $Loop_OBJ.count
write-output ""
write-output "`$GroupHit_CNT = $GroupHit_CNT"
write-output ""
#$Loop_OBJ.SourceCodeFile.File_NME
#$Loop_OBJ.SourceCodeFile.OverwriteFile_IND
#$Loop_OBJ.SourceCodeFile.SourceLocation_DIR
#$Loop_OBJ.SourceCodeFile.DestinationLocation_DIR
#$Loop_OBJ.SourceCodeFile.DestinationComputers
#$Loop_OBJ.SourceCodeFile.Comment_DSC
#
#write-output ""
#write-output "-- Just before loop --------------------"
foreach ($Element in $Loop_OBJ.SourceCodeFile)
{
$File_NME = $Element.File_NME
$OverwriteFile_IND = $Element.OverwriteFile_IND
$SourceLocation_DIR = $Element.SourceLocation_DIR
$DestinationLocation_DIR = $Element.DestinationLocation_DIR
$Comment_DSC = $Element.Comment_DSC
#$Element.DestinationComputers | gm
write-output ""
write-output "`$File_NME = $File_NME"
write-output "`$OverwriteFile_IND = $OverwriteFile_IND"
write-output "`$SourceLocation_DIR = $SourceLocation_DIR"
write-output "`$DestinationLocation_DIR = $DestinationLocation_DIR"
write-output "`$Comment_DSC = $Comment_DSC"
#$Element.DestinationComputers | gm
write-output "`$DestinationComputer_NME ="
foreach ($Computer in $Element.DestinationComputers)
{
$a = $Computer.DestinationComputer_NME
write-output " $a"
}
}
exit
Here is the current output.
$GroupHit_CNT = 1
$File_NME = my_script_d.ps1
$OverwriteFile_IND = Y
$SourceLocation_DIR = W:\DBA\DBA\my_dir_d\
$DestinationLocation_DIR = c:\cron\dba\dest_d\
$Comment_DSC = Comment D
$DestinationComputer_NME =
WORKSTATIONA WORKSTATIONB
$File_NME = my_script_e.ps1
$OverwriteFile_IND = Y
$SourceLocation_DIR = W:\DBA\DBA\my_dir_e\
$DestinationLocation_DIR = c:\cron\dba\dest_e\
$Comment_DSC = Comment E
$DestinationComputer_NME =
WORKSTATIONA WORKSTATIONB
shew01
November 25, 2016, 10:19pm
9
There may be a better way to do it, but this code appears to work.
$XMLFilePath_NME = "C:\cron\dba\jps\xml_question\release_code_source_code.xml"
$groupLetter = 'C'
$XML_OBJ = New-Object -TypeName XML
$XML_OBJ.Load($XMLFilePath_NME)
# [array]$Loop_OBJ = $XML_OBJ.SourceCodeFiles.FileGroup_C.SourceCodeFile | Select-Object -Property File_NME, OverwriteFile_IND, SourceLocation_DIR, DestinationLocation_DIR, DestinationComputers, Comment_DSC
# [array]$Loop_OBJ = $XML_OBJ.SourceCodeFiles.FileGroup | Where {$_.Group -eq $groupLetter} | Select-Object -Property Group, File_NME, OverwriteFile_IND, SourceLocation_DIR, DestinationLocation_DIR, DestinationComputers, Comment_DSC
[array]$Loop_OBJ = $XML_OBJ.SourceCodeFiles.FileGroup | Where {$_.Group -eq $groupLetter} | Select-Object -Property Group, SourceCodeFile
$GroupHit_CNT = $Loop_OBJ.count
write-output ""
write-output "`$groupLetter = $groupLetter"
write-output "`$GroupHit_CNT = $GroupHit_CNT"
#$Loop_OBJ.SourceCodeFile.File_NME
#$Loop_OBJ.SourceCodeFile.OverwriteFile_IND
#$Loop_OBJ.SourceCodeFile.SourceLocation_DIR
#$Loop_OBJ.SourceCodeFile.DestinationLocation_DIR
#$Loop_OBJ.SourceCodeFile.DestinationComputers
#$Loop_OBJ.SourceCodeFile.Comment_DSC
#
#write-output ""
#write-output "-- Just before loop --------------------"
foreach ($Element in $Loop_OBJ.SourceCodeFile)
{
$File_NME = $Element.File_NME
$OverwriteFile_IND = $Element.OverwriteFile_IND
$SourceLocation_DIR = $Element.SourceLocation_DIR
$DestinationLocation_DIR = $Element.DestinationLocation_DIR
$Comment_DSC = $Element.Comment_DSC
write-output ""
write-output "`$File_NME = $File_NME"
write-output "`$OverwriteFile_IND = $OverwriteFile_IND"
write-output "`$SourceLocation_DIR = $SourceLocation_DIR"
write-output "`$DestinationLocation_DIR = $DestinationLocation_DIR"
write-output "`$Comment_DSC = $Comment_DSC"
#$Element.DestinationComputers | gm
foreach ($Computer in $Element.DestinationComputers.GetEnumerator()."#text")
{
$DestinationComputer_NME = $Computer
write-output "`$DestinationComputer_NME = $DestinationComputer_NME"
}
}
exit
Here is the output.
$groupLetter = C
$GroupHit_CNT = 1
$File_NME = my_script_d.ps1
$OverwriteFile_IND = Y
$SourceLocation_DIR = W:\DBA\DBA\my_dir_d\
$DestinationLocation_DIR = c:\cron\dba\dest_d\
$Comment_DSC = Comment D
$DestinationComputer_NME = WORKSTATIONA
$DestinationComputer_NME = WORKSTATIONB
$File_NME = my_script_e.ps1
$OverwriteFile_IND = Y
$SourceLocation_DIR = W:\DBA\DBA\my_dir_e\
$DestinationLocation_DIR = c:\cron\dba\dest_e\
$Comment_DSC = Comment E
$DestinationComputer_NME = WORKSTATIONA
$DestinationComputer_NME = WORKSTATIONB
Not exactly sure what you want to do but there is really just a small change to the original code.
The difference is that the “Where” statement is checking on the tag “one step up” in the hierarchy in the XML tree compared to what was done earlier.
E.g.
$XMLFilePath_NME = '"C:\cron\dba\jps\xml_question\release_code_source_code.xml'
$XML_OBJ = New-Object -TypeName XML
$XML_OBJ.Load($XMLFilePath_NME)
$groupLetter = 'A'
$Group_OBJ = $XML_OBJ.SourceCodeFiles.FileGroup | Where {$_.Group -eq $groupLetter}
$Loop_OBJ = $Group_OBJ.SourceCodeFile
foreach ($Element in $Loop_OBJ)
{
$a = $Element.File_NME
$b = $Element.DestinationComputers.DestinationComputer_NME
write-output "$a $b"
}
So the Group_OBJ contain the data from the selected group and downwards.
The Loop_OBJ contain the information one step down from the Group_OBJ.
You could do it on one line but I seperated the steps so that it was easier to read.
But you could do it like this:
$Loop_OBJ = $XML_OBJ.SourceCodeFiles.FileGroup | Where {$_.Group -eq $groupLetter} | Select -expandproperty SourceCodeFile
Hope that helps.