Snippet
Retry a rate-limited planning operation
A complete PowerShell script that retries a synthetic operation when it returns retry-after guidance.
AI-generated, human-reviewed: the first draft was generated by an AI agent; I fact-checked, tested, and edited it. AI use policy
What it does#
Wraps a planning operation with retry handling. The sample operation is synthetic and local, but the control flow mirrors what you need around Azure Cost Management calls: honor retry guidance, back off, and fail loudly after the retry budget is spent.
You need#
- PowerShell 7.6.2 or newer
- No Azure modules for this local sample
- For a real Azure collector,
Az.Accounts5.5.0 or newer and permission to read Azure Cost Management data for the target subscriptions
Where it goes#
Save it as Invoke-PlanningOperationWithRetry.ps1. Use the function around
the one line that performs your real API call.
# SPDX-License-Identifier: MIT
# Copyright (c) 2026 Sarang Datye
#Requires -Version 7.4
[CmdletBinding()]
param()
function Invoke-PlanningOperationWithRetry {
param(
[Parameter(Mandatory)]
[scriptblock]$Operation,
[ValidateRange(0, 20)]
[int]$MaxRetries = 5,
[ValidateRange(0, 60)]
[int]$BaseDelaySeconds = 1
)
for ($attempt = 1; $attempt -le ($MaxRetries + 1); $attempt++) {
$result = & $Operation
if ($result.Success) {
return $result.Value
}
$isRetryable = $result.StatusCode -in 429, 503
if (-not $isRetryable -or $attempt -gt $MaxRetries) {
throw "Planning operation failed with status $($result.StatusCode): $($result.Message)"
}
$retryAfterSeconds = 0.0
$hasRetryAfter = $null -ne $result.RetryAfterSeconds -and [double]::TryParse(
[string]$result.RetryAfterSeconds,
[Globalization.NumberStyles]::Float,
[Globalization.CultureInfo]::InvariantCulture,
[ref]$retryAfterSeconds
) -and -not [double]::IsNaN($retryAfterSeconds)
$delaySeconds = if ($hasRetryAfter) {
[Math]::Min(60.0, [Math]::Max(0.0, $retryAfterSeconds))
} else {
[Math]::Min(60.0, [Math]::Pow(2, $attempt) * $BaseDelaySeconds)
}
Write-Verbose "Attempt $attempt was rate-limited. Waiting $delaySeconds seconds."
Start-Sleep -Seconds $delaySeconds
}
}
$script:attempts = 0
$syntheticOperation = {
$script:attempts++
if ($script:attempts -lt 3) {
return [pscustomobject]@{
Success = $false
StatusCode = 429
RetryAfterSeconds = 2 - $script:attempts
Message = 'Synthetic rate limit'
}
}
return [pscustomobject]@{
Success = $true
StatusCode = 200
Value = [pscustomobject]@{
Month = '2026-01'
Cost = 123.45
}
}
}
$value = Invoke-PlanningOperationWithRetry -Operation $syntheticOperation -MaxRetries 2 -Verbose
if ($value.Cost -ne 123.45 -or $script:attempts -ne 3) {
throw 'The retry wrapper did not honor both retry-after responses before succeeding.'
}
$script:attempts = 0
$alwaysRateLimited = {
$script:attempts++
[pscustomobject]@{
Success = $false
StatusCode = 429
RetryAfterSeconds = 0
Message = 'Still rate limited'
}
}
$threw = $false
try {
Invoke-PlanningOperationWithRetry -Operation $alwaysRateLimited -MaxRetries 0
} catch {
$threw = $true
}
if (-not $threw -or $script:attempts -ne 1) {
throw 'MaxRetries 0 should make exactly one attempt and then fail.'
}
$script:attempts = 0
try {
Invoke-PlanningOperationWithRetry -Operation $alwaysRateLimited -MaxRetries 1
throw 'MaxRetries 1 should fail after one retry.'
} catch {
if ($script:attempts -ne 2) {
throw 'MaxRetries 1 should make exactly two attempts.'
}
}
$value | Format-List
Check that it works#
Run:
pwsh -NoProfile -File ./Invoke-PlanningOperationWithRetry.ps1
You should see a synthetic monthly cost after two retries. In a real collector, replace the synthetic operation body with your Azure Cost Management request.