#Requires -Version 5.1 <# .SYNOPSIS Validates that Deploy-WinBuild2025.ps1 has correctly configured a guest VM. .DESCRIPTION Connects to the VM via WinRM (HTTP/Basic) and runs all checks that Install-CIToolchain-WinBuild2025.ps1 Steps 1/2/3/4b/5b/5c assert as validation-only. Use this after Deploy completes and before running Prepare/Install-CIToolchain. .PARAMETER VMIPAddress IP address of the guest VM on VMnet8 (e.g. 192.168.79.129). .PARAMETER AdminUsername Admin account inside the VM (default: Administrator). .PARAMETER AdminPassword Password for the admin account. Prompted if omitted. .EXAMPLE .\Validate-DeployState.ps1 -VMIPAddress 192.168.79.129 #> [CmdletBinding()] param( [Parameter(Mandatory)] [string] $VMIPAddress, [string] $AdminUsername = 'Administrator', [string] $AdminPassword ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' if (-not $AdminPassword) { $secure = Read-Host "Password for $AdminUsername@$VMIPAddress" -AsSecureString $AdminPassword = [Runtime.InteropServices.Marshal]::PtrToStringAuto( [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secure)) } $cred = New-Object System.Management.Automation.PSCredential( $AdminUsername, (ConvertTo-SecureString $AdminPassword -AsPlainText -Force) ) $so = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck $prevTrustedHosts = (Get-Item WSMan:\localhost\Client\TrustedHosts).Value try { $cur = (Get-Item WSMan:\localhost\Client\TrustedHosts).Value if ($cur -ne '*' -and $cur -notmatch [regex]::Escape($VMIPAddress)) { $newHosts = if ($cur) { "$cur,$VMIPAddress" } else { $VMIPAddress } Set-Item WSMan:\localhost\Client\TrustedHosts -Value $newHosts -Force } Write-Host "`nValidating Deploy state on $VMIPAddress..." -ForegroundColor Cyan $checks = Invoke-Command -ComputerName $VMIPAddress -Credential $cred ` -Authentication Basic -UseSSL -Port 5986 -SessionOption $so -ScriptBlock { $results = [System.Collections.Generic.List[PSCustomObject]]::new() function Chk { param([string]$Name, [scriptblock]$Test) try { $ok = [bool](& $Test); $err = '' } catch { $ok = $false; $err = $_.Exception.Message } $results.Add([PSCustomObject]@{ Name=$Name; Pass=$ok; Err=$err }) } # ── Firewall ───────────────────────────────────────────────────────── foreach ($p in Get-NetFirewallProfile) { $n = $p.Name; $e = $p.Enabled Chk "Firewall $n disabled" { $e -eq $false } } # ── Defender ───────────────────────────────────────────────────────── Chk 'Defender GPO DisableAntiSpyware=1' { (Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender' ` -Name DisableAntiSpyware -EA Stop).DisableAntiSpyware -eq 1 } # ── WinRM ───────────────────────────────────────────────────────────── Chk 'WinRM service Running' { (Get-Service WinRM -EA Stop).Status -eq 'Running' } Chk 'WinRM service Automatic' { (Get-Service WinRM -EA Stop).StartType -eq 'Automatic' } Chk 'WinRM AllowUnencrypted=false (HTTPS-only)' { (Get-Item WSMan:\localhost\Service\AllowUnencrypted -EA Stop).Value -eq 'false' } Chk 'WinRM Auth/Basic=true' { (Get-Item WSMan:\localhost\Service\Auth\Basic -EA Stop).Value -eq 'true' } Chk 'WinRM MaxMemoryPerShellMB >= 2048' { [int](Get-Item WSMan:\localhost\Shell\MaxMemoryPerShellMB -EA Stop).Value -ge 2048 } # ── UAC ─────────────────────────────────────────────────────────────── $polSys = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' Chk 'UAC EnableLUA=0' { (Get-ItemProperty $polSys -Name EnableLUA -EA Stop).EnableLUA -eq 0 } Chk 'UAC LocalAccountTokenFilterPolicy=1' { (Get-ItemProperty $polSys -Name LocalAccountTokenFilterPolicy -EA Stop).LocalAccountTokenFilterPolicy -eq 1 } # ── Explorer ────────────────────────────────────────────────────────── Chk 'Explorer LaunchTo=1 (HKCU)' { (Get-ItemProperty 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced' ` -Name LaunchTo -EA Stop).LaunchTo -eq 1 } # ── UX hardening ───────────────────────────────────────────────────── Chk 'NoLockScreen=1' { (Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\Personalization' ` -Name NoLockScreen -EA Stop).NoLockScreen -eq 1 } Chk 'Server Manager policy DoNotOpenAtLogon=1' { (Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\CurrentVersion\ServerManager' ` -Name DoNotOpenAtLogon -EA Stop).DoNotOpenAtLogon -eq 1 } Chk 'Server Manager HKLM DoNotOpenServerManagerAtLogon=1' { (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\ServerManager' ` -Name DoNotOpenServerManagerAtLogon -EA Stop).DoNotOpenServerManagerAtLogon -eq 1 } Chk 'Server Manager task Disabled' { $t = Get-ScheduledTask -TaskPath '\Microsoft\Windows\Server Manager\' ` -TaskName 'ServerManager' -EA SilentlyContinue (-not $t) -or ($t.State -eq 'Disabled') } Chk 'Server Manager HKCU DoNotOpenServerManagerAtLogon=1' { (Get-ItemProperty 'HKCU:\SOFTWARE\Microsoft\ServerManager' ` -Name DoNotOpenServerManagerAtLogon -EA Stop).DoNotOpenServerManagerAtLogon -eq 1 } Chk 'DisableCAD=1 (Policies\System)' { (Get-ItemProperty $polSys -Name DisableCAD -EA Stop).DisableCAD -eq 1 } Chk 'OOBE DisablePrivacyExperience=1' { (Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\OOBE' ` -Name DisablePrivacyExperience -EA Stop).DisablePrivacyExperience -eq 1 } Chk 'DataCollection AllowTelemetry=0' { (Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\DataCollection' ` -Name AllowTelemetry -EA Stop).AllowTelemetry -eq 0 } Chk 'AutoAdminLogon=1, DefaultUserName=Administrator' { $wl = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' -EA Stop $wl.AutoAdminLogon -eq '1' -and $wl.DefaultUserName -eq 'Administrator' } # ── Windows Update GPO + services ───────────────────────────────────── Chk 'WU GPO NoAutoUpdate=1' { (Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU' ` -Name NoAutoUpdate -EA Stop).NoAutoUpdate -eq 1 } Chk 'WU GPO DisableWindowsUpdateAccess=1' { (Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' ` -Name DisableWindowsUpdateAccess -EA Stop).DisableWindowsUpdateAccess -eq 1 } Chk 'wuauserv StartType=Manual' { (Get-Service wuauserv -EA Stop).StartType -eq 'Manual' } Chk 'UsoSvc StartType=Manual' { (Get-Service UsoSvc -EA Stop).StartType -eq 'Manual' } return $results.ToArray() } Write-Host '' $failed = 0 foreach ($r in $checks) { if ($r.Pass) { Write-Host " [OK] $($r.Name)" -ForegroundColor Green } else { $detail = if ($r.Err) { " ($($r.Err))" } else { '' } Write-Host " [FAIL] $($r.Name)$detail" -ForegroundColor Red $failed++ } } Write-Host '' if ($failed -eq 0) { Write-Host "All $($checks.Count) Deploy checks passed." -ForegroundColor Green exit 0 } else { Write-Host "$failed / $($checks.Count) Deploy checks FAILED." -ForegroundColor Red exit 1 } } finally { Set-Item WSMan:\localhost\Client\TrustedHosts $prevTrustedHosts -Force -EA SilentlyContinue }