One of the most common phone calls that the support team gets for Windows PowerShell is "How do I use Task Scheduler to schedule Windows PowerShell scripts?". As an administrator, you need to have full control over when scripts run in your environment. Perhaps you need run a script only during a one-off maintenance window or maybe you want to schedule some routine maintenance on a server so that it runs at non-peak times. Although it was possible to use Task Scheduler to invoke scripts in Windows PowerShell 2.0, it was not trivial. What's more, you were responsible for writing code to store the detailed results of your script if you wanted to view them later.
In Windows PowerShell 2.0, we introduced background jobs, which let you run commands asynchronously in the background. This allows you to get the prompt back and continue running commands at the command line while the background job runs. In keeping with our sacred vow to respect your investment in learning Windows PowerShell by reusing concepts, we reused jobs in many places with Windows PowerShell 3.0. This blog post introduces just one example of this: job scheduling. This feature allows administrators to schedule background jobs for execution at a later time or according to a particular schedule with a set of cmdlets right out of the box. One of the most valuable features of scheduled jobs in Windows PowerShell 3.0 is that we'll even take care of storing the results and output of your job.
Where to find the Job Scheduling cmdlets
The Job Scheduling cmdlets are delivered in the PSScheduledJob module that is included in Windows PowerShell 3.0 Beta and in the Windows Management Framework 3.0 Beta.
There are 16 cmdlets in the PSScheduledJob module that allow you to work with Scheduled Jobs, the triggers that cause them to run, and some more advanced configuration. To see the cmdlets, type:
PS > Get-Command -Module PSScheduledJob | Sort-Object Noun, Verb
Capability Name ModuleName
---------- ---- ----------
Cmdlet Add-JobTrigger psscheduledjob
Cmdlet Disable-JobTrigger psscheduledjob
Cmdlet Enable-JobTrigger psscheduledjob
Cmdlet Get-JobTrigger psscheduledjob
Cmdlet New-JobTrigger psscheduledjob
Cmdlet Remove-JobTrigger psscheduledjob
Cmdlet Set-JobTrigger psscheduledjob
Cmdlet Disable-ScheduledJob psscheduledjob
Cmdlet Enable-ScheduledJob psscheduledjob
Cmdlet Get-ScheduledJob psscheduledjob
Cmdlet Register-ScheduledJob psscheduledjob
Cmdlet Set-ScheduledJob psscheduledjob
Cmdlet Unregister-ScheduledJob psscheduledjob
Cmdlet Get-ScheduledJobOption psscheduledjob
Cmdlet New-ScheduledJobOption psscheduledjob
Cmdlet Set-ScheduledJobOption psscheduledjob
Basics of the Job Scheduling Cmdlets:
The JobTrigger cmdlets let you determine when scheduled jobs actually run. You can schedule jobs to execute one time (now or at a later date), daily, weekly, when certain users log on, or when the system first boots up.
The ScheduledJob cmdlets allow you to create and configure scheduled jobs. You use these cmdlets to perform actions like registering, unregistering, enabling and disabling scheduled jobs on the computer.
The ScheduledJobOption cmdlets allow you to specify advanced settings for scheduled jobs. With these cmdlets, you can configure many of the settings you're already familiar with in Task Scheduler, such as idle conditions that must be met before the job starts. The default values for each scheduled job should be sufficient in most cases, but the flexibility is there.
An example:
Imagine that you are incredibly passionate about configuring your computers for optimal energy efficiency. You've written a few lines to take the output of Powercfg.exe /energy and extract only the items that represent the most severe infractions. You want to run this analysis every night for a certain period of time so you can understand which issues appear most frequently.
With just a few lines, we can schedule our script to run on the server every night as part of an "EnergyAnalysisJob".
PS > $trigger = New-JobTrigger -Daily -At 3am
PS > Register-ScheduledJob -Name EnergyAnalysisJob -Trigger $trigger -ScriptBlock
{
powercfg.exe -energy -xml -output C:\temp\energy.xml -duration 60 | Out-Null
$EnergyReport = [xml](get-content C:\temp\energy.xml)
$namespace = @{ ns = "http://schemas.microsoft.com/energy/2007" }
$xPath = "//ns:EnergyReport/ns:Troubleshooter/ns:AnalysisLog/ns:LogEntry[ns:Severity = 'Error']"
$EnergyErrors = $EnergyReport | Select-Xml -XPath $xPath -Namespace $namespace
$EnergyErrors.Node | select Name, Description
}
Id Name JobTriggers Command Enabled
-- ---- ----------- ------- -------
1 EnergyAnalys... {1} ... True
If you've ever used the Start-Job cmdlet, the syntax of Register-ScheduledJob will look very familiar to you.
You can view the jobs that you have scheduled at any time by using the Get-ScheduledJob cmdlet. Scheduled Jobs are stored on a per-user basis, so Get-Scheduled job will only show the scheduled jobs that are relevant to you.
Windows PowerShell 3.0 keeps track of the results and output of scheduled jobs for you. If you come back to the server that runs the scheduled job a couple of days later, you can use the Job cmdlets (the same ones that you use to manage background jobs) to view and get the results of the EnergyAnalysisJob scheduled job. The nice thing about scheduled job results is that they are available even in different sessions! Since you are not always around to receive results when a scheduled job runs, we store them on disk so you can receive them at any time.
Here's an important "gotcha" that you should know. To get the results of scheduled jobs, you must import the PSScheduledJob module into the current session (Import-Module PSScheduledJob). Otherwise, the Job cmdlets, such as Get-Job and Receive-Job, do not return results for scheduled jobs. This is a bit of an exception to the module auto-loading behavior in Windows PowerShell 3.0. The reason is that Get-Job is in the Microsoft.PowerShell.Core snap-in, so using it doesn't automatically import the PSScheduledJob module. Also, if Get-Job were to automatically import all of the modules that implement custom job types, over time, the output would be crowded with irrelevant job types and performance might be affected by importing dozens of modules that aren't needed.
PS > Import-Module PSScheduledJob
With the PSScheduledJob module imported, Get-Job shows the results of "instances" of our scheduled jobs.
PS > Get-Job
Id Name State HasMoreData Location Command
-- ---- ----- ----------- -------- -------
2 EventCollect... Completed True localhost ...
4 EnergyAnalys... Completed True localhost ...
6 EnergyAnalys... Completed True localhost ...
Scheduled jobs have BeginTime and EndTime properties that tell when the job instance actually ran.
PS > Get-Job EnergyAnalysis | Select-Object Name,BeginTime,EndTime
Name BeginTime EndTime
---- ----------- ---------
EnergyAnalysisJob 3/12/2012 3:00:01 AM 3/12/2012 3:01:12 AM
EnergyAnalysisJob 3/13/2012 3:00:01 AM 3/13/2012 3:01:14 AM
To get the results of a scheduled job instance, use the Receive-Job cmdlet.
PS > Receive-Job -Name EnergyAnalysisJob
# Results will vary based on your system's configuration, but you will see records like:
Name : USB Device not Entering Selective Suspend
Description : This device did not enter the USB Selective Suspend state. Processor power management may be prevented when this USB device is not in the Selective Suspend state. Note that this issue will not prevent the system from sleeping.
By default, Windows PowerShell keeps the results of the last 32 instances of each scheduled job. After 32 results are stored for a particular scheduled job, the oldest ones are overwritten by subsequent executions. To change the number of results saved for each scheduled job, use the MaxResultCount parameter of the Set-ScheduledJob cmdlet.
PS > Get-ScheduledJob -Name EnergyAnalysisJob | Set-ScheduledJob -MaxResultCount 100
Lastly, if you want to start a scheduled job manually rather than waiting for its triggers, you can use the DefinitionName parameter of the Start-Job cmdlet.
PS > Start-Job -DefinitionName EnergyAnalysisJob
As you can see, working with scheduled jobs in Windows PowerShell 3.0 is a lot like working with regular background jobs, but with much more control. We hope you enjoy the flexibility and simplicity of this cool new feature.
Travis Jones [MSFT]
Program Manager - Windows PowerShell
Microsoft Corporation