Private/Invoke-InforcerAssessmentRun.ps1
|
function Invoke-InforcerAssessmentRun { <# .SYNOPSIS Runs a single assessment against one tenant (internal helper). .DESCRIPTION Executes POST /beta/tenants/{id}/assessments/{id}/runs asynchronously with progress updates. Returns the raw API response data or $null on error. #> [CmdletBinding()] param( [Parameter(Mandatory)] [int]$ClientTenantId, [Parameter(Mandatory)] [string]$ResolvedAssessmentId, [Parameter(Mandatory)] [string]$TenantDisplayName, [Parameter(Mandatory)] [string]$AssessmentDisplayName ) # Format elapsed time as human-readable (e.g. "1m 23s", "45s") $formatElapsed = { param([double]$totalSeconds) if ($totalSeconds -ge 60) { $mins = [math]::Floor($totalSeconds / 60) $secs = [math]::Round($totalSeconds % 60) return "${mins}m ${secs}s" } return "$([math]::Round($totalSeconds, 1))s" } Write-Host "Running '$AssessmentDisplayName' against $TenantDisplayName..." $endpoint = "/beta/tenants/$ClientTenantId/assessments/$ResolvedAssessmentId/runs" $uri = $script:InforcerSession.BaseUrl + $endpoint $apiKey = ConvertFrom-InforcerSecureString -SecureString $script:InforcerSession.ApiKey $headers = @{ 'Inf-Api-Key' = $apiKey; 'Content-Type' = 'application/json' } $ps = [PowerShell]::Create() $null = $ps.AddScript('param($Uri, $Headers); Invoke-RestMethod -Uri $Uri -Method POST -Headers $Headers') $null = $ps.AddParameter('Uri', $uri) $null = $ps.AddParameter('Headers', $headers) $asyncResult = $ps.BeginInvoke() $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() $lastUpdate = 0 while (-not $asyncResult.IsCompleted) { Start-Sleep -Milliseconds 500 $elapsed = [math]::Floor($stopwatch.Elapsed.TotalSeconds) if ($elapsed -gt 0 -and $elapsed % 10 -eq 0 -and $elapsed -ne $lastUpdate) { Write-Host " Still running... $(& $formatElapsed $elapsed) elapsed" $lastUpdate = $elapsed } } $rawResponse = $null $hasError = $false try { $rawResponse = $ps.EndInvoke($asyncResult) if ($ps.Streams.Error.Count -gt 0) { $hasError = $true foreach ($e in $ps.Streams.Error) { $errMsg = $e.ToString() if ($errMsg -match '<title>([^<]+)</title>') { $errMsg = "API error: $($Matches[1].Trim())" } elseif ($errMsg.Length -gt 500) { $errMsg = $errMsg.Substring(0, 200) + '...' } Write-Error -Message $errMsg -ErrorId 'AssessmentRunFailed' -Category InvalidResult } } } catch { $hasError = $true $errMsg = $_.Exception.Message if ($errMsg -match '<title>([^<]+)</title>') { $errMsg = "API error: $($Matches[1].Trim())" } elseif ($errMsg.Length -gt 500) { $errMsg = $errMsg.Substring(0, 200) + '...' } Write-Error -Message "Assessment run failed: $errMsg" -ErrorId 'AssessmentRunFailed' -Category InvalidResult } finally { $ps.Dispose() } $stopwatch.Stop() $elapsedStr = & $formatElapsed $stopwatch.Elapsed.TotalSeconds if ($hasError) { Write-Host " Failed after $elapsedStr." return $null } Write-Host " Completed in $elapsedStr." # Unwrap response and check for API-level errors $responseObj = if ($rawResponse -and $rawResponse.Count -gt 0) { $rawResponse[0] } else { $null } if ($null -eq $responseObj) { return $null } # Check for success:false (API returned 200 but operation failed) $successProp = $responseObj.PSObject.Properties['success'] if ($successProp -and $successProp.Value -eq $false) { $msgProp = $responseObj.PSObject.Properties['message'] $apiMsg = if ($msgProp) { $msgProp.Value } else { 'Unknown API error' } Write-Error -Message "Assessment API error: $apiMsg" -ErrorId 'AssessmentApiFailed' -Category InvalidResult return $null } $dataProp = $responseObj.PSObject.Properties['data'] if ($dataProp) { return $dataProp.Value } else { return $responseObj } } |