Block Retry using Powershell
Last Updated: Feb 06, 2015
I’ve been doing a lot of Powershell scripting lately, and one of the features I’ve really been pining for is the ability to apply some form of retry logic to either a function or a block.
Take the following sample:
1 2 3 4 5 6 7 8 |
|
Depending on what the random number we get is, we’ll get one of two scenarios:
# Success
RandomlyFail
W00t!!!
# Failure
RandomlyFail
OH NOES!!!
At line:62 char:9
+ throw "OH NOES!!!"
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (OH NOES!!!:String) [], RuntimeException
+ FullyQualifiedErrorId : OH NOES!!!
Now, if this happened to be part of a larger script and we didn’t have everything wrapped in a try..catch
block, execution could potentially stop there.
Since Powershell supports closures, we can write a function that accepts a script block as a parameter.
The operation retried a few times on failure, and if the maximum threshold is surpassed, the operation fails completely.
Params: Command - The ScriptBlock to be executed RetryDelay - Number (in seconds) to wait between retries (default: 5) MaxRetries - Number of times to retry before accepting failure (default: 5) VerboseOutput - More info about internal processing (default: false) Examples:
Execute-With-Retry { $connection.Open() } $result = Execute-With-Retry -RetryDelay 1 -MaxRetries 2 { $command.ExecuteReader() }
>
function Execute-With-Retry {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline,Mandatory)]
$Command,
$RetryDelay = 5,
$MaxRetries = 5,
$VerboseOutput = $false
)
$currentRetry = 0
$success = $false
$cmd = $Command.ToString()
do {
try
{
$result = & $Command
$success = $true
if ($VerboseOutput -eq $true) {
$Host.UI.WriteDebugLine("Successfully executed [$cmd]")
}
return $result
}
catch [System.Exception]
{
$currentRetry = $currentRetry + 1
if ($VerboseOutput -eq $true) {
$Host.UI.WriteErrorLine("Failed to execute [$cmd]: " + $_.Exception.Message)
}
if ($currentRetry -gt $MaxRetries) {
throw "Could not execute [$cmd]. The error: " + $_.Exception.ToString()
} else {
if ($VerboseOutput -eq $true) {
$Host.UI.WriteDebugLine("Waiting $RetryDelay second(s) before attempt #$currentRetry of [$cmd]")
}
Start-Sleep -s $RetryDelay
}
}
} while (!$success);
}
Now, if we retrofit our sample above:
1
|
|
Failed to execute [ RandomlyFail ]: OH NOES!!!
DEBUG: Waiting 1 second(s) before attempt #1 of [ RandomlyFail ]
Failed to execute [ RandomlyFail ]: OH NOES!!!
DEBUG: Waiting 1 second(s) before attempt #2 of [ RandomlyFail ]
Failed to execute [ RandomlyFail ]: OH NOES!!!
DEBUG: Waiting 1 second(s) before attempt #3 of [ RandomlyFail ]
Failed to execute [ RandomlyFail ]: OH NOES!!!
DEBUG: Waiting 1 second(s) before attempt #4 of [ RandomlyFail ]
W00t!!!
DEBUG: Successfully executed [ RandomlyFail ]
The inspiration for this comes from an excellent article by Pawel Pabich.