Private/Show-Win32ToolkitTestVM.ps1
|
function Show-Win32ToolkitTestVM { <# .SYNOPSIS Hyper-V test-VM screen (Spectre): set the default backend, provision / reset / remove the VM. Thin front-end over New/Reset/Remove-Win32ToolkitTestVM + the test-backend config. See knowledge-base/designs/hyperv-backend-plan.md. #> [CmdletBinding()] param() while ($true) { Clear-Host Write-SpectreRule -Title 'Hyper-V test VM' -Color Blue $backend = Get-Win32ToolkitConfigValue -Name 'TestBackend' -Default 'Sandbox' Write-SpectreHost "Default test backend: [blue]$backend[/]" $reasons = @(Test-Win32ToolkitHyperVReady) if ($reasons.Count -eq 0) { Write-SpectreHost '[green]Hyper-V backend is READY[/] — VM, checkpoint and guest credential are configured.' } else { Write-SpectreHost "[yellow]Hyper-V backend not ready:[/] $(Get-SpectreEscapedText -Text ($reasons -join '; '))" } # Show the VM's current CPU/RAM (live if it exists, else the configured spec the next provision will use). $vmName = Get-Win32ToolkitConfigValue -Name 'HyperVVMName' -Default 'win32tk-golden' $liveVm = $null; try { $liveVm = Get-VM -Name $vmName -ErrorAction SilentlyContinue } catch { } if ($liveVm) { $hCpu = (Get-VMProcessor -VMName $vmName).Count $hMem = (Get-VMMemory -VMName $vmName).Startup Write-SpectreHost "VM resources: [blue]$hCpu vCPU[/] / [blue]$([math]::Round($hMem / 1GB, 1)) GB[/] [grey]($vmName)[/]" } else { $hCpu = Get-Win32ToolkitConfigValue -Name 'HyperVProcessorCount' -Default '2' $hMem = [uint64](Get-Win32ToolkitConfigValue -Name 'HyperVMemoryStartupBytes' -Default ([uint64]4GB)) Write-SpectreHost "VM resources (next provision): [blue]$hCpu vCPU[/] / [blue]$([math]::Round($hMem / 1GB, 1)) GB[/]" } $sel = Read-SpectreSelection -Message 'Hyper-V test VM' -Choices @( [pscustomobject]@{ Key = 'backend'; Label = 'Set the default test backend (Sandbox / Hyper-V)' } [pscustomobject]@{ Key = 'provision'; Label = 'Provision the test VM from a Windows 11 ISO (one-time, ~30-60 min)' } [pscustomobject]@{ Key = 'resources'; Label = 'Change VM resources (CPU / memory) — reconfigures + re-checkpoints (minutes)' } [pscustomobject]@{ Key = 'reset'; Label = 'Reset the VM to its clean checkpoint' } [pscustomobject]@{ Key = 'autologon'; Label = 'Configure guest AutoLogon + re-checkpoint (fix a login-screen checkpoint)' } [pscustomobject]@{ Key = 'remove'; Label = 'Remove the test VM (and its VHDX)' } [pscustomobject]@{ Key = 'back'; Label = 'Back' } ) -ChoiceLabelProperty 'Label' -Color Blue -PageSize 10 switch ($sel.Key) { 'backend' { $b = Read-SpectreSelection -Message 'Default test backend' -Choices @( [pscustomobject]@{ Key = 'Sandbox'; Label = 'Windows Sandbox (default)' } [pscustomobject]@{ Key = 'HyperV'; Label = 'Hyper-V VM (fast; falls back to Sandbox if not ready)' } ) -ChoiceLabelProperty 'Label' -Color Blue Set-Win32ToolkitConfigValue -Name 'TestBackend' -Value $b.Key Write-SpectreHost "[green]Default backend set to $($b.Key).[/]" Read-SpectrePause -Message 'Press any key to continue' -AnyKey | Out-Null } 'provision' { $iso = Read-SpectreText -Message 'Path to a Windows 11 x64 ISO (blank to cancel)' -DefaultAnswer '' if (-not [string]::IsNullOrWhiteSpace($iso)) { # Detect a leftover VM / golden VHDX from a previous build. Without this, the underlying # New-Win32ToolkitGoldenVhdx throws "VHDX already exists (use -Force)" the instant it starts # — the TUI never passed -Force — so a repeat provisioning attempt appeared to "do nothing" # (it errored before any progress output). Offer a rebuild instead of dead-ending. $vmName = Get-Win32ToolkitConfigValue -Name 'HyperVVMName' -Default 'win32tk-golden' $vhdxPath = Join-Path (Get-Win32ToolkitHyperVPaths -BasePath (Get-Win32ToolkitBasePath)).Golden "$vmName.vhdx" $existingVhdx = Test-Path -LiteralPath $vhdxPath $existingVm = $false try { $existingVm = [bool](Get-VM -Name $vmName -ErrorAction SilentlyContinue) } catch { } $proceed = $true $force = $false if ($existingVm -or $existingVhdx) { $what = @(); if ($existingVm) { $what += "VM '$vmName'" }; if ($existingVhdx) { $what += 'golden VHDX' } Write-SpectreHost "[yellow]An existing $($what -join ' + ') from a previous build was found.[/]" Write-SpectreHost '[grey]Provisioning cannot proceed without rebuilding it — this is why a second attempt looked like it did nothing.[/]' if (Read-SpectreConfirm -Message 'Delete it and rebuild from scratch?' -DefaultAnswer 'y') { $force = $true } else { $proceed = $false Write-SpectreHost '[grey]Cancelled — nothing changed.[/]' Read-SpectrePause -Message 'Press any key to continue' -AnyKey | Out-Null } } if ($proceed) { Clear-Host Write-SpectreRule -Title 'Provisioning the Hyper-V test VM…' -Color Blue Write-SpectreHost '[yellow]Requires an elevated session. You will be prompted for a guest admin password — it must NOT be blank.[/]' Write-SpectreHost '[grey]This runs once and takes ~30-60 minutes (ISO -> VHDX -> VM -> warm checkpoint).[/]' Write-SpectreHost '[grey]When the VM is up, its console opens and provisioning PAUSES: sign in, run Windows Update, let all reboots finish, then press Enter to confirm. The clean-base checkpoint captures that fully-patched desktop — every test run reverts to it.[/]' try { $newArgs = @{ IsoPath = $iso; Verbose = $true } if ($force) { $newArgs['Force'] = $true } New-Win32ToolkitTestVM @newArgs | Out-Null Format-SpectrePanel -Data 'Test VM provisioned and checkpointed. The default backend can now be Hyper-V.' -Header 'Done' -Border Rounded -Color Green | Out-SpectreHost } catch { Format-SpectrePanel -Data "[red]$(Get-SpectreEscapedText -Text $_.Exception.Message)[/]" -Header 'Error' -Border Rounded -Color Red | Out-SpectreHost } Read-SpectrePause -Message 'Press any key to continue' -AnyKey | Out-Null } } } 'resources' { Clear-Host; Write-SpectreRule -Title 'VM resources (CPU / memory)' -Color Blue $rvm = $null; try { $rvm = Get-VM -Name $vmName -ErrorAction SilentlyContinue } catch { } if (-not $rvm) { Format-SpectrePanel -Data "No test VM ('$vmName') is provisioned yet — provision it first." -Header 'No VM' -Border Rounded -Color Yellow | Out-SpectreHost Read-SpectrePause -Message 'Press any key to continue' -AnyKey | Out-Null } else { $curCpu = (Get-VMProcessor -VMName $vmName).Count $curMem = (Get-VMMemory -VMName $vmName).Startup $vmHost = Get-VMHost Write-SpectreHost "Current: [blue]$curCpu vCPU[/] / [blue]$([math]::Round($curMem / 1GB, 2)) GB[/]" Write-SpectreHost "[grey]Host capacity: $($vmHost.LogicalProcessorCount) logical processors / $([math]::Round($vmHost.MemoryCapacity / 1GB, 1)) GB RAM (requests above this are refused).[/]" $cpuIn = Read-SpectreText -Message "New vCPU count (blank = keep $curCpu)" -DefaultAnswer '' $memIn = Read-SpectreText -Message "New memory, e.g. 6GB or 6144MB (blank = keep $([math]::Round($curMem / 1GB, 2)) GB)" -DefaultAnswer '' $bad = $false $rargs = @{} if (-not [string]::IsNullOrWhiteSpace($cpuIn)) { $pc = 0 if ([int]::TryParse($cpuIn.Trim(), [ref]$pc) -and $pc -ge 1) { $rargs['ProcessorCount'] = $pc } else { Write-SpectreHost "[red]Invalid vCPU count: '$(Get-SpectreEscapedText -Text $cpuIn)'.[/]"; $bad = $true } } if (-not [string]::IsNullOrWhiteSpace($memIn)) { $bytes = ConvertTo-Win32ToolkitByteSize -Text $memIn if ($bytes) { $rargs['MemoryStartupBytes'] = $bytes } else { Write-SpectreHost "[red]Invalid memory: '$(Get-SpectreEscapedText -Text $memIn)' — try 6GB or 6144MB.[/]"; $bad = $true } } if ($bad) { Read-SpectrePause -Message 'Press any key to continue' -AnyKey | Out-Null } elseif ($rargs.Count -eq 0) { Write-SpectreHost '[grey]No changes entered — nothing to do.[/]' Read-SpectrePause -Message 'Press any key to continue' -AnyKey | Out-Null } else { Write-SpectreHost '[yellow]This turns the VM OFF, applies the change, cold-boots it to the AutoLogon desktop, and re-takes the clean-base checkpoint (a few minutes).[/]' if (Read-SpectreConfirm -Message 'Apply now?' -DefaultAnswer 'y') { Clear-Host; Write-SpectreRule -Title 'Reconfiguring the VM…' -Color Blue try { Set-Win32ToolkitTestVMResource @rargs | Out-Null Format-SpectrePanel -Data 'VM resources updated and the clean-base checkpoint recreated.' -Header 'Done' -Border Rounded -Color Green | Out-SpectreHost } catch { Format-SpectrePanel -Data "[red]$(Get-SpectreEscapedText -Text $_.Exception.Message)[/]" -Header 'Error' -Border Rounded -Color Red | Out-SpectreHost } Read-SpectrePause -Message 'Press any key to continue' -AnyKey | Out-Null } } } } 'reset' { Clear-Host; Write-SpectreRule -Title 'Reverting to clean-base…' -Color Blue try { Reset-Win32ToolkitTestVM Format-SpectrePanel -Data 'VM reverted to the clean checkpoint.' -Header 'Done' -Border Rounded -Color Green | Out-SpectreHost } catch { Format-SpectrePanel -Data "[red]$(Get-SpectreEscapedText -Text $_.Exception.Message)[/]" -Header 'Error' -Border Rounded -Color Red | Out-SpectreHost } Read-SpectrePause -Message 'Press any key to continue' -AnyKey | Out-Null } 'remove' { if (Read-SpectreConfirm -Message 'Remove the test VM and delete its VHDX?' -DefaultAnswer 'n') { Clear-Host; Write-SpectreRule -Title 'Removing the test VM…' -Color Blue try { Remove-Win32ToolkitTestVM -RemoveVhdx Format-SpectrePanel -Data 'Test VM removed.' -Header 'Done' -Border Rounded -Color Green | Out-SpectreHost } catch { Format-SpectrePanel -Data "[red]$(Get-SpectreEscapedText -Text $_.Exception.Message)[/]" -Header 'Error' -Border Rounded -Color Red | Out-SpectreHost } Read-SpectrePause -Message 'Press any key to continue' -AnyKey | Out-Null } } 'autologon' { Clear-Host; Write-SpectreRule -Title 'Configuring guest AutoLogon + re-checkpoint…' -Color Blue try { $vm = Get-Win32ToolkitConfigValue -Name 'HyperVVMName' -Default 'win32tk-golden' $cp = Get-Win32ToolkitConfigValue -Name 'HyperVCheckpoint' -Default 'clean-base' $gc = Get-Win32ToolkitGuestCredential if (-not $gc) { throw 'No guest credential is configured — provision the VM first.' } Reset-Win32ToolkitTestVM Set-Win32ToolkitGuestAutoLogon -VMName $vm -Credential $gc if (Confirm-Win32ToolkitGuestDesktop -VMName $vm -Credential $gc) { Get-VMCheckpoint -VMName $vm -ErrorAction SilentlyContinue | Remove-VMCheckpoint -ErrorAction SilentlyContinue Set-VM -Name $vm -CheckpointType Standard Checkpoint-VM -VMName $vm -SnapshotName $cp Format-SpectrePanel -Data 'AutoLogon configured and the checkpoint re-taken at a logged-in desktop. Interactive GUI testing is now safe.' -Header 'Done' -Border Rounded -Color Green | Out-SpectreHost } else { Format-SpectrePanel -Data 'Could not reach a desktop to re-checkpoint. Log in once in the VM window, then run this again.' -Header 'Warning' -Border Rounded -Color Yellow | Out-SpectreHost } } catch { Format-SpectrePanel -Data "[red]$(Get-SpectreEscapedText -Text $_.Exception.Message)[/]" -Header 'Error' -Border Rounded -Color Red | Out-SpectreHost } Read-SpectrePause -Message 'Press any key to continue' -AnyKey | Out-Null } 'back' { return } } } } |