Reputation: 1478
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)
Upvotes: 6
Views: 2719
Reputation: 3409
$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[]
is an XPath filter criteria.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}
Upvotes: 3
Reputation: 9238
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:
Hope it helps.
Upvotes: 3