Austin Downing
Austin Downing

Reputation: 59

Modifying Multiple XML Documents Using Foreach using Powershell

I created a script to loop through our user directories and modify and XML document to change a setting in an application. Normally when I use a foreach it would make my variable = one item from collection. Instead this script is putting everything from $XMLPaths into my $Path variable.

$XMLPaths = Get-ChildItem \\DFSRoot\DFSShare\view\Profiles\*\AppData\Roaming\Trillian\ -Recurse -Force |
            Where-Object {$_.Name -contains 'Events.xml'} |
            Select FullName |
            FT -HideTableHeaders |
            Out-String

foreach ($Path in $XMLPaths) {
  $xml = [xml](Get-Content $Path)
  $node = $xml.events.prefs.setting | Where {$_.Name -eq 'Sounds'}
  $node.value = '1'
  $XML.Save($Path)
  $Path
  $node
}

Here is the XML document I am using.

<?xml version="1.0" encoding="utf-8" ?>
    <!DOCTYPE events
        PUBLIC "--//IETF//DTD RFCxxxx XEVENTS 1.0//EN" "xevents.dtd">

<!-- WARNING: This is a generated file by Trillian.  Do not update while -->
<!--          Trillian is running otherwise updates will be erased       -->

    <events>
        <version>1.0</version>
        <Prefs>
            <setting name="idle" value="1"/>
            <setting name="sounds" value="1"/>
            <setting name="sounds_away" value="1"/>
            <setting name="sounds_suppress" value="1"/>
            <setting name="automatic_outbound" value="1"/>
            <setting name="hide_disabled" value="1"/>
            <setting name="video_capture" value="1"/>
            <setting name="buzz_sound" value="1"/>
            <setting name="game_status" value="0"/>
            <setting name="awaymessage_song" value="0"/>
            <setting name="awaymessage_autosave" value="1"/>
            <setting name="awaymessage_update2" value="1"/>
            <setting name="away_autoresponse" value="0"/>
        </Prefs>
        <AwayList>
            <AwayGroup name="Root">
                <AwayMessage label="Set%20all%20Do%20Not%20Disturb" text="" awayState="1" awayMenu="1" autoRespond="0" system="1">
                    <Status medium="ASTRA" type="Do%20Not%20Disturb"/>
                </AwayMessage>
                <AwayMessage label="Set%20all%20Offline" text="" awayState="1" awayMenu="0" autoRespond="1" system="1">
                    <Status medium="ASTRA" type="Offline"/>
                </AwayMessage>
                <AwayMessage label="Set%20all%20Away" text="Away%20since%20%25time%25%20%28%25timeZoneOffset%25%29" awayState="1" awayMenu="1" autoRespond="1" system="1">
                    <Status medium="ASTRA" type="Away"/>
                </AwayMessage>
                <AwayMessage label="Set%20all%20Invisible" text="" awayState="1" awayMenu="1" autoRespond="0" system="1">
                    <Status medium="ASTRA" type="Invisible"/>
                </AwayMessage>
                <AwayMessage label="Set%20all%20Back" text="" awayState="0" awayMenu="1" autoRespond="0" system="1">
                    <Status medium="ASTRA" type="Online"/>
                </AwayMessage>
                <AwayMessage label="Set%20all%20Idle" text="Idle%20since%20%25time%25%20%28%25timeZoneOffset%25%29" awayState="1" awayMenu="0" autoRespond="1" system="1">
                    <Status medium="ASTRA" type="Away"/>
                </AwayMessage>
            </AwayGroup>
        </AwayList>
    </events>

The variable $Path which should only have one directory in it at a time instead has this in it.

\\DFSROOT\DFSSHare\view\Profiles\User1\AppData\Roaming\Trillian\users\User1\Events.xml
\\DFSROOT\DFSSHare\view\Profiles\User2\AppData\Roaming\Trillian\users\User2\Events.xml
\\DFSROOT\DFSSHare\view\Profiles\User3\AppData\Roaming\Trillian\users\User3\Events.xml
\\DFSROOT\DFSSHare\view\Profiles\User4\AppData\Roaming\Trillian\users\User4\Events.xml
\\DFSROOT\DFSSHare\view\Profiles\User5\AppData\Roaming\Trillian\users\User5\Events.xml

Upvotes: 0

Views: 508

Answers (2)

JosefZ
JosefZ

Reputation: 30153

$XMLPaths.GetType().Name returns String. Use

$auxiliaryPath = "\\DFSRoot\DFS Share\view\Profiles\*\AppData\Roaming\Trillian"
$XMLPaths = (Get-ChildItem "$auxiliaryPath\Events.xml" -Recurse -Force).FullName

Upvotes: 0

Nathan W
Nathan W

Reputation: 333

The problem lies in that your resulting $XMLPaths variable is a string and not an array. Thus, your ForEach expression interprets the variable $XMLPaths as an array of one element, with the one element being a string containing all of the paths concatenated together.

You can always check the type of a variable $Variable by typing $Variable.GetType(). For example, when I ran your first line of code this is what I saw:

PS > $XMLPaths.GetType()

IsPublic IsSerial Name    BaseType
-------- -------- ----    --------
True     True     String  System.Object

Try replacing the first line with this:

#Remote Computers
$XMLPaths = (Get-ChildItem \\DFSRoot\DFS Share\view\Profiles\*\AppData\Roaming\Trillian\ -Recurse -Force | Where-Object {$_.Name -contains 'Events.xml'} | Select FullName).FullName

After the Select FullName clause, an array of objects that contain the property FullName is returned. So by calling .FullName on that, it returns an array of the file names.

You should be able to check that $XMLPaths is of type array:

PS > $XMLPaths.GetType()

IsPublic IsSerial Name      BaseType
-------- -------- ----      --------
True     True     Object[]  System.Array

Upvotes: 0

Related Questions