There are many blogs on internet already speaking about updating XML files in PowerShell, but I felt need of one consolidated blog where complex XML files can also be updated with long complex Hierarchy of XML nodes and attributes.

Below is an XML example which we will try in this blog to update at various level of node Hierarchy.

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <Data version="2.0">
   3:   <Roles>
   4:     <Role Name="ManagementServer" Value="OldManagementServer" />
   5:   </Roles>
   6:   <SQL>
   7:     <Instance Server="OldSQLServer" Instance="MSSQLSERVER" Version="SQL Server 2012">
   8:       <Variable Name="SQLAdmin" Value="Domain\OldSQlAdmin" />
   9:       <Variable Name="SQLUser" Value="domain\sqluser" />
  10:     </Instance>
  11:   </SQL>
  12:   <VMs>
  13:     <VM Type="ClientVM">
  14:       <VMName>ClientVM</VMName>
  15:     </VM>
  16:     <VM Type="DNSServerVM">
  17:       <VMName>OldDNSServer</VMName>
  18:     </VM>
  19:   </VMs>
  20:   <Course>
  21:     <Subject>Maths</Subject>
  22:     <Subject>Science</Subject>
  23:   </Course>
  24: </Data>

We will target to update Roles, Variables and VMName, Subject etc. in this XML file. Below are the steps given separately on how we can update and add nodes and their attributes at various levels.

1. Define the variable which are required to be modified:-

   1: $path = 'C:\Users\sorastog\Desktop\blog\Variable.xml'
   2:  
   3: $ManagementServer = 'NewManagementServer'
   4: $SQLServer = 'NewSQLServer'
   5: $SQLAdmin = 'Domain\NewSQlAdmin'
   6: $DNSServerVMName = 'NewDNSServer'

2. Reading the content of XML file.

   1: $xml = [xml](Get-Content $path)

3. Reading List of Subject: Read Child Tags of Course Node

   1: $xml.Data.Course.Subject

4.  Update ‘ManagementServer’: Changing Attribute value of node at level 3 based on ‘Name’ attribute on same level.

   1: $node = $xml.Data.Roles.Role | where {$_.Name -eq 'ManagementServer'}
   2: $node.Value = $ManagementServer

5. Update ‘SQLServer’: Changing Attribute value of node at level 3.

   1: $node = $xml.Data.SQL.Instance
   2: $node.Server = $SQLServer

6. Update ‘SQLAdmin’: Changing Attribute value of node at level 4 based on ‘Name’ attribute on same level.

   1: $node = $xml.Data.SQL.Instance.Variable | where {$_.Name -eq 'SQLAdmin'}
   2: $node.Value = $SQLAdmin

7. Update ‘DNSServerVM’: Changing Attribute value of node at level 4 based on ‘VMType’ attribute at above level.

   1: $node = $xml.Data.VMs.VM | where {$_.Type -eq 'DNSServerVM'}
   2: $node.VMName = $DNSServerVMName

8. Update Subject Maths: Update Child nodes based on ‘Text’ property where no attribute is available to differentiate.

   1: $node = $xml.Data.Course.ChildNodes
   2: foreach($subject in $node)
   3: {
   4:     if ($subject.'#text' -eq "Maths") 
   5:     {
   6:         $newChild = $xml.CreateElement("Subject")
   7:         $newChild.set_InnerXML("History") 
   8:         $xml.Data.Course.ReplaceChild($newChild, $subject)     
   9:     }
  10: }

9. Update Subject Science: Update Child node based on position of Child node

   1: $node = $xml.Data.Course.ChildNodes
   2: $newChild = $xml.CreateElement("Subject")
   3: $newChild.set_InnerXML("Computers") 
   4: $xml.Data.Course.ReplaceChild($newChild, $node.Item(1))

10. Adding Role: Adding New Node in XML Hierarchy

   1: $newRole = $xml.CreateElement("Role")
   2: $xml.Data.Roles.AppendChild($newRole)

11. Adding ‘Name’ and ‘Value’ attributes to Role node: Adding Attributes to XML node

   1: $newRole.SetAttribute(“Name”,”ADServer”);
   2: $newRole.SetAttribute(“Value”,”NewADServer”);

12. Saving changes to XML file.

   1: $xml.Save($path)

The final PowerShell script would look like below:-

   1: $path = 'C:\Users\sorastog\Desktop\blog\XML\Variable.xml'
   2:  
   3: $ManagementServer = 'NewManagementServer'
   4: $SQLServer = 'NewSQLServer'
   5: $SQLAdmin = 'Domain\NewSQlAdmin'
   6: $DNSServerVMName = 'NewDNSServer'
   7:  
   8:  
   9: $xml = [xml](Get-Content $path)
  10:  
  11: $xml.Data.Course.Subject
  12:     
  13: $node = $xml.Data.Roles.Role | where {$_.Name -eq 'ManagementServer'}
  14: $node.Value = $ManagementServer
  15:     
  16: $node = $xml.Data.SQL.Instance
  17: $node.Server = $SQLServer
  18:  
  19: $node = $xml.Data.SQL.Instance.Variable | where {$_.Name -eq 'SQLAdmin'}
  20: $node.Value = $SQLAdmin
  21:  
  22: $node = $xml.Data.VMs.VM | where {$_.Type -eq 'DNSServerVM'}
  23: $node.VMName = $DNSServerVMName
  24:  
  25: $node = $xml.Data.Course.ChildNodes
  26: foreach($subject in $node)
  27: {
  28:     if ($subject.'#text' -eq 'Maths') 
  29:     {
  30:         $newChild = $xml.CreateElement("Subject")
  31:         $newChild.set_InnerXML("History") 
  32:         $xml.Data.Course.ReplaceChild($newChild, $subject)     
  33:     }
  34: }
  35:  
  36: $node = $xml.Data.Course.ChildNodes
  37: $newChild = $xml.CreateElement("Subject")
  38: $newChild.set_InnerXML("Computers") 
  39: $xml.Data.Course.ReplaceChild($newChild, $node.Item(1))
  40:  
  41:  
  42: $newRole = $xml.CreateElement("Role")
  43: $xml.Data.Roles.AppendChild($newRole)
  44:  
  45: $newRole.SetAttribute(“Name”,”ADServer”);
  46: $newRole.SetAttribute(“Value”,”NewADServer”);
  47:  
  48: $xml.Save($path)

You can create variable for old and new subjects to be replaced and created respectively.

We also need to consider situations  where the specified node is not found or the given attribute value is not present. We can always use the try-catch error handling approach in such scenarios, an example would be:-

   1: $node = $xml.Data.Roles.Role | where {$_.Name -eq 'ManagementServer'}
   2: if($node -eq $null)
   3: {
   4:     Write-Host "The Role 'ManagementServer' with Attribute 'Name' was not found"
   5: }
   6: else
   7: {
   8:     $node.Value = $ManagementServer
   9: }

 

Hope this will help you to update even complex XML files with multiple nodes and complex Hierarchy. If there are some XML nodes that you would like to update and the category is not included in this blog, please reply to this post and I will add it.

Till Then, Happy Scripting :)