Scheduled Tasks is sort of a weird beast. Powershell v2 natively does not handle scheduled tasks very well at all, so you will see most people using scripting with schtasks, however this way is still clunky at best.

I recently was tasked with getting all of the ntbackup information for our servers. The type of info we were looking for was who was the task running as, where were the backups going etc… You know, the type of things us Sys Admins want to know.

Just doing the standard “schtasks.exe /query” doesn’t really get us all the info we really want to know…

Yes, I know, that does have anything to do with ntbackup, but as you can see, with just those three pieces of info, that isn’t going to be nearly enough information.

Fortunately schtasks has a verbose switch…. So lets try “schtasks.exe /query /v”

Whoa whoa whoa. That just looks like a mess…ok there has to be a better way than this…

The /fo csv  switch allows me to get the data as a csv which I can immediately turn around and use Convertfrom-csv to get the data into a nice array with properties and everything else….

But wait, Task to Run isn’t all the information it should be. It doesn’t contain where the backup(the .bkf) is being saved to, which is arguably one of most important pieces of information.

This is atleast what I was dealing with and what I wanted to figure out. From what I have gathered the schtasks only can display info up to a  string of length 254ish. Apparently it is an old cmd limitation. This also gets carried over into the csv format of the output.

What I did find though is that if you use the xml output you can get the information you want….which is why I created this script…


ForEach ($server in $servers){

$ping = new-object System.Net.NetworkInformation.Ping
 $Reply = $ping.send($server)
Catch {}
if ($Reply.status –eq “Success”)

FOREACH ($server in $online)

Write-host "Grabbing Tasks from $server"

$querycsv= schtasks.exe /query /v /s $server /fo csv
IF ($querycsv -ne $null){$tasks=convertfrom-csv $querycsv}
$queryxml=(schtasks.exe /query /s $server /xml one)

IF($querycsv -eq $null){continue}

$switch= ($tasks.count -gt 1)

FOREach ($task in $tasks)
 IF ($task.("task to run") -like "*ntbackup*")

switch ($switch)

$false{$task.("task to run")=$queryxml.tasks.task.actions.exec.command + " " + $queryxml.tasks.task.actions.exec.arguments}

 {$i=0; do { if ($queryxml.tasks.("#comment")[$i] -like "*$name*") {$num=$i}; $i++} while ($i -le $queryxml.tasks.("#comment").count)
 $task.("task to run")=$queryxml.tasks.task[$num].actions.exec.command + " " + $queryxml.tasks.task[$num].actions.exec.arguments



This script presented two main issues that I had to deal with, along with several little ones. The first issue is  that in the xml output, it stores the name of the task, and the info for that task in two seperate arrays. They are however in the same order. Meaning that if the name of the task is object 5 in the first array, all that information for the task is object number 5 in the second array. This is where lines 46 and 47 come into play

Lines 6-18: I normally pull my $servers from AD so I threw in these lines to check if the servers were actually awake and alive

Lines 19-30: These are the basic info gathering I was doing earlier in the post. Along with that are a couple logic statements to account for that information gathering being blank.

Line 32: A boolean to determine if there is more than one scheduled task created for the server…

Line 34-54: Seperates the tasks between tasks that are ntbackup related, ones that aren’t, and further seperating tasks by ones that have ntbackup listed with more than 1 task listed. At the end, the all tasks are thrown into the $out array

Line 46-47: For ntbackup tasks, it figures out where that information is located in its xml equivalent. Then replaces the “Task To Run” property with the “Command” and “Argument” properties for the xml version.

In the end we have

As you can see this is much different than the first run earlier as now it contains everything regarding the ntbackup…INCLUDING the full path, which is what I was going after in the first place. Also following the Masterful Jeff Hicks’s tip from a previous post. I specifically left this one to be more generic so it would be immediately useable to more people.

Hope this Helps and let me know what you think!!!!

Categories: Powershell


Leave a Reply

%d bloggers like this: