Private/Invoke-SCARequest.ps1
|
function Invoke-SCARequest { <# .SYNOPSIS Central REST client used by every public, API-backed psSCA cmdlet. .DESCRIPTION Resolves the target session and URI, attaches auth/diagnostic headers, serializes the request body, and executes the call with retry/backoff for transient failures. Verbose and Debug output is redacted before it is written, and any HTTP failure is translated into a structured error via Resolve-SCAError. Retries apply to GET requests automatically, and to other methods only when -Idempotent is passed, so a transient 503 on a POST that creates an access request is never silently retried into a duplicate. .PARAMETER Session A psSCA.Session object, a session name, or omitted to use the current default session. .PARAMETER Service The CyberArk service that owns the endpoint. .PARAMETER Method HTTP method to use. .PARAMETER Path Endpoint path, which may contain {name} placeholders resolved via -PathParameters. .PARAMETER PathParameters Values to substitute into {name} placeholders in -Path. .PARAMETER QueryParameters Query string parameters. .PARAMETER Body Request body object, serialized to JSON. .PARAMETER Operation Logical operation name (usually the calling cmdlet) used in diagnostics and errors. .PARAMETER TypeName PSTypeName stamped onto the returned object(s). .PARAMETER Idempotent Allows retry-on-transient-failure for non-GET methods that are safe to repeat (e.g. a search endpoint implemented as POST). .OUTPUTS System.Object #> [CmdletBinding()] [OutputType([object])] param( [Parameter()] [AllowNull()] [object]$Session, [Parameter(Mandatory)] [ValidateSet('SCA', 'UAP', 'UAR', 'CDS', 'CEM', 'Compass')] [string]$Service, [Parameter(Mandatory)] [ValidateSet('GET', 'POST', 'PUT', 'PATCH', 'DELETE')] [string]$Method, [Parameter(Mandatory)] [string]$Path, [hashtable]$PathParameters, [hashtable]$QueryParameters, [object]$Body, [Parameter(Mandatory)] [string]$Operation, [string]$TypeName, [switch]$Idempotent ) $resolvedSession = Test-SCASession -Session $Session $uri = Resolve-SCAUri -Session $resolvedSession -Service $Service -Path $Path -PathParameters $PathParameters -QueryParameters $QueryParameters $plainToken = [System.Net.NetworkCredential]::new('', $resolvedSession.AccessToken).Password $headers = @{ Authorization = "Bearer $plainToken" Accept = 'application/json' 'User-Agent' = "psSCA/$script:PSSCAModuleVersion" } $bodyJson = $null if ($null -ne $Body) { $bodyJson = ConvertTo-SCARequestBody -InputObject $Body $headers['Content-Type'] = 'application/json' } Write-Verbose "psSCA: $Method $uri" Write-Debug "psSCA request headers: $((Protect-SCASecret -InputObject $headers) | ConvertTo-Json -Compress)" if ($bodyJson) { Write-Debug "psSCA request body: $(Protect-SCASecret -InputObject $bodyJson)" } $retryableStatusCodes = 429, 502, 503, 504 $maxAttempts = $script:PSSCADefaultRetryCount + 1 $canRetry = ($Method -eq 'GET') -or $Idempotent.IsPresent for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) { try { $requestParams = @{ Uri = $uri Method = $Method Headers = $headers TimeoutSec = $script:PSSCADefaultTimeoutSec UseBasicParsing = $true ErrorAction = 'Stop' } if ($bodyJson) { $requestParams['Body'] = $bodyJson } $response = Invoke-WebRequest @requestParams $parsed = ConvertFrom-SCAResponse -InputObject $response.Content -TypeName $TypeName $morePages = Get-SCAPagination -Response $parsed if ($morePages) { Write-Warning "psSCA: additional results are available for '$Operation' but automatic pagination is not yet implemented for this endpoint. See docs/API-COMPATIBILITY.md." } return $parsed } catch { $statusCode = $null if ($_.Exception -is [Microsoft.PowerShell.Commands.HttpResponseException]) { $statusCode = [int]$_.Exception.Response.StatusCode } $shouldRetry = $canRetry -and ($statusCode -in $retryableStatusCodes) -and ($attempt -lt $maxAttempts) if ($shouldRetry) { $retryAfterHeader = $null if ($_.Exception.Response -and $_.Exception.Response.Headers.RetryAfter) { $retryAfterHeader = $_.Exception.Response.Headers.RetryAfter.Delta.TotalSeconds } $delaySeconds = if ($retryAfterHeader) { $retryAfterHeader } else { [Math]::Pow(2, $attempt - 1) * $script:PSSCADefaultRetryDelaySeconds } Write-Verbose "psSCA: received HTTP $statusCode from $Operation, retrying in $delaySeconds second(s) (attempt $attempt of $maxAttempts)." Start-Sleep -Seconds $delaySeconds continue } $errorRecord = Resolve-SCAError -ErrorRecord $_ -Service $Service -Operation $Operation -Uri $uri $PSCmdlet.ThrowTerminatingError($errorRecord) } } } |