Providers/Okta/Private/Invoke-OktaPendingCleanupRequest.ps1
|
function Invoke-OktaPendingCleanupRequest { <# .SYNOPSIS Makes a request that can fail purely because Okta has not finished deleting something .DESCRIPTION Okta deletes schema-bearing objects asynchronously. The DELETE returns success, the object disappears from every listing, and for some seconds afterwards the name is still reserved by a background clean-up job. Re-creating it in that window fails. This matters here more than it would elsewhere, because the module's normal workflow is exactly the thing that triggers it: tear down, then seed again. Both failures were reproduced against a live tenant immediately after a teardown, and both went away on their own: - Custom profile attributes come back as a 400 naming each attribute: "the deletion process for an attribute with the same variable name is incomplete. Wait until the data clean up process finishes and then try again." - A user type comes back as a bare 400 "The request body was not well-formed", which says nothing about the real cause and sends you looking for a malformed payload that is in fact perfectly valid. The identical body succeeds a few seconds later. So this retries on that specific pair of signatures and nothing else. A 400 that means what it says is still an immediate failure - retrying every 400 would turn a genuine bad request into a slow genuine bad request. .PARAMETER Method HTTP method .PARAMETER Path Path below the org URL .PARAMETER Body Request body .PARAMETER MaxWaitSeconds Total time to keep retrying before giving up .OUTPUTS The deserialised response body .EXAMPLE Invoke-OktaPendingCleanupRequest -Method POST -Path '/api/v1/meta/types/user' -Body $body .NOTES Author: Jeffrey Stuhr Version: 1.0.0 Last Updated: 2026-08-07 #> [CmdletBinding()] [OutputType([object])] param( [Parameter(Mandatory = $true)] [ValidateSet('POST', 'PUT')] [string]$Method, [Parameter(Mandatory = $true)] [ValidatePattern('^/')] [string]$Path, [Parameter(Mandatory = $true)] [object]$Body, # Ceiling raised from 300 because a user type name was measured still reserved four # minutes after deletion and free at roughly twenty, so the old ceiling could not # express a wait long enough to cover a teardown-then-re-seed. [Parameter()] [ValidateRange(0, 1200)] [int]$MaxWaitSeconds = 90 ) # Both signatures Okta uses for "still cleaning up". The second is deliberately broad # because Okta gives no better hint for a user type, and it is only ever consulted for an # HTTP 400 on a create. $pendingPattern = 'deletion process|clean up process|was not well-formed' $waited = 0 $delay = 5 while ($true) { try { return Invoke-OktaRequest -Method $Method -Path $Path -Body $Body } catch { $isPending = $_.Exception.Message -match 'HTTP 400' -and $_.Exception.Message -match $pendingPattern if (-not $isPending -or $waited -ge $MaxWaitSeconds) { throw } Write-Warning ("Okta has not finished deleting a previous object of this name. " + "Waiting $delay seconds and retrying ($waited of $MaxWaitSeconds elapsed).") Start-Sleep -Seconds $delay $waited += $delay $delay = [Math]::Min(20, $delay * 2) } } } |