Private/Google/Invoke-GoogleAdminApi.ps1
|
# PSGuerrilla - Jim Tyler, Microsoft MVP - CC BY 4.0 # https://github.com/jimrtyler/PSGuerrilla | https://creativecommons.org/licenses/by/4.0/ # AI/LLM use: see AI-USAGE.md for required attribution function Invoke-GoogleAdminApi { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$AccessToken, [Parameter(Mandatory)] [string]$Uri, [ValidateSet('Get', 'Post', 'Patch', 'Delete')] [string]$Method = 'Get', [hashtable]$Body, [hashtable]$QueryParameters, [int]$MaxRetries = 3, [switch]$Paginate, [string]$ItemsProperty, [switch]$Quiet ) $headers = @{ Authorization = "Bearer $AccessToken" } $allItems = [System.Collections.Generic.List[PSCustomObject]]::new() $pageToken = $null $pageCount = 0 do { # Build query string $params = @{} if ($QueryParameters) { foreach ($key in $QueryParameters.Keys) { $params[$key] = $QueryParameters[$key] } } if ($pageToken) { $params['pageToken'] = $pageToken } $queryString = if ($params.Count -gt 0) { ($params.GetEnumerator() | ForEach-Object { "$($_.Key)=$([System.Uri]::EscapeDataString($_.Value.ToString()))" }) -join '&' } else { '' } $separator = if ($Uri.Contains('?')) { '&' } else { '?' } $fullUri = if ($queryString) { "$Uri$separator$queryString" } else { $Uri } $response = $null for ($attempt = 0; $attempt -lt $MaxRetries; $attempt++) { try { $invokeParams = @{ Uri = $fullUri Headers = $headers Method = $Method ErrorAction = 'Stop' } if ($Body -and $Method -in @('Post', 'Patch')) { $invokeParams['Body'] = ($Body | ConvertTo-Json -Depth 10) $invokeParams['ContentType'] = 'application/json' } $response = Invoke-RestMethod @invokeParams break } catch { $statusCode = $_.Exception.Response.StatusCode.value__ if ($statusCode -in @(429, 503) -and $attempt -lt ($MaxRetries - 1)) { $wait = [Math]::Pow(2, $attempt + 1) Write-Verbose "Rate limited ($statusCode), waiting ${wait}s..." Start-Sleep -Seconds $wait } elseif ($statusCode -eq 400) { $errMsg = $_.ErrorDetails.Message ?? $_.Exception.Message if ($errMsg -match 'Mail service not enabled|FAILED_PRECONDITION') { Write-Verbose "Skipped (service not enabled): $Uri" } else { Write-Warning "API returned 400 for $Uri`: $errMsg" } return $null } elseif ($statusCode -in @(401, 403)) { $msg = $_.ErrorDetails.Message ?? $_.Exception.Message throw "Authentication/authorization failed ($statusCode) for $Uri`. Check service account permissions and domain-wide delegation. $msg" } elseif ($statusCode -eq 404) { Write-Verbose "Resource not found (404) for $Uri" return $null } else { if ($attempt -eq ($MaxRetries - 1)) { Write-Warning "API call failed after $MaxRetries retries for $Uri`: $($_.ErrorDetails.Message ?? $_.Exception.Message)" return $null } $wait = [Math]::Pow(2, $attempt + 1) Start-Sleep -Seconds $wait } } } if (-not $response) { break } if ($Paginate -and $ItemsProperty) { $items = $response.$ItemsProperty if ($items) { foreach ($item in @($items)) { $allItems.Add($item) } } $pageToken = $response.nextPageToken $pageCount++ if (-not $Quiet -and $pageCount % 5 -eq 0) { Write-Verbose "Fetched $pageCount pages, $($allItems.Count) items so far" } } else { # Non-paginated: return raw response return $response } } while ($Paginate -and $pageToken) if ($Paginate) { return @($allItems) } return $response } |