<# .SYNOPSIS Arrow-key bay picker for Wax/Trace Export / Backup / Install scripts. .DESCRIPTION Reads bay-config.csv (asset_tag,ftpak_version,model,user_id) and presents an Up/Down picker. Returns the chosen asset_tag to stdout so the calling script can capture it. Always appends an "Other (manual entry)" option. Same UX as the WinPE select-waxtrace-asset.ps1 used by startnet.cmd. Distinct from that script because it returns the asset tag via stdout rather than writing to a file, and presents the richer per-bay columns (FTPak version + model + USER ID) from bay-config.csv. .PARAMETER ConfigCsv Path to bay-config.csv. Defaults to a sibling file next to this script. .PARAMETER Title Header text shown above the picker. Defaults to "Wax/Trace Asset". .OUTPUTS String. The chosen asset_tag, printed to stdout. Empty string on cancel. .EXAMPLE $asset = & .\Select-WaxtraceAsset.ps1 $asset = & .\Select-WaxtraceAsset.ps1 -ConfigCsv 'C:\foo\bay-config.csv' -Title 'Pick bay to back up' #> [CmdletBinding()] param( [string]$ConfigCsv = (Join-Path $PSScriptRoot 'bay-config.csv'), [string]$Title = 'Wax/Trace Asset' ) $ErrorActionPreference = 'Continue' function Read-BayList { param([string]$Path) if (-not (Test-Path -LiteralPath $Path)) { return @() } try { return Import-Csv -LiteralPath $Path | Sort-Object -Property asset_tag } catch { return @() } } function Show-Menu { param([object[]]$Items, [int]$Selected, [string]$Title) Clear-Host Write-Host '' Write-Host ' ============================================================' Write-Host " $Title" Write-Host ' ============================================================' Write-Host '' Write-Host ' Up / Down arrows = navigate, Enter = select, Esc = cancel' Write-Host '' Write-Host (' {0,-10} {1,-8} {2,-10} {3}' -f 'ASSET', 'FTPAK', 'MODEL', 'USER ID') Write-Host (' {0,-10} {1,-8} {2,-10} {3}' -f '-----', '-----', '-----', '-------') for ($i = 0; $i -lt $Items.Count; $i++) { $item = $Items[$i] if ($item -is [string]) { $line = $item } else { $line = '{0,-10} {1,-8} {2,-10} {3}' -f $item.asset_tag, $item.ftpak_version, $item.model, $item.user_id } if ($i -eq $Selected) { Write-Host (' > ' + $line) -ForegroundColor Black -BackgroundColor White } else { Write-Host (' ' + $line) } } Write-Host '' } $bays = @(Read-BayList -Path $ConfigCsv) $menuItems = @() foreach ($b in $bays) { $menuItems += $b } $menuItems += '** Other (enter asset tag manually) **' if ($menuItems.Count -eq 1) { # Only the manual-entry sentinel: CSV missing or unparseable. Fall back # to a plain prompt rather than wedging the operator in an empty menu. Write-Host " (bay-config.csv not readable at $ConfigCsv - falling back to manual entry)" $manual = Read-Host " Enter asset tag (e.g. WJRP9999) or blank to abort" if ($manual) { return $manual.Trim().ToUpper() } else { return '' } } $sel = 0 while ($true) { Show-Menu -Items $menuItems -Selected $sel -Title $Title $key = [System.Console]::ReadKey($true) switch ($key.Key) { 'UpArrow' { if ($sel -gt 0) { $sel-- } } 'DownArrow' { if ($sel -lt ($menuItems.Count - 1)) { $sel++ } } 'Enter' { if ($sel -eq ($menuItems.Count - 1)) { Write-Host '' $manual = Read-Host ' Enter asset tag (e.g. WJRP9999) or blank to abort' if ($manual) { return $manual.Trim().ToUpper() } else { return '' } } else { return $bays[$sel].asset_tag } } 'Escape' { return '' } } }