Creating A Timeout Feature In Your PowerShell Scripts

September 02, 2018 Security and Compliance, MOVEit

Don't let your entire workflow be hung up because one of the scripts is stuck on some task. There's a way around that!

 

Have you ever written an excellent PowerShell script only to find that it just hangs on some task without any indication why? It's especially frustrating if the script is being kicked off by an unattended process. The entire workflow may be hung up just because one of the scripts is hung on some task. Fortunately, there's a way around this. To prevent a script from hanging indefinitely, we can add a timeout feature to it. Adding a PowerShell timeout feature forces the script to either exit or continue if a certain task is taking too long.

Planning a Timeout Script

To add a timeout feature to a PowerShell script requires a few different tasks:

  • Start a timer
  • Invoke some piece of code
  • Check the code's status every so often
  • If timeout is exceeded, have PowerShell do something
  • If timeout is not exceeded, continue with the script
  • Stop the timer

Let's see an example of how this whole process will work.

Read: Advanced PowerShell Functions: Upping Your Game

Writing the Script

The first thing we need to do is define a timeout. Most often, a timeout in seconds will work. I want to ensure my code doesn't last for more than 10 seconds, so I'll set a variable for that.

$Timeout = 10 ## seconds

Next, I'll need to take whatever code I want to wait on and add it to a scriptblock. For this example, imagine I've created a few background jobs further up the script. I'd like to wait until all of these jobs are completed before continuing.

$jobs = Get-Job
$Condition = {param($jobs) 'Running' -not in $jobs.State }
$ConditionArgs = $jobs

Next, I need to define how long in between checks my script should perform the task.

$RetryInterval = 5 ## seconds

Now I'll start the timer.

## Start the timer
$timer = [Diagnostics.Stopwatch]::StartNew()

 

Now that the timer is started, I can now invoke that piece of code I need to be done.

## Start checking the condition scriptblock. Do this as long as the action hasn't exceeded
## the timeout or the condition scriptblock returns something other than $false or $null.
while (($timer.Elapsed.TotalSeconds -lt $Timeout) -and (& $Condition $ConditionArgs)) {

    ## Wait a specific interval
    Start-Sleep -Seconds $RetryInterval

    ## Check the time
    $totalSecs = [math]::Round($timer.Elapsed.TotalSeconds,0)
    Write-Verbose -Message "Still waiting for action to complete after [$totalSecs] seconds..."
}

Once the timeout has been exceeded, or the task has completed, I'll then need to stop the timer.

## The action either completed or timed out. Stop the timer.
$timer.Stop()

Now I can check to see if the timeout was exceeded or if the task completed on its own. Here, I'm throwing in an exception indicating that the action did not complete if the timeout stopped it. Otherwise, I'm just writing out a verbose statement that any code can follow after that.

## Return status of what happened
if ($timer.Elapsed.TotalSeconds -gt $Timeout) {
    throw 'Action did not complete before timeout period.'
} else {
    Write-Verbose -Message 'Action completed before the timeout period.'
}

For a function called Wait-Action that incorporates all of this functionality into a PowerShell function, download it from the PowerShell Gallery:

Install-Script -Name Wait-Action
 

Adam Bertram

Adam Bertram is a 25+ year IT veteran and an experienced online business professional. He’s a successful blogger, consultant, 6x Microsoft MVP, trainer, published author and freelance writer for dozens of publications. For how-to tech tutorials, catch up with Adam at adamtheautomator.com, connect on LinkedIn or follow him on X at @adbertram.

Read next The PowerShell Script Orchestrator