Skip to content

Commit 6904e07

Browse files
committed
feat: Enhance project discovery and validation in invoke-adoqr.ps1
1 parent 0aad487 commit 6904e07

1 file changed

Lines changed: 83 additions & 11 deletions

File tree

invoke-adoqr.ps1

Lines changed: 83 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3058,19 +3058,91 @@ Write-Host ""
30583058
# Set default org for az devops commands
30593059
az devops configure --defaults organization=$OrgUrl 2>&1 | Out-Null
30603060

3061-
# Determine projects
3061+
# Validate organization and discover projects in one call.
3062+
# Using Invoke-WebRequest directly so we can distinguish 401/403/404 vs other failures
3063+
# and produce actionable guidance for misspelled org / project names.
3064+
Write-Host "Validating organization and discovering projects..." -ForegroundColor Yellow
3065+
$projectsApi = "$OrgUrl/_apis/projects?api-version=7.1-preview.4&`$top=1000&stateFilter=all"
3066+
$discoveredNames = @()
3067+
try {
3068+
$resp = Invoke-WebRequest -Uri $projectsApi -Headers $header -Method GET -UseBasicParsing -ErrorAction Stop
3069+
$data = $resp.Content | ConvertFrom-Json
3070+
$discoveredNames = @($data.value | ForEach-Object { $_.name } | Sort-Object)
3071+
}
3072+
catch {
3073+
$status = 0
3074+
if ($_.Exception.Response) { try { $status = [int]$_.Exception.Response.StatusCode } catch { $status = 0 } }
3075+
switch ($status) {
3076+
401 {
3077+
Write-Error "Authentication failed (HTTP 401) for '$OrgUrl'. Run 'az login' and confirm the active tenant with 'az account show'."
3078+
return
3079+
}
3080+
403 {
3081+
Write-Error "Access denied (HTTP 403) to organization '$OrgShortName'. The signed-in identity does not have permission to list projects. Ensure it is at least a Project Collection Valid User."
3082+
return
3083+
}
3084+
404 {
3085+
Write-Error @"
3086+
Organization '$OrgShortName' was not found (HTTP 404).
3087+
3088+
Resolved URL: $OrgUrl
3089+
3090+
Check that:
3091+
- The organization name is spelled correctly (you passed: '$Organization')
3092+
- The signed-in account has access (verify with: az account show)
3093+
- If using a legacy *.visualstudio.com URL, pass the full URL form
3094+
"@
3095+
return
3096+
}
3097+
default {
3098+
Write-Error "Failed to query organization '$OrgShortName' at $projectsApi`: $($_.Exception.Message)"
3099+
return
3100+
}
3101+
}
3102+
}
3103+
3104+
if ($discoveredNames.Count -eq 0) {
3105+
Write-Warning "Organization '$OrgShortName' returned no projects. It may be empty, or your account may lack visibility."
3106+
}
3107+
3108+
# Resolve -Project filter against discovered list (case-insensitive, with suggestions)
30623109
if ($Project) {
3063-
$projectNames = $Project
3064-
} else {
3065-
Write-Host "No projects specified — discovering all projects..." -ForegroundColor Yellow
3066-
$allProjects = Invoke-AzCli -Command "devops project list --org $OrgUrl -o json"
3067-
if ($allProjects -and $allProjects.value) {
3068-
$projectNames = @($allProjects.value | ForEach-Object { $_.name })
3069-
Write-Host " Found $($projectNames.Count) project(s): $($projectNames -join ', ')" -ForegroundColor Green
3070-
} else {
3071-
Write-Warning "Could not list projects. Proceeding with org-only assessment."
3072-
$projectNames = @()
3110+
$resolved = New-Object 'System.Collections.Generic.List[string]'
3111+
$unknown = New-Object 'System.Collections.Generic.List[string]'
3112+
foreach ($req in $Project) {
3113+
$match = $discoveredNames | Where-Object { $_ -ieq $req } | Select-Object -First 1
3114+
if ($match) { [void]$resolved.Add($match) } else { [void]$unknown.Add($req) }
3115+
}
3116+
if ($unknown.Count -gt 0) {
3117+
$lines = foreach ($u in $unknown) {
3118+
# Substring contains either direction
3119+
$suggestions = @($discoveredNames | Where-Object { $_ -like "*$u*" -or $u -like "*$_*" } | Select-Object -First 3)
3120+
if ($suggestions.Count -eq 0 -and $u.Length -ge 2) {
3121+
# Fallback: shared prefix (first 2-3 chars)
3122+
$prefix = $u.Substring(0, [math]::Min(3, $u.Length))
3123+
$suggestions = @($discoveredNames | Where-Object { $_ -like "$prefix*" } | Select-Object -First 3)
3124+
}
3125+
if ($suggestions.Count -gt 0) {
3126+
" - '$u' (did you mean: $($suggestions -join ', ')?)"
3127+
} else {
3128+
" - '$u'"
3129+
}
3130+
}
3131+
$availableSample = if ($discoveredNames.Count -le 25) { $discoveredNames -join ', ' } else { ($discoveredNames | Select-Object -First 25) -join ', ' + ", ... (+$($discoveredNames.Count - 25) more)" }
3132+
Write-Error @"
3133+
The following project(s) were not found in organization '$OrgShortName':
3134+
$($lines -join "`n")
3135+
3136+
Available projects ($($discoveredNames.Count)):
3137+
$availableSample
3138+
"@
3139+
return
30733140
}
3141+
$projectNames = @($resolved)
3142+
Write-Host " Validated $($projectNames.Count) project(s): $($projectNames -join ', ')" -ForegroundColor Green
3143+
} else {
3144+
$projectNames = $discoveredNames
3145+
Write-Host " Found $($projectNames.Count) project(s) in '$OrgShortName'." -ForegroundColor Green
30743146
}
30753147
Write-Host ""
30763148

0 commit comments

Comments
 (0)