Public/Test-GovernanceAccess.ps1
|
function Test-GovernanceAccess { <# .SYNOPSIS Verifies the PAT referenced by the program manifest carries every scope the engine needs, using side-effect-free probes (reads, plus writes with intentionally invalid payloads that ADO rejects AFTER the scope check — nothing is ever created or changed). Prints one line per scope family and returns a non-terminating error when any scope needed is missing, so CI exits non-zero. `unknown` verdicts (network failure, unexpected status) are reported but do not fail the run. #> [CmdletBinding()] param( [Parameter(Mandatory)][string]$ProgramPath, [string]$Org ) $source = Import-GovernanceSource -ProgramPath $ProgramPath $manifest = $source.Manifest $targetOrg = if ($Org) { $Org } else { $manifest.org } $orgUrl = ConvertTo-AdoOrgUrl -Org $targetOrg $authMode = Initialize-AdoAuth -Manifest $manifest -OrgUrl $orgUrl $projectName = if ($manifest.project -and $manifest.project.name) { $manifest.project.name } else { $manifest.program } $identity = if ($authMode -eq 'entra') { $who = az account show --query user.name --output tsv 2>$null "Entra ($who)" } else { 'PAT' } Write-Host "Checking access for '$projectName' in $orgUrl [auth: $identity]" -ForegroundColor Cyan Write-Host "(read probes + intentionally invalid writes - no changes are made)" -ForegroundColor DarkGray $results = @(Test-AdoAuthScope -OrgUrl $orgUrl -Project $projectName) foreach ($r in $results) { switch ($r.Verdict) { 'ok' { Write-Host (" [OK] {0,-28} {1,-28} needed for: {2}" -f $r.Family, $r.Scope, $r.NeededFor) -ForegroundColor Green } 'missing' { Write-Host (" [MISSING] {0,-28} {1,-28} needed for: {2}" -f $r.Family, $r.Scope, $r.NeededFor) -ForegroundColor Red } default { Write-Host (" [?] {0,-28} {1,-28} probe inconclusive (HTTP {3}); needed for: {2}" -f $r.Family, $r.Scope, $r.NeededFor, $r.StatusCode) -ForegroundColor Yellow } } if ($r.Note) { Write-Host (" note: {0}" -f $r.Note) -ForegroundColor Yellow } } # A missing scope is a finding, not a malfunction — report it as normal # output and return $false so the caller can set the exit code. Throwing # here would dress a successful diagnosis up as a crash. $missing = @($results | Where-Object Verdict -eq 'missing') if ($missing.Count -gt 0) { Write-Host "" Write-Host ("Missing {0} capability(ies): {1}" -f $missing.Count, (($missing | ForEach-Object { $_.Scope }) -join ', ')) -ForegroundColor Red Write-Host "Fix: add the scope to the PAT where selectable; for security writes (not PAT-selectable) sign in with 'az login' using an account with access to this org - see the note on the [MISSING] row." -ForegroundColor Red return $false } Write-Host "All scope probes passed." -ForegroundColor Green return $true } |