Using 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
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
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.'
}