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.

<?xml version="1.0" encoding="utf-8"?>
<Data version="2.0">
  <Roles>
    <Role Name="ManagementServer" Value="NewManagementServer" />
    <Role Name="ADServer" Value="NewADServer" />
    <Role Name="ADServer" Value="NewADServer" />
  </Roles>
  <SQL>
    <Instance Server="NewSQLServer" Instance="MSSQLSERVER" Version="SQL Server 2012">
      <Variable Name="SQLAdmin" Value="Domain\NewSQlAdmin" />
      <Variable Name="SQLUser" Value="domain\sqluser" />
    </Instance>
  </SQL>
  <VMs>
    <VM Type="ClientVM">
      <VMName>ClientVM</VMName>
    </VM>
    <VM Type="DNSServerVM">
      <VMName>NewDNSServer</VMName>
    </VM>
  </VMs>
  <Course>
    <Subject>History</Subject>
    <Subject>Computers</Subject>
  </Course>
  <AllContacts>
    <Contact>
      <ContactType Type="Mobile" />
      <Details Number="000000000" />
    </Contact>
    <Contact>
      <ContactType Type="Landline" />
      <Details Number="11111111111" />
    </Contact>
  </AllContacts>
</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:-

$path = 'C:\Users\sorastog\Desktop\blog\Variable.xml'
 
$ManagementServer = 'NewManagementServer'
$SQLServer = 'NewSQLServer'
$SQLAdmin = 'Domain\NewSQlAdmin'
$DNSServerVMName = 'NewDNSServer'
$NewNumber = 'NewContactNumber'

2. Reading the content of XML file.

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

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

$xml.Data.Course.Subject

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

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

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

$node = $xml.Data.SQL.Instance
$node.Server = $SQLServer

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

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

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

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

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

$node = $xml.Data.Course.ChildNodes
foreach($subject in $node)
{
    if ($subject.'#text' -eq "Maths") 
    {
        $newChild = $xml.CreateElement("Subject")
        $newChild.set_InnerXML("History") 
        $xml.Data.Course.ReplaceChild($newChild, $subject)     
    }
}

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

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

10. Adding Role: Adding New Node in XML Hierarchy

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

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

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

12. Updating Contact Number when Type is Mobile

$allContactsNodes = $xml.Data.AllContacts.Contact
foreach($node in $allContactsNodes)
{
   if($node.ContactType.Type -eq 'Mobile')
   {
       $updateNode = $node.Details
       $updateNode.Number = $NewNumber
       break
   }
}

13. Saving changes to XML file.

   1: $xml.Save($path)

The final PowerShell script would look like below:-

$path = 'C:\Users\sorastog\Desktop\Variable.xml'
 
$ManagementServer = 'NewManagementServer'
$SQLServer = 'NewSQLServer'
$SQLAdmin = 'Domain\NewSQlAdmin'
$DNSServerVMName = 'NewDNSServer'
$NewNumber = 'NewContactNumber'
 
$xml = [xml](Get-Content $path)
 
$xml.Data.Course.Subject
     
$node = $xml.Data.Roles.Role | where {$_.Name -eq 'ManagementServer'}
$node.Value = $ManagementServer
    
$node = $xml.Data.SQL.Instance
$node.Server = $SQLServer
 
$node = $xml.Data.SQL.Instance.Variable | where {$_.Name -eq 'SQLAdmin'}
$node.Value = $SQLAdmin
 
$node = $xml.Data.VMs.VM | where {$_.Type -eq 'DNSServerVM'}
$node.VMName = $DNSServerVMName
  
$node = $xml.Data.Course.ChildNodes
foreach($subject in $node)
{
   if ($subject.'#text' -eq 'Maths') 
   {
       $newChild = $xml.CreateElement("Subject")
       $newChild.set_InnerXML("History") 
       $xml.Data.Course.ReplaceChild($newChild, $subject)     
    }
}
  
$node = $xml.Data.Course.ChildNodes
$newChild = $xml.CreateElement("Subject")
$newChild.set_InnerXML("Computers") 
$xml.Data.Course.ReplaceChild($newChild, $node.Item(1))
 
$newRole = $xml.CreateElement("Role")
$xml.Data.Roles.AppendChild($newRole)
 
$newRole.SetAttribute(“Name”,”ADServer”);
$newRole.SetAttribute(“Value”,”NewADServer”);
 
$allContactsNodes = $xml.Data.AllContacts.Contact
foreach($node in $allContactsNodes)
{
   if($node.ContactType.Type -eq 'Mobile')
   {
       $updateNode = $node.Details
       $updateNode.Number = $NewNumber
       break
   }
}
 
$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 :)