PowerShell How-To
How To Force PowerShell to Wait for Scheduled Tasks
Learning this handy technique will also allow you to add timers to different types of tasks.
- By Adam Bertram
- 02/09/2017
Most of the time when writing ad-hoc PowerShell scripts, you'll most likely be writing synchronous tasks. These are tasks that run in serial which inherently wait for the previous task to complete before starting up. There's no reason to explicitly force a command to wait because that's just what it inherently does in the first place. However, there are also times when those commands may take a long time and are run asynchronously. Most of the time it's not important to wait on them to run the next command but you'd like the option to do so.
A good example of a possible asynchronous command is Restart-Computer. When invoked with only mandatory parameters, Restart-Computer will invoke a restart but will immediately release control once it's done that. It will not wait around to see if the computer actually restarted. Restart-Computer has a Wait parameter for that. The same can be said for scheduled tasks but, unfortunately, PowerShell doesn't have a built-in method to wait for one. That's what we're here for.
PowerShell has some built-in functions as of Windows 8 and Windows Server 2012 R2 for managing scheduled tasks, but none include a Wait-ScheduledTask command. Granted, a scheduled task is usually going to be executed asynchronously but there are times when it'd be nice just to wait on one. Let's create a PowerShell function to do this.
To create a custom Wait function like this usually means running a Get command over and over again and checking the expected result. In our case, we've got the Get-ScheduledTask command. This is a built-in command that will return the state of the task. Through testing, I've confirmed that when a scheduled task is not running, the state will be Ready and, when running, will be Running. We first need to come up with a line to gather the state.
This line will accomplish that task.
(Get-ScheduledTask -TaskName 'NameHere').State
Rather than running this over and over ourselves and checking to see if the state changes from Running to Ready, let's automate that and create a loop that does it for us. We need to run this loop while the state is not Ready. Let's create a PowerShell while loop to invoke Get-ScheduledTask over and over again until the state is not equal to Ready. This will ensure the function does not return control until the scheduled task is set to Ready.
while ((Get-ScheduledTask -TaskName 'TaskNameHere').State -ne 'Ready') {
Write-Verbose -Message "Waiting on scheduled task..."
}
At its most basic, this command alone would wait until the scheduled task has completed. However, we're not accounting for an important condition. We're not accounting for when, for whatever reason, the scheduled task runs indefinitely. We don't want to just wait on it forever. Let's create a timeout and only wait on it for a specified time. One way to do that is by using the Diagnostics.StopWatch .NET object. This object has a StartNew() and Stop() method which stops a counter and includes an always up-to-date Elapsed property which indicates how much time has passed since StartNew() was invoked.
Using this object, we can start the timer right before we begin to check on the scheduled task and then instead of just checking for the state change, also ensure that the time elapsed did not exceed some threshold.
$timeout = 60 ## seconds
$timer = [Diagnostics.Stopwatch]::StartNew()
while (((Get-ScheduledTask -TaskName 'TaskName').State -ne 'Ready') -and ($timer.Elapsed.TotalSeconds -lt $timeout)) {
Write-Verbose -Message "Waiting on scheduled task..."
Start-Sleep -Seconds 3
}
$timer.Stop()
Write-Verbose -Message "We waited [$($timer.Elapsed.TotalSeconds)] seconds on the task 'TaskName'"
You can see above how this technique could be implemented. We're adding a condition to the loop and checking it after each iteration of the loop as well as the scheduled task's state. Once we're done waiting, it then stops the timer and optionally shows us how long we waited for the scheduled task to complete.
Using this while loop technique and timer can be applied to lots of different tasks as well. Just figure out the code that checks for the condition and implement that into the while loop's condition. This allows you to wait on just about any task!
About the Author
Adam Bertram is a 20-year veteran of IT. He's an automation engineer, blogger, consultant, freelance writer, Pluralsight course author and content marketing advisor to multiple technology companies. Adam also founded the popular TechSnips e-learning platform. He mainly focuses on DevOps, system management and automation technologies, as well as various cloud platforms mostly in the Microsoft space. He is a Microsoft Cloud and Datacenter Management MVP who absorbs knowledge from the IT field and explains it in an easy-to-understand fashion. Catch up on Adam's articles at adamtheautomator.com, connect on LinkedIn or follow him on Twitter at @adbertram or the TechSnips Twitter account @techsnips_io.