Private/Get-AACRetryDelay.ps1

function Get-AACRetryDelay {
    <#
    .SYNOPSIS
        How many seconds to wait before retrying a throttled (429), failed
        (5xx) or dropped (no response) Azure call.
    .DESCRIPTION
        The longest wait any header asks for: Retry-After (seconds or a
        date), Cost Management's x-ms-ratelimit-...-retry-after, Resource
        Graph's x-ms-user-quota-resets-after (hh:mm:ss). Without one: a
        dropped connection waits -Attempt seconds, anything else 5 x
        -Attempt. Never more than 60 seconds.
    #>

    [CmdletBinding()]
    [OutputType([double])]
    param(
        [System.Net.Http.HttpResponseMessage] $Response,

        [int] $Status,

        [int] $Attempt = 1
    )

    $wait = 0.0
    if ($Response) {
        $retryAfter = $Response.Headers.RetryAfter
        # (PowerShell unwraps the nullable Delta and Date.)
        if ($retryAfter -and $null -ne $retryAfter.Delta) { $wait = ([TimeSpan]$retryAfter.Delta).TotalSeconds }
        elseif ($retryAfter -and $null -ne $retryAfter.Date) { $wait = (([datetimeoffset]$retryAfter.Date).UtcDateTime - [datetime]::UtcNow).TotalSeconds }
        foreach ($header in $Response.Headers) {
            foreach ($value in $header.Value) {
                $seconds = 0
                $span = [TimeSpan]::Zero
                if ($header.Key -match 'retry-after$' -and [int]::TryParse($value, [ref]$seconds)) { $wait = [Math]::Max($wait, $seconds) }
                elseif ($header.Key -match 'resets-after$' -and [TimeSpan]::TryParse($value, [cultureinfo]::InvariantCulture, [ref]$span)) { $wait = [Math]::Max($wait, $span.TotalSeconds) }
            }
        }
    }
    if ($wait -le 0) { $wait = if ($Status -eq 0) { $Attempt } else { 5 * $Attempt } }
    [Math]::Min(60, [Math]::Max(1, $wait))
}