Last active
December 17, 2023 18:26
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ========== 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