none
Powershell 5.1 - XML Parse Error after Text Replacement RRS feed

  • Question

  • Hello,

    The following code appears to run perfectly. It is run on a single domain controller as part of renaming the forest. Yes. I want to do this. 

    The text in the XML file is replaced properly, but when I run rendom /showforest, I get the following error message:
    Failed to parse the file Domainlist.xml: Win32 Error -1072896682 :-1072896682

    $OldDNSName="ad.publicdns.tld"
    $NewDNSName="dc.waynecharities.org"
    $OldNetBIOSName="AD"
    $NewNetBIOSName="DC"
    
    rendom /list
    
    $Domainlist = Get-Content .\Domainlist.xml
    $Domainlist | % { $_.Replace($OldDNSName,$NewDNSName) } | Set-Content .\Domainlist.xml
    $Domainlist = Get-Content .\Domainlist.xml
    $Domainlist | % { $_.Replace("<NetBiosName>$OldNetBIOSName</NetBiosName>","<NetBiosName>$NewNetBIOSName</NetBiosName>") } | Set-Content .\Domainlist.xml
    
    rendom /showforest

    The above should be readily duplicatable and will not actually push any changes. Of course, the obvious thought is that there is some change within the file syntax (an extra space, spelling issue, etc.). However, I have been over the side-by-side comparison and cannot find it. So I am hoping:

    1. Someone out there has better eyes than me and will enjoy the opportunity for some basic correction, or

    2. Someone knows a mechanical, underlying reason this is not working and will maybe even provide me the Select-Xml version since I am not winning with my text replacement.

    Funny side note:

    The file is still named Domainlist.xml, but the file size actually goes down from showing 2kb in File Explorer to 1kb after the script is run. this makes me think it is something under-the-hood:

    Thank you much in advance! I've been hammering away at this for longer than I care to admit :)

    For reference, the original Domainlist.xml file created by random /list is as follows:

    <?xml version ="1.0"?>
    <Forest>
    	<Domain>
    		<!-- PartitionType:Application -->
    		<Guid>8ed494b1-0672-4a26-8959-9addbda8ba37</Guid>
    		<DNSname>DomainDnsZones.ad.publicdns.tld</DNSname>
    		<NetBiosName></NetBiosName>
    		<DcName></DcName>
    	</Domain>
    	<Domain>
    		<!-- PartitionType:Application -->
    		<Guid>66b40776-4586-4e1e-8630-3aa78c91a816</Guid>
    		<DNSname>ForestDnsZones.ad.publicdns.tld</DNSname>
    		<NetBiosName></NetBiosName>
    		<DcName></DcName>
    	</Domain>
    	<Domain>
    		<!-- ForestRoot -->
    		<Guid>2e2fa5dd-754d-4c4c-b943-0a54dba10387</Guid>
    		<DNSname>ad.publicdns.tld</DNSname>
    		<NetBiosName>AD</NetBiosName>
    		<DcName></DcName>
    	</Domain>
    </Forest>



    • Edited by TechGuy1921 Tuesday, April 30, 2019 2:43 AM
    • Moved by Bill_Stewart Monday, July 29, 2019 8:26 PM This is not "troubleshoot my high risk script for me free" forum
    Monday, April 29, 2019 11:58 PM

