Downloads
140978.zip

An Update to Get-ScheduledTask.ps1: June 19, 2012

In my article “How-To: Use PowerShell to Report on Scheduled Tasks” (December 2011), I presented the Get-ScheduledTask.ps1 script that solves the problem of how to report on scheduled tasks, mainly because the native Windows Schtasks.exe isn’t up to the job. Since the article was published, I have made two important updates to this script:

  1. The original version of the script had a bug. If a scheduled task does not have a LastResult property, the original version of the script would output the previous task’s LastResult property instead of $null, as it should have. Thanks to Pawel Czopowik for alerting me to the bug.
  2. The original version of the script wasn’t aware of the existence of hidden tasks. You can hide a task by selecting the Hidden check box on the task’s General page. The updated version of the script adds the -Hidden parameter which allows the script to enumerate these hidden tasks. Without the -Hidden parameter, the script behaves as before and ignores hidden tasks.

You can download the updated copy of the script at the top of this page from the 140978.zip link.
—Bill Stewart

The Windows NT family of OSs has had a built-in program scheduler since its inception. It has grown from the command-line based At scheduler available in Windows NT to the more powerful Task Scheduler service that debuted in Windows 2000. In Windows Server 2008 and Windows Vista, Microsoft overhauled the Task Scheduler service and provided even more functionality.

Windows 2000 didn't provide a script object or command-line interface to the Task Scheduler service, with the exception of the difficult-to-use command-line utility Jt.exe, which was provided in the Microsoft Windows NT Server Resource Kit and not the OS. However, this situation improved starting in Windows XP, which provided the Schtasks utility.

I've seen various requests in newsgroups and online forums asking for a way to create a report of scheduled tasks on one or more computers. I'll describe the OS's built-in way of doing this (namely, Schtasks.exe with the /query parameter), why its output format is difficult to use for reporting purposes, and how I solved this problem using a PowerShell script.

The Problem with Schtasks

As I mentioned previously, XP and later includes the Schtasks utility, which is a command-line interface to the Task Scheduler. The Schtasks command's /query parameter outputs a list of scheduled tasks on a computer. For example, the command

schtasks /query /s server1 /fo CSV

outputs the scheduled tasks on the computer named server1 in comma-separated value (CSV) format, which is suitable for importing into a spreadsheet or database. The Schtasks /query command works fine on XP and Windows 2003, but if you use it on later versions, you'll run into problems. Vista/Server 2008 and later support task folders, and unfortunately the Schtasks command outputs a separate CSV header row for each task folder, even if a task folder doesn't contain any tasks. Figure 1 shows a sample of CSV data imported from the Schtasks /query /fo CSV command, with the repeated CSV header rows highlighted. It isn't a big problem to delete the extra rows from the output for one computer, but this doesn't scale well when you need to report on scheduled tasks for many computers.

Figure 1: Repeated CSV header rows in the Schtasks command output

The other Schtasks output formats have problems of their own. The List format (/fo List) provides a newline-separated list, but this is difficult to parse. The Table format (/fo Table) supports a /nh (no headers) option, but there are blank lines in the output and the output is separated by task folders, making it difficult to parse as well. The XML format (/XML) requires writing XML-parsing code to generate a usable report.

Rather than wrestle with parsing the Schtasks command's output, I decided to look for a better way. Fortunately, you can use the Task Scheduler scripting objects (msdn.microsoft.com/en-us/library/aa383607.aspx) to get scheduled task information. I decided to write a PowerShell script, Get-ScheduledTask.ps1, that uses these objects to output scheduled tasks on one or more computers.

Introducing Get-ScheduledTask.ps1

Get-ScheduledTask.ps1 requires Vista/Server 2008 or later because the TaskService object isn't available on earlier OS versions. You must also run the script from an elevated PowerShell session. (To do this, right-click the PowerShell icon and choose Run as administrator.) The script's syntax is as follows:

Get-ScheduledTask

[[-TaskName] ]

[[-ComputerName] ]

[-Subfolders]

[-ConnectionCredential ]

