Workoho.Automation.Azure/Public/Invoke-Auto_AzRestMethod.ps1
<# .SYNOPSIS Wrapper for Invoke-AzRestMethod to add retries for rate limiting and service unavailable errors. .DESCRIPTION This script is a wrapper for the Invoke-AzRestMethod script to add retries in case of rate limiting or service unavailable errors. The script will retry the request up to 5 times with an exponential backoff strategy. The script will also handle the Retry-After header for rate limiting errors. Note that when using batch requests, each response must be checked for rate limiting separately as this script only handles this for the batch request itself. .PARAMETER Params The parameters to pass to the Invoke-AzRestMethod cmdlet using splatting. .OUTPUTS The response from the Azure REST API request. #> function Invoke-Auto_AzRestMethod { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] [hashtable] $Params ) Write-Auto_FunctionBegin $MyInvocation -OnceOnly $maxRetries = 5 $retryCount = 0 $baseWaitTime = 1 # start with 1 second if ($Params.Payload -is [System.Collections.IEnumerable]) { $Params.Payload = $Params.Payload | ConvertTo-Json -Depth 10 } do { try { $response = Az.Accounts\Invoke-AzRestMethod @Params try { if ($response.Content -is [string] -and $response.Content -match '^\s*[\[{]') { if ($PSVersionTable.PSEdition -eq 'Desktop') { $response | Add-Member -NotePropertyName 'Content' -NotePropertyValue $($response.Content | ConvertFrom-Json) -Force } else { $response | Add-Member -NotePropertyName 'Content' -NotePropertyValue $($response.Content | ConvertFrom-Json -Depth 10) -Force } } } catch { Write-Error "Failed to parse response content as JSON: $($_.Exception.Message)" } $rateLimitExceeded = $false } catch { if ($null -eq $_.Exception.Response) { Throw "Network error: $($_.Exception.Message)" } if ($_.Exception.Response.StatusCode -eq 404) { $rateLimitExceeded = $false } elseif ( $_.Exception.Response.StatusCode -eq 429 -or $_.Exception.Response.StatusCode -eq 503 ) { $waitTime = [math]::max($_.Exception.Response.Headers['Retry-After'] -as [int], $baseWaitTime) $jitter = Get-Random -Minimum 0 -Maximum 0.5 # random jitter between 0 and 0.5 seconds, with decimal precision $waitTime += $jitter Clear-Variable -Name response [System.GC]::Collect() [System.GC]::WaitForPendingFinalizers() if ($_.Exception.Response.StatusCode -eq 429) { Write-Verbose "[COMMON]: - Rate limit exceeded, retrying in $waitTime seconds..." } else { Write-Verbose "[COMMON]: - Service unavailable, retrying in $waitTime seconds..." } Start-Sleep -Milliseconds ($waitTime * 1000) # convert wait time to milliseconds for Start-Sleep $retryCount++ $baseWaitTime *= 1.5 # client side exponential backoff $rateLimitExceeded = $true } else { $errorMessage = $_.Exception.Response.Content.ReadAsStringAsync().Result | ConvertFrom-Json Throw "Error $($_.Exception.Response.StatusCode.value__) $($_.Exception.Response.StatusCode): [$($errorMessage.error.code)] $($errorMessage.error.message)" } } } while ($rateLimitExceeded -and $retryCount -lt $maxRetries) if ($rateLimitExceeded) { Throw "Rate limit exceeded after $maxRetries retries." } Write-Auto_FunctionEnd $MyInvocation -OnceOnly return $response } |