- Export and import to and from Excel with the PowerShell module ImportExcel - Thu, Apr 21 2022
- Getting started with the PSReadLine module for PowerShell - Thu, Feb 24 2022
- SecretsManagement module for PowerShell: Save passwords in PowerShell - Tue, Dec 22 2020
Scheduled jobs vs. scheduled tasks
Scheduled jobs are batch jobs that run as background tasks created and managed using PowerShell. On the surface, scheduled jobs look, act, and feel much like an old Windows standard: scheduled tasks. The similarity between the two tools often confuses people. For the most part, scheduled jobs and scheduled tasks are the same but apply to different use cases. Microsoft modeled scheduled jobs to be similar to scheduled tasks on purpose so you wouldn't have to learn a new syntax for scheduling.
Scheduled tasks are native to Windows, aiming to interact with Windows and run Windows programs. Similarly, scheduled jobs are native to Windows PowerShell and cannot interact with Windows applications like Notepad, Word, or IE, for example.
So, what's so great about scheduled jobs then? While there are more than a few differences between the two, the biggest difference is with the job results. Scheduled tasks interact with Windows, but they do not capture any results from the tasks performed. Scheduled jobs are essentially PowerShell scripts, and as such, the results of the jobs are objects just like anything else you do with PowerShell. It stores the object data with the results.
If you created a scheduled job to get the running services on a computer, the results data would capture all the information about the services at the time of the job's execution. If you had a scheduled job that pinged a server, it would capture the ping results. It's like you get to go back in time and see what the data looked like at the time of its creation. As I mentioned earlier, since the results are essentially objects, you can manipulate that data and do things like pass the data to other cmdlets if need be. If you created a scheduled task that performs an action, the system does not capture the results of that action anywhere.
Let me briefly discuss management options—here's where it gets confusing. You can only create PowerShell scheduled jobs from the command line. Scheduled tasks have their own set of cmdlets for creation and management. You can use scheduled task cmdlets to view and manage scheduled jobs but not create jobs.
The Windows tool to interact with scheduled tasks is called Task Scheduler. You can create scheduled tasks from Task Scheduler but not scheduled jobs. However, you can view and manage scheduled jobs with the Task Scheduler, but you cannot use the scheduled job cmdlets to manage scheduled tasks in any way. Yes, it is a lot to try and keep track of, and I blame Microsoft for having too many options here.
I also want to point out that I am highlighting the biggest differences between the two technologies, but there are some other subtle differences I don't have the room to explain here. I recommend you read an excellent article from Microsoft that dives deep into the two tools and explains all the differences in depth.
The thing about scheduled jobs that should appeal to you, the administrator, is that you can manage the entire process of creating, managing, deleting, and reviewing results from the command line. You don't need to interact with the GUI at all. Since the PowerShell scheduled jobs are code, you can save them as scripts. If disaster strikes, you can re-run your scripts and have the scheduled jobs back in place in minutes!
Creating a scheduled job
You should be thinking about the following questions when you create your first scheduled job. I'll run through an example and show you how to build up the code based on the answers to the questions.
- When will the job run?
- How often will the job run?
- What code/cmdlets will the code run?
- What options are necessary to configure the scheduled job?
- Under what credentials will the job run?
- What will be the name of the job?
Let's walk through each question and start to build the syntax.
When will the job run? How often will the job run?
A job trigger starts a scheduled job. The cmdlet New-JobTrigger will define how often the job will run (e.g., once, daily, weekly, random, etc.) and when it will start (e.g., Monday at 1:30 p.m.).
$trigger = New-JobTrigger -Once -At 1:30PM
What code/cmdlets will the job run? The code that your scheduled job will run can either be a script block if the code is small enough, or it can call a file.
-file c:\scripts\test-connection.ps1
or
-scriptblock {code}
What options are necessary for the job to run?
Options are the conditions that may be required for the job to run, controlled through the New-ScheduledJobOption cmdlet. These are choices such as RUNELEVATED or STARTIFONBATTERY or REQUIRENETWORK. There are others, but I hope from my brief example you understand these are the more complex options you could use for a job. A scheduled job may have no options at all, but more often than not, they'll have defined options.
$opt = New-ScheduledJobOption -RunElevated -RequireNetwork
Under what credentials will the job run?
We use the Get-Credential cmdlet to consume credentials for the job to "run as."
$cred = Get-Credential -UserName company\someuser
What will be the name of the job? The -name parameter defines the job name
-name Test-DCsAreOnline
One last piece I didn't mention yet is defining how many instances of the job we want to save. Think of this as the job history option. How much history do I want to save? If we don't define this, the default is 32 instances, which may be overkill depending on what your job is doing. Keep in mind, scheduled jobs will save the output. This setting controls how much history you have to look through later on.
-MaxResultCount 10
Now you can take all of that info and build a new job. You use the Register-ScheduledJob cmdlet to pull it all together.
Register-ScheduledJob -Name Test-DCsAreOnline -Trigger $trigger -Credential $cred ` -FilePath c:\scripts\test-dconline.ps1 -MaxResultCount 5 -ScheduledJobOption $opt
Let's see another example of defining a scheduled job. This time we're defining all of our variables like we would in a script. This scheduled job runs a script that checks the disk space on domain controllers every 12 hours and saves the last 10 results.
#Define the interval to repeat the job $trigger = New-JobTrigger -Once -At 9:00AM -RepetitionInterval (New-TimeSpan -Hours 12) -RepeatIndefinitely #Get user credential so that the job has access to the network $cred = Get-Credential -UserName bigfirm.biz\someuser #Set job options $opt = New-ScheduledJobOption -RunElevated -RequireNetwork Register-ScheduledJob -Name Get-DCDiskSpace -Trigger $trigger -Credential $cred ` -FilePath c:\scripts\Get-DCDiskSpace.ps1 -MaxResultCount 10 ScheduledJobOption $opt
You can save this code to create a new scheduled job so you can easily run it again in the future or use it as a template to create more scheduled jobs.
Displaying scheduled jobs
You can view your scheduled jobs two ways: PowerShell or in the GUI. First let's take a look at viewing them from PowerShell. One simple command Get-ScheduledJob shows all of your scheduled jobs.
PS C:\scripts> get-scheduledjob | ft -auto Id Name JobTriggers Command Enabled -- ---- ----------- ------- ------- 1 Test-DCsOnline 1 D:\PowerShell_Scripts\AD-Health-Checks\Test-DCsOnline.ps1 False
Remember earlier when I mentioned you could manage your scheduled jobs with the Task Scheduler? You can view and manage scheduled jobs from the command line or from within the Task Scheduler. However, even though you use the Task Scheduler to view your scheduled jobs, they live in a different location in the Task Scheduler window than scheduled tasks.
This first screenshot is of the Task Scheduler. The line items are scheduled tasks, not scheduled jobs.
When using the Task Scheduler to manage your scheduled jobs, you need to look in a different location for the scheduled jobs.
If you go back and look at where I showed you how to view scheduled jobs in PowerShell, you'll notice that the command line ignored the scheduled tasks and only returned scheduled jobs. If you manage your scheduled jobs from the command line, one simple cmdlet Get-ScheduledJob gives you your results.
If you decide to manage your jobs in the Task Scheduler GUI, you need to remember it lists scheduled jobs in a different location than the default scheduled tasks, and, yes, I know this is confusing and a bit silly. That's also why I mentioned at the beginning of this article that it is a bad idea to manage your scheduled jobs in the GUI; it's just confusing to keep track of all the caveats when doing so.
I've given you a quite a bit of information to digest, but I'm far from done. Keep an eye out next week for a follow-up article on managing your scheduled jobs and viewing the results of completed jobs.
Please leave me some feedback in the comments section so I can continue to write useful articles. If there is something you'd like to see me write about or if you have a specific question about this or any or my other articles, please feel free to leave me a note as well.
In my next post I will explain how to manage scheduled jobs with PowerShell.
Before you start explaining all kind of great possibilities in your follow up articles (which I in general highly valuate) can you please explain the basics of scheduling without using hashes or variables – and files storing them – for storing all required and optional parameters? I can’t find out the correct syntax for scheduling for instance updating the powershell help doing so this forever, every month, on the first day at 00.30AM but only if idle for 5 minutes, doing so hidden as elevated as system. I’d like to do this using only 1 line!
All the powershell help examples local as well as on-line simply do not dig in deep enough (for me)! I do understand the command New-ScheduledJobOption shows options to configure but how?
I managed to create a simple job like “Register-ScheduledJob -Name “Update Powershell Help” -RunElevated -ScriptBlock { Update-Help }” although deleting and creating the same job again returns the error “The scheduled job definition Update Powershell Help already exists in the job definition store.” Changing and saving the job using the GUI seams to curropt the job store and throws up errors also when running the job afterwards.
Also next command was throwing errors I couldn’t resolve: “Register-ScheduledJob -Name “Update Powershell Help” -StartIfNotIdle (“False”) -Trigger (“Startup”) -Frequency (“Monthly”;”00:02″) -ScriptBlock { Update-Help }”.
I tried for instance “Register-ScheduledJob -Name “Update Powershell Help” -New-JobTrigger -Monthly -At 0:30AM -RunElevated -ScriptBlock { Update-Help }”. This resulted in the error “A parameter cannot be found that matches parameter name ‘New-JobTrigger’.”
Hi Don,
Let me see if I can help you out. You said:
I think your error here is that you are embedding the New-JobTrigger cmd inside the Register-scheduled job cmdlet which is not correct because New-Jobtrigger is not a parameter of the Register-ScheduledJob cmdlet. Also -runelevated is not a parameter of Register-ScheduledJob either.
To set up the trigger and the scheduled job options correctly you do one of the two scenarios (based on your code).
1 – You use the -trigger and -scheduledjoboptions switches like so:
Register-ScheduledJob -Name “Update Powershell Help” -Trigger @{-Monthly -At 0:30AM} -ScheduledJobOption RunElevated -ScriptBlock { Update-Help }”.
2 – You instantiate the trigger and scheduledjoboption switches as separate cmds and save to a variable, and then call the variable:
$trigger = New-JobTrigger -Monthly -at 0:30am
$options = New-ScheduledJobOption – runelevated
Register-ScheduledJob -Name “Update Powershell Help” -Trigger $trigger -ScheduledJoboption $options -ScriptBlock { Update-Help }
Hope that helps! Also for reference, the help file for Register-ScheduledJobOption includes this example, which is an example of how you should write the syntax.
Register-ScheduledJob -FilePath “\\Srv01\Scripts\Update-Version.ps1″ -Trigger @{Frequency=Weekly; At=”9:00PM”; DaysOfWeek=”Monday”;
Interval=2} -ScheduledJobOption @{WakeToRun; StartIfNotIdle; MultipleInstancesPolicy=”Queue”}
Does Register-ScheduledJob require an elevated powershell session to succeed? I can’t seem to get it working without an elevated session, the error message is below:
If it requires an elevated session, perhaps you could add that to your article? For what it’s worth, I can register a scheduled task without an elevated session so I thought I’d save myself the trouble of registering a scheduled task and leverage jobs instead…. until I can do so without admin rights, this is not as useful in my case.
Thanks
Hey Miguel,
You do need to be working in an elevated session, because you are making changes to the machine and without an elevated session, UAC kicks in and stops the process.
I did a little research to confirm this fact and I stumbled across an article from Richard Siddaway which states:
I will mention that detail in my follow-up article which should be released early next week.
FYI, -RepeatIndefinitely was introduced in powershell 4.0.
If 3.0 is installed MS has an example of -RepetitionDuration ([TimeSpan]::MaxValue) to do the same thing on their docs page.
Mike,
Great article! Extremely helpful. I need to pass a parameter to my PS script; how would one accomplish this when creating a Scheduled Job? ex: test.ps1 someparm
I already have this working in Task Scheduler, but, am investigating using PS Scheduled jobs. Long story…..
Thanks in advance,
Joni Fejes
Joni:
I’m not sure where you want to get your parameters from for your scheduled tasks.. but you should be able to add a -argumentlist <someparam> or -argumentlist “<someparam1> <someparam2>” to your Register-ScheduledJob command line.
David F.
Great Article! My job prompts for the user password. is here anyway o ass in password? Also, I get this prompt as well:
Supply values for the following parameters:
Hello:
Hi Tom,
Thank you for the kinds words. You asked about passing in credentials and I am bit confused and need a little more info. Creds come into place in two places here…
Creds for who the job runs as is already covered in the article:
#Define the interval to repeat the job
$trigger = New-JobTrigger -Once -At 9:00AM -RepetitionInterval (New-TimeSpan -Hours 12) -RepeatIndefinitely
#Get user credential so that the job has access to the network
$cred = Get-Credential -UserName bigfirm.biz\someuser
#Set job options
$opt = New-ScheduledJobOption -RunElevated -RequireNetwork
Register-ScheduledJob -Name Get-DCDiskSpace -Trigger $trigger -Credential $cred `
-FilePath c:\scripts\Get-DCDiskSpace.ps1 -MaxResultCount 10 ScheduledJobOption $opt
Can you give me some details on what you’re doing, so I can give you a little bit more targeted help?
Hi,
Can i schedule powershell script which queries MSOL service using powershell jobs.
i tried same with task scheduler however it doesnt work does not matter how many things i have:
passing credential via a secured file.
running with highest privileges and run even if user logged on or off setting.
still same. it shows in history that run is completed however no result.
the same script runs fine if i run it using powershell ISE manually.
please help…
Also, what is the exit code in Task Scheduler, is it 0 as Success or different one?
Ankit, have you run it with start-transcript to catch any errors? I would add a lot of write-verbose -verbose statements to check every step.
David F.
Great article man.
It was very useful to me. Im trying to build an script with the help of chatgpt. Im trying to do a script that prompts username, ad group and a date. With this the script adds the informed user to the informed group and should remove the user from the group when the selected date is reached. Im still working with this last part. Finding some difficult to remove the user when the job start. At the results.xml i can see something like the execution enviroment, or ad status… after reading your tips hopefuly i will reach out what i need.
Nice job man, God bless you!