All replies

  • Your text replacement method has damaged the XML structure or violated the schema.  You cannot safely edit XML with text methods. The XML can have many issues that cannot be anticipated when using text methods.

    Use the XML object editor to replace nodes and/or attribute content.


    \_(ツ)_/

    Tuesday, April 30, 2019 12:10 AM
  • Here is an example of how to access the nodes in the file:

    PS D:\scripts> $node = $xml.SelectSingleNode('//Domain/Guid[text()="9fe68fd1-d3b2-40be-939f-e87916c0ca36"]')
    PS D:\scripts> $node.ParentNode
    
    
    #comment    :  ForestRoot
    Guid        : 9fe68fd1-d3b2-40be-939f-e87916c0ca36
    DNSname     : KAHLNET.local
    NetBiosName : KAHLNET
    DcName      :
    
    
    
    PS D:\scripts> $node.ParentNode.DnsName
    KAHLNET.local
    PS D:\scripts> $node.ParentNode.NetBiosName
    KAHLNET
    PS D:\scripts>

    Just assign the values and save the XML to a file.


    \_(ツ)_/

    Tuesday, April 30, 2019 12:18 AM
  • It is very necessary that you use native methods to edit XML.  Opening and changing an XML file then saving as text can cause all kinds of havoc with the XML.


    \_(ツ)_/

    Tuesday, April 30, 2019 12:20 AM
  • In this case, I am trying to replace a portion of the Node entry as part of automating the process. I have pasted the Domainlist.xml contents in my original post as an edit.

    I am not yet able to duplicate the functionality of the above script - apologies for being slow and thank you for the responses.

    I also found this article:

    https://blog.stangroome.com/2014/02/10/powershell-select-xml-versus-get-content/

    I incorporated [xml] into my code as shown here:

    [xml] nlist = Get-Content .\Domainlist.xml
    $Domainlist | % { $_.Replace($OldDNSName,$NewDNSName) } | Set-Content .\Domainlist.xml
    [xml] nlist = Get-Content .\Domainlist.xml
    $Domainlist | % { $_.Replace("<NetBiosName>$OldNetBIOSName</NetBiosName>","<NetBiosName>$NewNetBIOSName</NetBiosName>") } | Set-Content .\Domainlist.xml

    But that failed because the replace method is no longer valid in that instance:

    Method invocation failed because [System.Xml.XmlDocument] does not contain a method named 'Replace'.

    I have also been experimenting with variations on this:

    rendom /list
    
    $OldDNSName="ad.publicdns.tld"
    $NewDNSName="ad.waynecharities.org"
    $NetBIOSName="AD"
    $file = "C:\TechSoftware\DomainList.xml"
    $x = [xml](Get-Content $file)
    
    Select-Xml -xml $x  -XPath //DNSname |
        % { $_.Node.'#text' = $_."#text".Replace($OldDNSName,$NewDNSName)
          }
    $x.Save($file)
    
    rendom /showforest

    • Edited by TechGuy1921 Tuesday, April 30, 2019 3:06 AM
    Tuesday, April 30, 2019 2:58 AM
  • $newDNSName = 'ad.waynecharities.org'
    $newNetBIOSName = 'AD'
    $fileName = Resolve-Path '.\Domainlist.xml'
    [xml]$xml = Get-Content $fileName.Path
    $node = $xml.SelectSingleNode('//Domain/Guid[text()="2e2fa5dd-754d-4c4c-b943-0a54dba10387"]')
    $node.ParentNode.DNSName = $newDNSName
    $node.ParentNode.NetBIOSName = $newNetBIOSName
    $xml.Save($fileName.Path)


    \_(ツ)_/

    Tuesday, April 30, 2019 3:17 AM
  • Method using current DNS name to find correct node:

    $dnsName = 'ad.publicdns.tld'
    $newDNSName = 'ad.waynecharities.org'
    $newNetBIOSName = 'AD'
    $fileName = Resolve-Path '.\Domainlist.xml'
    [xml]$xml = Get-Content $fileName.Path
    $node = $xml.SelectSingleNode("//Domain/DNSname[text()='$dnsName']")
    $node.ParentNode.DNSName = $newDNSName
    $node.ParentNode.NetBIOSName = $newNetBIOSName
    $xml.Save($fileName.Path)


    \_(ツ)_/


    • Edited by jrv Tuesday, April 30, 2019 3:26 AM
    • Proposed as answer by jrv Tuesday, April 30, 2019 4:01 AM
    Tuesday, April 30, 2019 3:19 AM
  • I have also been trying variations on: 

    rendom /list
    
    $OldDNSName="ad.publicdns.tld"
    $NewDNSName="ad.waynecharities.org"
    $NetBIOSName="AD"
    $file = "C:\TechSoftware\DomainList.xml"
    
    $files=Get-ChildItem $file -recurse 
    $files|
         ForEach-Object{
              $xml=[xml](Get-Content $_)
              $xml.selectNodes('//data/value[contains(.,"ad.publicdns.tld")]')|
                   ForEach-Object{ $_.'#text' -replace ($OldDNSName,$NewDNSName) }
         $xml.Save($_)
    }
    
    rendom /showforest

    Based on your solution here:

    https://social.technet.microsoft.com/Forums/en-US/286b0626-d1ea-48d3-91ab-9129a258e40a/how-can-i-replace-text-in-xml-node-with-powershell?forum=ITCG

    However, despite a significant number of attempts, I am still getting the same parse error, and the text in question is not being replaced.

    Tuesday, April 30, 2019 3:50 AM
  • You can try this to force the file to Unicode:

    $dnsName = 'ad.publicdns.tld'
    $newDNSName = 'ad.waynecharities.org'
    $newNetBIOSName = 'AD'
    $fileName = Resolve-Path '.\Domainlist.xml'
    [xml]$xml = Get-Content $fileName.Path
    $node = $xml.SelectSingleNode("//Domain/DNSName[text()='$dnsName']")
    $node.ParentNode.DNSName = $newDNSName
    $node.ParentNode.NetBIOSName = $newNetBIOSName
    $xml.Forest.OuterXml | Out-File $fileName.Path -Encoding Unicode


    \_(ツ)_/

    Tuesday, April 30, 2019 4:07 AM
  • This replaced text for both <DNSname> and <NetBIOSName> in the third node, but not in the first two nodes (see Domainlist.xml above).

    <?xml version="1.0"?>
    <Forest>
      <Domain>
        <!-- PartitionType:Application -->
        <Guid>8ed494b1-0672-4a26-8959-9addbda8ba37</Guid>
        <DNSname>DomainDnsZones.ad.publicdns.tld</DNSname>
        <NetBiosName>
        </NetBiosName>
        <DcName>
        </DcName>
      </Domain>
      <Domain>
        <!-- PartitionType:Application -->
        <Guid>66b40776-4586-4e1e-8630-3aa78c91a816</Guid>
        <DNSname>ForestDnsZones.ad.publicdns.tld</DNSname>
        <NetBiosName>
        </NetBiosName>
        <DcName>
        </DcName>
      </Domain>
      <Domain>
        <!-- ForestRoot -->
        <Guid>2e2fa5dd-754d-4c4c-b943-0a54dba10387</Guid>
        <DNSname>dc.waynecharities.org</DNSname>
        <NetBiosName>DC</NetBiosName>
        <DcName>
        </DcName>
      </Domain>
    </Forest>

    It also returned the same parse error:

    Tuesday, April 30, 2019 5:36 AM
  • The output from this solution was unexpected.

    Thank you for the continued input:

    Tuesday, April 30, 2019 5:40 AM
  • If you are trying to replace the values in all nodes then you will need to enumerate all nodes and replace the values you need to replace.

    $xml.SelectNodes('//Domain') |
         ForEach-Object{
             $_.DNSname = $_.DNSname -replace 'ad\.publicdns\.tld','ad.waynecharities.org'
         }


    \_(ツ)_/

    Tuesday, April 30, 2019 6:25 AM
  • Thank you.

    This code is the first code to be readable by rendom /showforest and the first to perform replacement without giving a parse error . I attribute the primary change to the forced Unicode output:

    $OldDNSName = 'ad.publicdns.tld'
    $NewDNSName = 'dc.waynecharities.org'
    $OldNetBIOSName="AD"
    $NewNetBIOSName = 'DC'
    $fileName = Resolve-Path '.\Domainlist.xml'
    [xml]$xml = Get-Content $fileName.Path
    $node = $xml.SelectSingleNode("//Domain/NetBiosName[text()='$OldNetBIOSName']")
    $node.ParentNode.NetBIOSName = $NewNetBIOSName
    $xml.SelectNodes('//Domain') |
         ForEach-Object{
             $_.DNSname = $_.DNSname -replace $OldDNSName,$NewDNSName
         }
    $xml.Forest.OuterXml | Out-File $fileName.Path -Encoding Unicode
    1. In the original post above, the rendom /list output file size is 1,352 bytes.
    2. In previous efforts, the size was cut in half and the parse error occurred.
    3. Using the above code, file size is reduced to 1,178 bytes, but the parse error does not occur.

    Interestingly, the XML file is now a single line:

    <Forest><Domain><!-- PartitionType:Application --><Guid>8ed494b1-0672-4a26-8959-9addbda8ba37</Guid><DNSname>DomainDnsZones.dc.waynecharities.org</DNSname><NetBiosName></NetBiosName><DcName></DcName></Domain><Domain><!-- PartitionType:Application --><Guid>66b40776-4586-4e1e-8630-3aa78c91a816</Guid><DNSname>ForestDnsZones.dc.waynecharities.org</DNSname><NetBiosName></NetBiosName><DcName></DcName></Domain><Domain><!-- ForestRoot --><Guid>2e2fa5dd-754d-4c4c-b943-0a54dba10387</Guid><DNSname>dc.waynecharities.org</DNSname><NetBiosName>AD</NetBiosName><DcName></DcName></Domain></Forest>

    and is missing the very first line:

    <?xml version ="1.0"?>

    Should I be concerned about the formatting and/or the missing first line?

    Thank you so much for the insights! Between this thread and all the reading I am doing, I am learning a lot. I also note that a fair number of the TechNet articles I am reading on this subject have your input jrv - much appreciation for the contributions!


    • Edited by TechGuy1921 Tuesday, April 30, 2019 12:47 PM
    Tuesday, April 30, 2019 12:36 PM
  • No.  It is a temporary use file.  Use once and throw away.


    \_(ツ)_/

    Tuesday, April 30, 2019 12:47 PM