Optimizing PowerShell Scheduled Jobs

In the first part of this series I demonstrated how to create a simple PowerShell scheduled job, where you can take your existing PowerShell scripts and easily run them as scheduled tasks. Today, I want to give you a little more information on what’s happening behind the scenes.

Here are the scheduled jobs that I’ve set up.

The get-scheduledjob cmdlet in Windows PowerShell. (Image Credit: Jeff Hicks)
The get-scheduledjob cmdlet in Windows PowerShell. (Image Credit: Jeff Hicks)

In my previous article, I set up the first job. Let’s look at it from PowerShell’s perspective in more detail.
The scheduled job execution details in Windows PowerShell. (Image Credit: Jeff Hicks)
The scheduled job execution details in Windows PowerShell. (Image Credit: Jeff Hicks)

I’ve highlighted the execution details. The scheduled task is launching PowerShell.exe and running a complicated command. You can see the same thing in the Task Scheduler.
Task Scheduler. (Image Credit: Jeff Hicks)
Task Scheduler. (Image Credit: Jeff Hicks)

Here’s how the magic happens. When you create a PowerShell scheduled job, it has to be persisted to disk so that the Task Scheduler knows what to execute. The files for each job are stored under $env:LocalAppData\Microsoft\Windows\PowerShell\ScheduledJobs.
The file path for stored scheduled jobs. (Image Credit: Jeff Hicks)
The file path for stored scheduled jobs. (Image Credit: Jeff Hicks)

There is a directory for each scheduled job. Within each directory is an XML file with the job definition and the results.
Within each directory for a scheduled job, there's an associated XML file. (Image Credit: Jeff Hicks)
Within each directory for a scheduled job, there’s an associated XML file. (Image Credit: Jeff Hicks)

Don’t bother trying to parse the XML file. You can grab everything you need from this file with the scheduled job cmdlets. Because the output is also persisted to disk, you can always get the job results.
033015 1939 ScheduledPo6
Again, you don’t need to be concerned about parsing these results. Instead use the job cmdlets.
Using the receive-job PowerShell cmdlet. (Image Credit: Jeff Hicks)
Using the receive-job PowerShell cmdlet. (Image Credit: Jeff Hicks)

I modified my original script to get the report file, but there’s one caveat. If you want to see these type of job results in PowerShell 3.0, you need to first import the PSScheduledJob module. In PowerShell 4.0, this module is imported automatically. You should get results every time the job runs regardless of whether you scheduled the task to run automatically or manually ran the task from PowerShell or the Task Scheduler.

The job results will persist between PowerShell sessions and overtime this can clutter your job listings. When you register a scheduled job, it will retain the 32 most recent results by default. That’s what my current job is doing. Look at second figure again and notice the ExecutionHistoryLength parameter. I’m going to change the current job to only keep the five most recent results.

get-scheduledjob "dc service report" | Set-ScheduledJob -MaxResultCount 5

If you know in advance how many results to retain, Register-ScheduledJob also has a MaxResultCount property. Otherwise, you can manually delete job results like any other job.

get-job -Name "DC Service Report" | Remove-Job

If you unregister a job, this will not only remove it from the Task Scheduler, but it will also delete all the associated files, which means you will lose any job results. If you need to, you can easily disable a job so that it won’t remove anything.

The Disable-ScheduledJob cmdlet. (Image Credit: Jeff Hicks)
The Disable-ScheduledJob cmdlet. (Image Credit: Jeff Hicks)

When you are ready to resume, use Enable-ScheduledJob.
The scheduled job cmdlets aren’t perfect. You may find that you need to tweak the scheduled task itself. To start, let’s say I want to adjust my current job trigger.
The get-jobtrigger cmdlet in Windows PowerShell. (Image Credit: Jeff Hicks)
The get-jobtrigger cmdlet in Windows PowerShell. (Image Credit: Jeff Hicks)

Perhaps I only need this to run Monday through Friday.

get-jobtrigger -Name "DC Service Report" | Set-JobTrigger -Weekly -DaysOfWeek "Monday","Tuesday","Wednesday","Thursday","Friday"

As you can see, this changed the scheduled task.

Our scheduled task has been updated. (Image Credit: Jeff Hicks)
Our scheduled task has been updated. (Image Credit: Jeff Hicks)

Be careful though because when you change trigger types, I went from daily to weekly, it changes the job. Now when I run the job I get access denied because I lost the credential.

get-scheduledjob "DC Service Report" | Set-ScheduledJob -Credential globomantics\jeff

Ideally, you want to get everything right the first time when you create the scheduled job. But let’s say I want to run this when I log on to my computer and then once again every hour. If you look at help for New-JobTrigger, there is no parameter set that will let me do that. I will have to manually change it in the Task Scheduler.

Editing the trigger in Task Scheduler. (Image Credit: Jeff Hicks)
Editing the trigger in Task Scheduler. (Image Credit: Jeff Hicks)

PowerShell can’t see the repetition information when it comes to the scheduled job.
033015 1939 ScheduledPo12
I think for the majority of you this shouldn’t be a problem, but I wanted you to be aware. And there is one more gotcha with scheduled jobs. I have another scheduled job set up.
033015 1939 ScheduledPo13
But the job never works.
Our job has failed to run. (Image Credit: Jeff Hicks)
Our job has failed to run. (Image Credit: Jeff Hicks)

Looking at the job in Task Scheduler tells me that the “last run of the task was terminated by the user (0x41306).” Turns out there is an odd bug when creating a weekly job using alternate credentials, which has been reported to Microsoft.

When I created the job, I was logged on as Globomantics\Jeff, but I configured the job to use Globomantics\Administrator. If I change the job credential to my Jeff account, it appears to work. The bottom line is that you will need to carefully test all of this in a non-production environment.
I see PowerShell scheduled jobs as the reward for learning PowerShell in the first place. You should be able to automate the routine, the mundane and the boring right out of your day. If you are using scheduled jobs, I’d love to hear what problems they are solving.