PowerShell Try/Catch and Retry

Viewed 35498

I have a fairly large powershell scripts with many (20+) functions which perform various actions.

Right now all of the code doesn't really have any error handling or retry functionality. If a particular task/function fails it just fails and continues on.

I would like to improve error handling and implement retries to make it more robust.

I was thinking something similar to this:

$tries = 0
while ($tries -lt 5) {
    try{    

       # Do Something

       # No retries necessary
       $tries = 5;
    } catch {
       # Report the error
       # Other error handling
    }
 }

The problem is that I have many many steps where I would need to do this.

I don't think it make sense to implement the above code 20 times. That seems really superfluous.

I was thinking about writing an "TryCatch" function with a single parameter that contains the actual function I want to call?

I'm not sure that's the right approach either though. Won't I end up with a script that reads something like:

TryCatch "Function1 Parameter1 Parameter2"
TryCatch "Function2 Parameter1 Parameter2"
TryCatch "Function3 Parameter1 Parameter2"

Is there a better way to do this?

4 Answers

I adapted @Victor's answer and added:

  • parameter for retries
  • ErrorAction set and restore (or else exceptions do not get caught)
  • exponential backoff delay (I know the OP didn't ask for this, but I use it)
  • got rid of VSCode warnings (i.e. replaced sleep with Start-Sleep)
# [Solution with passing a delegate into a function instead of script block](https://stackoverflow.com/a/47712807/)
function Retry()
{
    param(
        [Parameter(Mandatory=$true)][Action]$action,
        [Parameter(Mandatory=$false)][int]$maxAttempts = 3
    )

    $attempts=1    
    $ErrorActionPreferenceToRestore = $ErrorActionPreference
    $ErrorActionPreference = "Stop"

    do
    {
        try
        {
            $action.Invoke();
            break;
        }
        catch [Exception]
        {
            Write-Host $_.Exception.Message
        }

        # exponential backoff delay
        $attempts++
        if ($attempts -le $maxAttempts) {
            $retryDelaySeconds = [math]::Pow(2, $attempts)
            $retryDelaySeconds = $retryDelaySeconds - 1  # Exponential Backoff Max == (2^n)-1
            Write-Host("Action failed. Waiting " + $retryDelaySeconds + " seconds before attempt " + $attempts + " of " + $maxAttempts + ".")
            Start-Sleep $retryDelaySeconds 
        }
        else {
            $ErrorActionPreference = $ErrorActionPreferenceToRestore
            Write-Error $_.Exception.Message
        }
    } while ($attempts -le $maxAttempts)
    $ErrorActionPreference = $ErrorActionPreferenceToRestore
}

# function MyFunction($inputArg)
# {
#     Throw $inputArg
# }

# #Example of a call:
# Retry({MyFunction "Oh no! It happened again!"})
# Retry {MyFunction "Oh no! It happened again!"} -maxAttempts 10
Related