Public/Utility/Invoke-Retry.ps1
<# .SYNOPSIS Retries a powershell command n-times. .DESCRIPTION The cmdlet is capable of retrying a PowerShell command passed as a [ScriptBlock] according to the user defined number of retries and timeout (In Seconds) .PARAMETER TimeoutInSecs Timeout in secods for each retry. .PARAMETER RetryCount Number of times to retry the command. Default value is '3' .PARAMETER ScriptBlock PoweShell command as a ScriptBlock that will be executed and retried in case of Errors. Make sure the script block throws an error when it fails, otherwise the cmdlet won't run the retry logic. .PARAMETER SuccessMessage Message displayed when the command was executed successfuly. .PARAMETER FailureMessage Message displayed when the command was failed to execute. .EXAMPLE Invoke-Retry -ScriptBlock {Test-Connection 'test.com'} -Verbose VERBOSE: [1/3] Failed to Complete the task. Retrying in 30 seconds... VERBOSE: [2/3] Failed to Complete the task. Retrying in 30 seconds... VERBOSE: [3/3] Failed to Complete the task. Retrying in 30 seconds... VERBOSE: Failed to Complete the task! Total retry attempts: 3 VERBOSE: [Error Message] Testing connection to computer 'test.com' failed: Error due to lack of resources Try test connection to the website that doesn't exists, which will throw host not found error. Any error is caught by the Invoke-Retry cmdlet and it will retry to execute test connection 3 more times. By default 3 retry attempts are made at every 30 seconds and you have to explicitly define the 'Verbose' switch to see the retry logic in action. .EXAMPLE Invoke-Retry -ScriptBlock {Get-Service bits | Stop-Service} -TimeoutInSecs 2 -RetryCount 5 -Verbose VERBOSE: [1/5] Failed to Complete the task. Retrying in 2 seconds... VERBOSE: [2/5] Failed to Complete the task. Retrying in 2 seconds... VERBOSE: [3/5] Failed to Complete the task. Retrying in 2 seconds... VERBOSE: [4/5] Failed to Complete the task. Retrying in 2 seconds... VERBOSE: [5/5] Failed to Complete the task. Retrying in 2 seconds... VERBOSE: Failed to Complete the task! Total retry attempts: 5 VERBOSE: [Error Message] Service 'Background Intelligent Transfer Service (bits)' cannot be stopped due to the following error: Cannot open bits service on computer '.'. We can customize the number of retry attempts and timeout times using the parameters: '-RetryCount' and '-TimeoutInSecs' respectively. .EXAMPLE Invoke-Retry -ScriptBlock {Write-Error -Message 'something went wrong!'} -TimeoutInSecs 2 -Verbose VERBOSE: [1/3] Failed to Complete the task. Retrying in 2 seconds... VERBOSE: [2/3] Failed to Complete the task. Retrying in 2 seconds... VERBOSE: [3/3] Failed to Complete the task. Retrying in 2 seconds... VERBOSE: Failed to Complete the task! Total retry attempts: 3 VERBOSE: [Error Message] something went wrong! In some scenarios you would want the retry logic when something fails or you don't get a desired output. In such cases to implement the retry logic, make sure to throw and error in you script block that would be executed .EXAMPLE Invoke-Retry -ScriptBlock { if(2 -eq 2){ throw('Exception occured!') } } -TimeoutInSecs 2 -Verbose VERBOSE: [1/3] Failed to execute the command. Retrying in 2 seconds... VERBOSE: [2/3] Failed to execute the command. Retrying in 2 seconds... VERBOSE: [3/3] Failed to execute the command. Retrying in 2 seconds... VERBOSE: Failed to execute the command! Total retry attempts: 3 VERBOSE: [Error Message] Exception occured! You can even define some conditional statements and throw errors to trigger the retry statments in your program. .EXAMPLE {Test-Connection 'prateeks.cim'},{Write-Host 'hello'} ,{1/0} | Invoke-Retry -TimeoutInSecs 2 -Verbose VERBOSE: [1/3] Failed to execute the command. Retrying in 2 seconds... VERBOSE: [2/3] Failed to execute the command. Retrying in 2 seconds... VERBOSE: [3/3] Failed to execute the command. Retrying in 2 seconds... VERBOSE: Failed to execute the command! Total retry attempts: 3 VERBOSE: [Error Message] Testing connection to computer 'prateeks.cim' failed: No such host is known hello VERBOSE: Command executed successfuly! VERBOSE: [1/3] Failed to execute the command. Retrying in 2 seconds... VERBOSE: [2/3] Failed to execute the command. Retrying in 2 seconds... VERBOSE: [3/3] Failed to execute the command. Retrying in 2 seconds... VERBOSE: Failed to execute the command! Total retry attempts: 3 VERBOSE: [Error Message] Attempted to divide by zero. Capable of handling scriptblock's as input through the pipeline. #> function Invoke-Retry { [CmdletBinding()] param ( [parameter(Mandatory, ValueFromPipeline)] [ValidateNotNullOrEmpty()] [scriptblock] $ScriptBlock, [int] $RetryCount = 10, [int] $TimeoutInSecs = 3, [string] $SuccessMessage = "Command executed successfuly!", [string] $FailureMessage = "Failed to execute the command" ) begin { Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started" } process { $Attempt = 1 $Flag = $true do { try { $PreviousPreference = $ErrorActionPreference $ErrorActionPreference = 'Stop' Invoke-Command -ScriptBlock $ScriptBlock -OutVariable Result $ErrorActionPreference = $PreviousPreference # flow control will execute the next line only if the command in the scriptblock executed without any errors # if an error is thrown, flow control will go to the 'catch' block Write-Verbose "$SuccessMessage `n" $Flag = $false } catch { if ($Attempt -gt $RetryCount) { Write-Verbose "$FailureMessage! Total retry attempts: $RetryCount" Write-Verbose "[Error Message] $($_.exception.message) `n" $Flag = $false } else { Write-Verbose $_.Exception.Message Write-Verbose "[$Attempt/$RetryCount] $FailureMessage. Retrying in $TimeoutInSecs seconds..." Start-Sleep -Seconds $TimeoutInSecs $Attempt = $Attempt + 1 } } } While ($Flag) } end { Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete" } } |