6

I'm having trouble getting the desired results in the correct format with this PowerShell query when run against an XML file exported from Task Scheduler. I will eventually have this run against all Task Scheduler exported jobs dumped to the same one XML file but I'm starting with just one for now.

PowerShell Logic (not working)

$file = "C:\folder\test.xml"
$Xml  = [xml](Get-Content $file)
$Xml.SelectNodes("//*").ChildNodes | Select URI,Command | 
Where-Object { ($_.URI -ne $null) -or ($_.Command -ne $null) } | FL 

Perhaps there's a better or more explicit way of doing this with PowerShell in a loop or embedding some C# code and executing that with PowerShell, etc. I figured I'd ask others here since I cannot get this figured out after a ton of testing and research but I'm open to all ideas to help me get the desired result or something similar (see below). If I needed the result from SQL Server I'd use FOR XML PATH.


Current Result Format (not desired)

URI     : \_Test\Test
Command : 

URI     : 
Command : C:\Folder\process1.exe

URI     : 
Command : C:\Folder\process2.exe

Desired Result Format (or similar)

URI     : \_Test\Test
Command : C:\Folder\process1.exe
Command : C:\Folder\process2.exe

Sample XML

   <?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
  <RegistrationInfo>
    <Date>2017-03-09T09:36:56.2658334</Date>
    <Author>FBI-PC1\User</Author>
    <URI>\_Test\Test</URI>
  </RegistrationInfo>
  <Triggers />
  <Principals>
    <Principal id="Author">
      <UserId>S-1-5-21-3517161704-4063526634-2635523359-1001</UserId>
      <LogonType>InteractiveToken</LogonType>
      <RunLevel>LeastPrivilege</RunLevel>
    </Principal>
  </Principals>
  <Settings>
    <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
    <DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries>
    <StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
    <AllowHardTerminate>true</AllowHardTerminate>
    <StartWhenAvailable>false</StartWhenAvailable>
    <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
    <IdleSettings>
      <StopOnIdleEnd>true</StopOnIdleEnd>
      <RestartOnIdle>false</RestartOnIdle>
    </IdleSettings>
    <AllowStartOnDemand>true</AllowStartOnDemand>
    <Enabled>true</Enabled>
    <Hidden>false</Hidden>
    <RunOnlyIfIdle>false</RunOnlyIfIdle>
    <WakeToRun>false</WakeToRun>
    <ExecutionTimeLimit>PT72H</ExecutionTimeLimit>
    <Priority>7</Priority>
  </Settings>
  <Actions Context="Author">
    <Exec>
      <Command>C:\Folder\process1.exe</Command>
    </Exec>
    <Exec>
      <Command>C:\Folder\process2.exe</Command>
    </Exec>
  </Actions>
</Task>

System Specs (needs to work with)

  • Windows 7
  • PowerShell V 4.0

2 Answers 2

3

Since you are selecting both the objects together applying the where condition, so for maintaining the equality as a single object, it is showing like that .

In your case what you can do is to store the resultant in arraylist and do the manipulation there for all the Commands what you are getting like :

$file = "C:\folder\test.xml"
$Xml  = [xml](Get-Content $file)
$URI= $Xml.SelectNodes("//*").ChildNodes | Select URI| Where-Object { $_.URI -ne $null}
$Command= $Xml.SelectNodes("//*").ChildNodes | Select Command| Where-Object { $_.Command -ne $null}

$arraylisttable = New-Object System.Collections.ArrayList
$arraylisttable.Add($URI)  | Out-Null

foreach($cmd in $Command)
{
$arraylisttable.Add($Cmd) | Out-Null
}
$arraylisttable | fl

OUTPUT:

Result

Hope it helps.

Sign up to request clarification or add additional context in comments.

Comments

3
$Xml.SelectNodes("//*[local-name()='Command' or local-name()='URI']") | select Name, "#text"

Name    #text
----    -----
URI     \_Test\Test
Command C:\Folder\process1.exe
Command C:\Folder\process2.exe

This way you move selection logic to Xpath. Things to note:

  • //* selects all nodes
  • text inside [] is an XPath filter criteria.
  • using local-name() XPath function is a trick to avoid messing with namespaces

Note that XPath comparisons are case sensitive. Unfortunately XPath 1.0 does not have lower-case() or the like function. To make case-insensitive comparison (as Powershell does) use translate() function:

$Xml.SelectNodes("//*[translate(local-name(), 'COMAND', 'comand')='command' or translate(local-name(), 'uri', 'URI')='URI']") | select Name, "#text"

If you prefer pure Powershell over XPath here is alternate solution:

$xml.SelectNodes('//*') | ? {$_.Name -in ('command', 'uri')} | select Name, "#text"

One more thing about tasks. You do not have to dump them. They natively reside as xml already in theC:\Windows\Tasks or C:\Windows\System32\Tasks folders depending on the OS. For Win7 it is C:\Windows\System32\Tasks. You would need admin access to read them.

Edit: To get exactly the same output:

$xml.SelectNodes('//*') | ? { $_.Name -in ('command', 'uri') } |
 select Name, @{Name = 'Value'; Expression = {": $($_.'#text')" } } |
 ft -HideTableHeaders

URI     : \_Test\Test
Command : C:\Folder\process1.exe
Command : C:\Folder\process2.exe

Custom object:

$customObject = [pscustomObject]@{
    URI = ($xml.SelectNodes("//*[local-name()='URI']")).'#text'
    Command = ($xml.SelectNodes("//*[local-name()='Command']")).'#text'
}
$customObject | fl

URI     : \_Test\Test
Command : {C:\Folder\process1.exe, C:\Folder\process2.exe}

1 Comment

@MagicallyDelicous No problem, I have updated my answer just for the case.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.