Skip to content

Instantly share code, notes, and snippets.

@deadlydog
Last active December 17, 2023 18:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save deadlydog/620808036d309c8fa2606f32e5ef2f42 to your computer and use it in GitHub Desktop.
Save deadlydog/620808036d309c8fa2606f32e5ef2f42 to your computer and use it in GitHub Desktop.
Powershell function Invoke-ScriptBlockWithRetries to execute arbitrary PowerShell code and automatically retry if an exception is thrown
function Invoke-ScriptBlockWithRetries
{
param
(
[Parameter(Mandatory = $true, HelpMessage = 'The script block to execute.')]
[ValidateNotNullOrEmpty()]
[scriptblock] $ScriptBlock,
[Parameter(Mandatory = $false, HelpMessage = 'The maximum number of times to retry the script block.')]
[int] $MaxNumberOfRetries = 5,
[Parameter(Mandatory = $false, HelpMessage = 'The number of milliseconds to wait between each retry.')]
[int] $MillisecondsBetweenRetries = 5000,
[Parameter(Mandatory = $false, HelpMessage = 'A list of error messages that should not be retried. If any part of the exception or error details match one of these messages, the script block will not be retried.')]
[string[]] $ErrorMessagesToNotRetry = @()
)
[int] $numberOfAttempts = 0
while ($true)
{
try
{
Invoke-Command -ScriptBlock $ScriptBlock
break # Break out of the while loop since the command succeeded.
}
catch
{
$numberOfAttempts++
[string] $errorMessage = $_.Exception.ToString()
[string] $errorDetails = $_.ErrorDetails
Write-Verbose "Script block attempt number '$numberOfAttempts' failed. Will try up to '$($MaxNumberOfRetries + 1)' times.`nException: $errorMessage `nErrorDetails: $errorDetails"
[bool] $isErrorThatShouldBeRetried = $true
foreach ($noRetryMessage in $ErrorMessagesToNotRetry)
{
if ($errorMessage -like "*$noRetryMessage*" -or $errorDetails -like "*$noRetryMessage*")
{
$isErrorThatShouldBeRetried = $false
break # Break out of the foreach loop since we found a match.
}
}
[bool] $shouldRetry = $isErrorThatShouldBeRetried -and ($numberOfAttempts -le $MaxNumberOfRetries)
if ($shouldRetry)
{
Write-Verbose "Waiting $MillisecondsBetweenRetries milliseconds before trying again..."
Start-Sleep -Milliseconds $MillisecondsBetweenRetries
}
else
{
throw
}
}
}
}
Export-ModuleMember -Function Invoke-ScriptBlockWithRetries
# ========== Examples of how to use module from another ps1 file ==========
using module .\PowerShellUtilities.psm1
# === Example 1 - Action that does not return data ===
[scriptblock] $exampleThatDoesNotReturnData = {
Stop-Service -Name "SomeService"
}
Invoke-ScriptBlockWithRetries -ScriptBlock $exampleThatDoesNotReturnData
# === Example 2 - Capturing data returned ===
[scriptblock] $exampleThatReturnsData = {
Invoke-WebRequest -Uri 'https://google.com'
}
$result = Invoke-ScriptBlockWithRetries -ScriptBlock $exampleThatReturnsData -MaxNumberOfRetries 3
if ($result.StatusCode -eq 200)
{
Write-Output "Success"
}
# === Example 3 - Dealing with failures that still occur even with retries ===
[scriptblock] $exampleThatWillAlwaysFail = {
Invoke-WebRequest -Uri 'https://SomeAddressThatDoesNotExist.com'
}
try
{
$result = Invoke-ScriptBlockWithRetries -ScriptBlock $exampleThatWillAlwaysFail
}
catch
{
$exceptionMessage = $_.Exception.Message
Write-Error "The following error occurred: $exceptionMessage"
}
# === Example 4 - Specify messages that should not be retried if the exception contains them ===
[scriptblock] $exampleThatReturnsData = {
Invoke-RestMethod -Uri 'https://google.com'
}
[string[]] $noRetryMessages = @(
'400 (Bad Request)'
'401 (Unauthorized)'
)
Invoke-ScriptBlockWithRetries -ScriptBlock $exampleThatReturnsData -ErrorMessagesToNotRetry $noRetryMessages
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment