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)
Reading the XML file in PowerShell is easy. We have the below XML file for our example, Suppose this file is saved as Animals.xml to our current path and to read this XML file we will first get the content of the file using Get-Content command and then we will perform type conversion into XML. For example,
To convert any possible output to the XML, you can use the convertTo-XML command. To expand further, we can use any of the methods (Xpath or Dot method). For example,
Despite its annoying use of angle brackets, XML format is still widely used. Configuration files, RSS feeds, Office files (the ‘x’ in the .docx) are just a partial list. Using PowerShell to parse XML files is an essential step in your PowerShell journey.
IMPORTANT: It is very important to remember that XML file exported with Export-Clixml can be imported with counterpart Import-Clixml CmdLet while the initial sample XML file cannot be imported. Both xml files are well-formated XML files but only first one is readable by PowerShell using Import-Clixml CmdLet .
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.
$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}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With