fix(template): §7.4 Validate-SetupState -Remediate flag + Setup/Deploy tweaks
This commit is contained in:
@@ -198,7 +198,7 @@ Final Pre-snapshot gate — 7 check cross-cutting + no installer leftover
|
|||||||
| 4b | Assert | EnableLUA=0, LocalAccountTokenFilterPolicy=1 |
|
| 4b | Assert | EnableLUA=0, LocalAccountTokenFilterPolicy=1 |
|
||||||
| 5 | Operativo | Ogni dir Test-Path -PathType Container |
|
| 5 | Operativo | Ogni dir Test-Path -PathType Container |
|
||||||
| 5b | Assert | HKCU LaunchTo=1 |
|
| 5b | Assert | HKCU LaunchTo=1 |
|
||||||
| 5c | Assert | NoLockScreen=1, SM policy DoNotOpenAtLogon=1, SM task Disabled, DisableCAD=1, OOBE DisablePrivacyExperience=1, AllowTelemetry=0, AutoAdminLogon=1 + DefaultUserName=Administrator |
|
| 5c | Assert+Op | NoLockScreen=1, SM policy DoNotOpenAtLogon=1, SM task Disabled, DisableCAD=1, OOBE DisablePrivacyExperience=1, AllowTelemetry=0; AutoAdminLogon=1+DefaultUserName=Administrator re-affermati se assenti (Windows OOBE può resettarli al primo boot) |
|
||||||
| 6 | Operativo | ResultCode ∈ {0,2,3}, no reboot required (else exit 3010) |
|
| 6 | Operativo | ResultCode ∈ {0,2,3}, no reboot required (else exit 3010) |
|
||||||
| 6b | Operativo | wuauserv StartType=Disabled, NoAutoUpdate=1, DisableWindowsUpdateAccess=1 |
|
| 6b | Operativo | wuauserv StartType=Disabled, NoAutoUpdate=1, DisableWindowsUpdateAccess=1 |
|
||||||
| 7 | Operativo | `dotnet --version` channel match, machine PATH contiene `C:\dotnet` |
|
| 7 | Operativo | `dotnet --version` channel match, machine PATH contiene `C:\dotnet` |
|
||||||
|
|||||||
@@ -1110,4 +1110,8 @@ Write-Host " RDP : mstsc /v:$guestIP"
|
|||||||
Write-Host " WinRM HTTP : 5985"
|
Write-Host " WinRM HTTP : 5985"
|
||||||
Write-Host " WinRM HTTPS : 5986 (self-signed)"
|
Write-Host " WinRM HTTPS : 5986 (self-signed)"
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
Write-Host " VM is powered on with vmxnet3 + DHCP. Live snapshot taken." -ForegroundColor Green
|
Write-Host " Next steps (from template\ dir):"
|
||||||
|
Write-Host " .\Validate-DeployState.ps1 -VMIPAddress $guestIP"
|
||||||
|
Write-Host " .\Prepare-WinBuild2025.ps1 -VMIPAddress $guestIP [-SkipWindowsUpdate]"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host " VM is powered on with vmxnet3 + DHCP. Snapshot '$SnapshotName' taken." -ForegroundColor Green
|
||||||
|
|||||||
@@ -380,6 +380,7 @@ try {
|
|||||||
BuildPassword = $BuildPassword
|
BuildPassword = $BuildPassword
|
||||||
BuildUsername = $BuildUsername
|
BuildUsername = $BuildUsername
|
||||||
DotNetSdkVersion = $DotNetSdkVersion
|
DotNetSdkVersion = $DotNetSdkVersion
|
||||||
|
AdminPassword = $AdminPassword
|
||||||
}
|
}
|
||||||
if ($SkipWindowsUpdate) { $setupArgs['SkipWindowsUpdate'] = $true }
|
if ($SkipWindowsUpdate) { $setupArgs['SkipWindowsUpdate'] = $true }
|
||||||
if ($SkipCleanup) { $setupArgs['SkipCleanup'] = $true }
|
if ($SkipCleanup) { $setupArgs['SkipCleanup'] = $true }
|
||||||
|
|||||||
@@ -131,7 +131,11 @@ param(
|
|||||||
[switch] $SkipWindowsUpdate,
|
[switch] $SkipWindowsUpdate,
|
||||||
|
|
||||||
# Skip disk cleanup step (cleanmgr + DISM + cache clear) — speeds up re-runs during dev/debug
|
# Skip disk cleanup step (cleanmgr + DISM + cache clear) — speeds up re-runs during dev/debug
|
||||||
[switch] $SkipCleanup
|
[switch] $SkipCleanup,
|
||||||
|
|
||||||
|
# Administrator password — used only to write DefaultPassword for autologin.
|
||||||
|
# Optional: if empty, DefaultPassword registry value is left as-is (Deploy may have set it).
|
||||||
|
[string] $AdminPassword = ''
|
||||||
)
|
)
|
||||||
|
|
||||||
Set-StrictMode -Version Latest
|
Set-StrictMode -Version Latest
|
||||||
@@ -360,8 +364,23 @@ Assert-Step 'CIUX' 'OOBE policy DisablePrivacyExperience=1' {
|
|||||||
Assert-Step 'CIUX' 'DataCollection AllowTelemetry=0' {
|
Assert-Step 'CIUX' 'DataCollection AllowTelemetry=0' {
|
||||||
(Get-ItemProperty -Path $dcKey -Name AllowTelemetry -ErrorAction Stop).AllowTelemetry -eq 0
|
(Get-ItemProperty -Path $dcKey -Name AllowTelemetry -ErrorAction Stop).AllowTelemetry -eq 0
|
||||||
}
|
}
|
||||||
|
# AutoAdminLogon: operativo — Deploy sets this, but Windows Server 2025 OOBE/first-boot
|
||||||
|
# tasks can reset AutoAdminLogon to 0 after the initial login. Re-affirm without touching
|
||||||
|
# DefaultPassword (already written by Deploy's post-install.ps1 and still present).
|
||||||
|
$wlKey = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon'
|
||||||
|
$wlNow = Get-ItemProperty -Path $wlKey -ErrorAction SilentlyContinue
|
||||||
|
if ($wlNow.AutoAdminLogon -ne '1' -or $wlNow.DefaultUserName -ne 'Administrator') {
|
||||||
|
Set-ItemProperty -Path $wlKey -Name AutoAdminLogon -Value '1' -Type String -Force
|
||||||
|
Set-ItemProperty -Path $wlKey -Name DefaultUserName -Value 'Administrator' -Type String -Force
|
||||||
|
Set-ItemProperty -Path $wlKey -Name DefaultDomainName -Value '.' -Type String -Force
|
||||||
|
}
|
||||||
|
# DefaultPassword written unconditionally when available — previous run may have set
|
||||||
|
# AutoAdminLogon=1 without knowing the password (AdminPassword was added later).
|
||||||
|
if ($AdminPassword -ne '') {
|
||||||
|
Set-ItemProperty -Path $wlKey -Name DefaultPassword -Value $AdminPassword -Type String -Force
|
||||||
|
}
|
||||||
Assert-Step 'CIUX' 'autologin Administrator enabled (AutoAdminLogon=1)' {
|
Assert-Step 'CIUX' 'autologin Administrator enabled (AutoAdminLogon=1)' {
|
||||||
$wl = Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' -ErrorAction Stop
|
$wl = Get-ItemProperty -Path $wlKey -ErrorAction Stop
|
||||||
$wl.AutoAdminLogon -eq '1' -and $wl.DefaultUserName -eq 'Administrator'
|
$wl.AutoAdminLogon -eq '1' -and $wl.DefaultUserName -eq 'Administrator'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,8 @@ try {
|
|||||||
Set-Item WSMan:\localhost\Client\AllowUnencrypted $true -Force
|
Set-Item WSMan:\localhost\Client\AllowUnencrypted $true -Force
|
||||||
$cur = (Get-Item WSMan:\localhost\Client\TrustedHosts).Value
|
$cur = (Get-Item WSMan:\localhost\Client\TrustedHosts).Value
|
||||||
if ($cur -ne '*' -and $cur -notmatch [regex]::Escape($VMIPAddress)) {
|
if ($cur -ne '*' -and $cur -notmatch [regex]::Escape($VMIPAddress)) {
|
||||||
Set-Item WSMan:\localhost\Client\TrustedHosts -Value ($cur ? "$cur,$VMIPAddress" : $VMIPAddress) -Force
|
$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
|
Write-Host "`nValidating Deploy state on $VMIPAddress..." -ForegroundColor Cyan
|
||||||
|
|||||||
@@ -27,8 +27,15 @@
|
|||||||
.PARAMETER PythonVersion
|
.PARAMETER PythonVersion
|
||||||
Expected exact Python version string (default: 3.13.3).
|
Expected exact Python version string (default: 3.13.3).
|
||||||
|
|
||||||
|
.PARAMETER Remediate
|
||||||
|
If set, automatically applies fixes for known remediable failures
|
||||||
|
(AutoAdminLogon, wuauserv/UsoSvc StartType=Disabled) then re-runs all checks.
|
||||||
|
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
.\Validate-SetupState.ps1 -VMIPAddress 192.168.79.129
|
.\Validate-SetupState.ps1 -VMIPAddress 192.168.79.129
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
.\Validate-SetupState.ps1 -VMIPAddress 192.168.79.129 -Remediate
|
||||||
#>
|
#>
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param(
|
param(
|
||||||
@@ -37,7 +44,8 @@ param(
|
|||||||
[string] $AdminPassword,
|
[string] $AdminPassword,
|
||||||
[string] $BuildUsername = 'ci_build',
|
[string] $BuildUsername = 'ci_build',
|
||||||
[string] $DotNetChannel = '10.0',
|
[string] $DotNetChannel = '10.0',
|
||||||
[string] $PythonVersion = '3.13.3'
|
[string] $PythonVersion = '3.13.3',
|
||||||
|
[switch] $Remediate
|
||||||
)
|
)
|
||||||
|
|
||||||
Set-StrictMode -Version Latest
|
Set-StrictMode -Version Latest
|
||||||
@@ -62,7 +70,8 @@ try {
|
|||||||
Set-Item WSMan:\localhost\Client\AllowUnencrypted $true -Force
|
Set-Item WSMan:\localhost\Client\AllowUnencrypted $true -Force
|
||||||
$cur = (Get-Item WSMan:\localhost\Client\TrustedHosts).Value
|
$cur = (Get-Item WSMan:\localhost\Client\TrustedHosts).Value
|
||||||
if ($cur -ne '*' -and $cur -notmatch [regex]::Escape($VMIPAddress)) {
|
if ($cur -ne '*' -and $cur -notmatch [regex]::Escape($VMIPAddress)) {
|
||||||
Set-Item WSMan:\localhost\Client\TrustedHosts -Value ($cur ? "$cur,$VMIPAddress" : $VMIPAddress) -Force
|
$newHosts = if ($cur) { "$cur,$VMIPAddress" } else { $VMIPAddress }
|
||||||
|
Set-Item WSMan:\localhost\Client\TrustedHosts -Value $newHosts -Force
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "`nValidating full Setup state on $VMIPAddress..." -ForegroundColor Cyan
|
Write-Host "`nValidating full Setup state on $VMIPAddress..." -ForegroundColor Cyan
|
||||||
@@ -216,7 +225,7 @@ try {
|
|||||||
# No leftover installer files in C:\CI root
|
# No leftover installer files in C:\CI root
|
||||||
Chk 'no leftover installer scripts in C:\CI' {
|
Chk 'no leftover installer scripts in C:\CI' {
|
||||||
$leftovers = Get-ChildItem 'C:\CI' -File -EA SilentlyContinue |
|
$leftovers = Get-ChildItem 'C:\CI' -File -EA SilentlyContinue |
|
||||||
Where-Object { $_.Name -match 'wu_worker|vs_buildtools|dotnet-install|Setup-WinBuild' }
|
Where-Object { $_.Name -match 'wu_worker|wu_result|vs_buildtools|dotnet-install' }
|
||||||
$leftovers.Count -eq 0
|
$leftovers.Count -eq 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,8 +257,114 @@ try {
|
|||||||
if ($failed -eq 0) {
|
if ($failed -eq 0) {
|
||||||
Write-Host "All $($checks.Count) checks passed — VM ready for BaseClean snapshot." -ForegroundColor Green
|
Write-Host "All $($checks.Count) checks passed — VM ready for BaseClean snapshot." -ForegroundColor Green
|
||||||
exit 0
|
exit 0
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
if (-not $Remediate) {
|
||||||
Write-Host "$failed / $($checks.Count) checks FAILED." -ForegroundColor Red
|
Write-Host "$failed / $($checks.Count) checks FAILED." -ForegroundColor Red
|
||||||
|
Write-Host "Re-run with -Remediate to auto-fix known issues." -ForegroundColor Yellow
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Remediation pass ─────────────────────────────────────────────────────
|
||||||
|
$failedNames = @($checks | Where-Object { -not $_.Pass } | Select-Object -ExpandProperty Name)
|
||||||
|
Write-Host "Applying remediation for $($failedNames.Count) failed check(s)..." -ForegroundColor Yellow
|
||||||
|
|
||||||
|
Invoke-Command -ComputerName $VMIPAddress -Credential $cred `
|
||||||
|
-Authentication Basic -SessionOption $so -ScriptBlock {
|
||||||
|
|
||||||
|
param([string[]] $FailedNames, [string] $AdminPassword)
|
||||||
|
|
||||||
|
if ($FailedNames -contains 'AutoAdminLogon=1, DefaultUserName=Administrator') {
|
||||||
|
$wl = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon'
|
||||||
|
Set-ItemProperty $wl AutoAdminLogon '1' -Type String
|
||||||
|
Set-ItemProperty $wl DefaultUserName 'Administrator' -Type String
|
||||||
|
Set-ItemProperty $wl DefaultDomainName '.' -Type String
|
||||||
|
Set-ItemProperty $wl DefaultPassword $AdminPassword -Type String
|
||||||
|
Write-Host " [FIX] AutoAdminLogon keys written."
|
||||||
|
}
|
||||||
|
if ($FailedNames -contains 'wuauserv StartType=Disabled') {
|
||||||
|
Set-Service -Name wuauserv -StartupType Disabled -ErrorAction SilentlyContinue
|
||||||
|
Write-Host " [FIX] wuauserv set to Disabled."
|
||||||
|
}
|
||||||
|
if ($FailedNames -contains 'UsoSvc StartType=Disabled') {
|
||||||
|
Set-Service -Name UsoSvc -StartupType Disabled -ErrorAction SilentlyContinue
|
||||||
|
Write-Host " [FIX] UsoSvc set to Disabled."
|
||||||
|
}
|
||||||
|
|
||||||
|
} -ArgumentList $failedNames, $AdminPassword
|
||||||
|
|
||||||
|
# ── Re-run checks ─────────────────────────────────────────────────────────
|
||||||
|
Write-Host "`nRe-validating after remediation..." -ForegroundColor Cyan
|
||||||
|
$checks = Invoke-Command -ComputerName $VMIPAddress -Credential $cred `
|
||||||
|
-Authentication Basic -SessionOption $so -ScriptBlock {
|
||||||
|
|
||||||
|
param($BuildUsername, $DotNetChannel, $PythonVersion)
|
||||||
|
$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 })
|
||||||
|
}
|
||||||
|
foreach ($p in Get-NetFirewallProfile) { $n=$p.Name;$e=$p.Enabled; Chk "Firewall $n disabled" { $e -eq $false } }
|
||||||
|
Chk 'Defender GPO DisableAntiSpyware=1' { (Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender' -Name DisableAntiSpyware -EA Stop).DisableAntiSpyware -eq 1 }
|
||||||
|
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=true' { (Get-Item WSMan:\localhost\Service\AllowUnencrypted -EA Stop).Value -eq 'true' }
|
||||||
|
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 }
|
||||||
|
$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 }
|
||||||
|
Chk 'Explorer LaunchTo=1 (HKCU)' { (Get-ItemProperty 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced' -Name LaunchTo -EA Stop).LaunchTo -eq 1 }
|
||||||
|
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' }
|
||||||
|
Chk 'wuauserv StartType=Disabled' { (Get-Service wuauserv -EA Stop).StartType -eq 'Disabled' }
|
||||||
|
Chk 'UsoSvc StartType=Disabled' { (Get-Service UsoSvc -EA Stop).StartType -eq 'Disabled' }
|
||||||
|
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 "user $BuildUsername exists" { $null -ne (Get-LocalUser -Name $BuildUsername -EA Stop) }
|
||||||
|
Chk "user $BuildUsername enabled" { (Get-LocalUser -Name $BuildUsername -EA Stop).Enabled }
|
||||||
|
Chk "user $BuildUsername PasswordNeverExpires" { (Get-LocalUser -Name $BuildUsername -EA Stop).PasswordExpires -eq $null }
|
||||||
|
Chk "user $BuildUsername member of Administrators" { [bool](& net localgroup Administrators 2>&1 | Select-String -SimpleMatch $BuildUsername) }
|
||||||
|
foreach ($dir in 'C:\CI\build','C:\CI\output','C:\CI\scripts') { Chk "$dir exists" { Test-Path $dir -PathType Container } }
|
||||||
|
Chk ".NET SDK channel $DotNetChannel" { $v=(& dotnet --version 2>&1) -as [string]; $v -and $v.StartsWith($DotNetChannel) }
|
||||||
|
Chk 'Python C:\Python\python.exe present' { Test-Path 'C:\Python\python.exe' }
|
||||||
|
Chk "Python version $PythonVersion" { $v=(& 'C:\Python\python.exe' --version 2>&1) -as [string]; $v -and $v.Trim() -eq "Python $PythonVersion" }
|
||||||
|
Chk 'MSBuild.exe present' { $msb=Get-Command msbuild -EA SilentlyContinue; $msb -and (Test-Path $msb.Source) }
|
||||||
|
Chk 'no leftover installer scripts in C:\CI' { $l=Get-ChildItem 'C:\CI' -File -EA SilentlyContinue | Where-Object { $_.Name -match 'wu_worker|wu_result|vs_buildtools|dotnet-install' }; $l.Count -eq 0 }
|
||||||
|
return $results.ToArray()
|
||||||
|
} -ArgumentList $BuildUsername, $DotNetChannel, $PythonVersion
|
||||||
|
|
||||||
|
Write-Host ''
|
||||||
|
$failed = 0
|
||||||
|
$inSetup = $false
|
||||||
|
foreach ($r in $checks) {
|
||||||
|
if (-not $inSetup -and $r.Name -match "^user $BuildUsername|^C:\\CI\\|wuauserv Start|UsoSvc Start|WU GPO|\.NET SDK|Python|MSBuild|leftover") {
|
||||||
|
Write-Host "`n --- Setup checks ---" -ForegroundColor DarkCyan
|
||||||
|
$inSetup = $true
|
||||||
|
}
|
||||||
|
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) checks passed — VM ready for BaseClean snapshot." -ForegroundColor Green
|
||||||
|
exit 0
|
||||||
|
} else {
|
||||||
|
Write-Host "$failed / $($checks.Count) checks still FAILED after remediation." -ForegroundColor Red
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user