The -TaskName parameter specifies the name of one or more scheduled tasks. Wildcards (* and ?) are allowed. You can also specify a list of task names separated by commas or a variable containing an array. If you omit this parameter, the default is "*" (i.e., output all tasks). You can omit the -TaskName parameter name if its argument is first on the command line.

The -ComputerName parameter specifies the name of one or more computers. Wildcards aren't allowed with this parameter, but you can specify a text file that contains a list of computer names (one name per line). This parameter also supports pipeline input. If you don't specify a computer name, the current computer is the default. You can omit the -ComputerName parameter name if its argument is second on the command line.

The -Subfolders parameter specifies whether the script supports task subfolders. Without this parameter, the script works only with tasks in the root tasks folder ("\"). In the Task Scheduler GUI, the root tasks folder is the topmost folder in the hierarchy. If a remote computer doesn't support task folders, this parameter is ignored.

The -ConnectionCredential parameter requires a PSCredential object (created with the Get-Credential cmdlet) that contains the credentials you want to use to connect to the TaskService object on the specified computers. Please note that this is a potentially insecure operation. The script must get a plaintext copy of the PSCredential object's password because the TaskService object's Connect method doesn't support encrypted credentials.

Get-ScheduledTask.ps1 outputs custom objects (PSObjects) for each task. Table 1 lists the default object properties output by the script. If the default set of properties provides too much information, you can use the Select-Object cmdlet to select only the properties you want. If a property isn't supported (e.g., a property that doesn't exist on an older OS), it will be empty. Also, if the ActionType is not Execute, the Action property will be empty.

Table 2 shows sample commands to run Get-ScheduledTask.ps1. Note that although the commands wrap in the table, you'd enter them on one line in the PowerShell console.

How Does It Work?

The Get-ScheduledTask.ps1 script is designed to operate like a cmdlet because it can use pipeline input in place of the -ComputerName parameter. To support this, it uses the begin and process script blocks. In the begin script block, the script defines some script-wide (global) variables, then attempts to create an instance of the TaskService object, as shown in Listing 1. If the script can't create the object, it throws an error and ends. After creating the TaskService object, the script defines all of the supporting functions. The workhorse function of the script is the get-scheduledtask2 function, which I'll describe in a moment.

The process script block is where the script passes each computer name to the get-scheduledtask2 function. The $PIPELINEINPUT variable, defined at the top of the begin script block, enables the process script block to determine whether it should use the -ComputerName parameter or retrieve its input from the pipeline.

The get-scheduledtask2 Function

As I mentioned previously, the get-scheduledtask2 function is the workhorse function of the script. The process script block executes this function for each computer name. If the -ConnectionCredential parameter is used, the function extracts the domain name, username, and plaintext copy of the password in the PSCredential object to pass to the TaskService object's Connect method. If the Connect method fails, the function outputs a warning message using the Write-Warning cmdlet and exits from the function.

After this, the get-scheduledtask2 function checks the TaskService object's HighestVersion property to determine the version of the Task Scheduler service. The function uses the service's version number to determine whether there's support for task folders and to determine the value for the output object's Elevated property (see Table 1).

The function then uses the TaskService object's GetFolder method to get the root task folder, after which it uses the get-task function to retrieve a list of all tasks on the computer. Next, the get-scheduledtask2 function compares the task's name with the name in the -TaskName parameter. If there isn't a match, the function continues to the next task. If there is a match, the function sets the $actionCount variable to zero and iterates through the task's Actions collection.

For each action in the task, the get-scheduledtask2 function uses the New-Object cmdlet to create an empty custom object (a PSObject) and adds the task's properties to PSObject. After adding all the properties, it outputs the object.

Know What's Scheduled on Your Computers

You can use Microsoft's Schtasks utility to report on scheduled tasks, but its output formats are limited, difficult to parse, and don't scale well. Get-ScheduledTask.ps1 overcomes these limitations and makes it easy to report on scheduled tasks for as many computers as you need. You can download this script by clicking the download link at the top of this page.

Listing 1: Code That Creates the TaskService Object

try {

  $TaskService = new-object -comobject "Schedule.Service"

}

catch [System.Management.Automation.PSArgumentException] {

  throw $_

}