refactor(template): Defender validation-only + auto WU-reboot loop in Prepare

Setup-WinBuild2025.ps1 (Step 2):
- Defender is now disabled at deploy time by Deploy-WinBuild2025.ps1 via
  DisableAntiSpyware=1 GPO + RTP off during unattended install.
- Step 2 reduced to a single Assert-Step sanity check (GPO key present = 1).
  Exclusion paths removed: scanner is not running, they would be moot.

Prepare-WinBuild2025.ps1 (Setup invocation):
- Replace one-shot Invoke-Command + manual 're-run with -SkipWindowsUpdate'
  message with an automated loop (max 10 iterations):
  * Invoke-GuestSetup helper runs Setup inside the VM and returns exit code.
  * Exit 3010 (ERROR_SUCCESS_REBOOT_REQUIRED) triggers Restart-Computer on
    the guest, waits for WinRM via Wait-GuestWinRMReady (20 min timeout),
    reopens PSSession and loops.
  * Exit 0 breaks the loop; any other code throws immediately.
  * Hard cap prevents infinite loops if WU never converges.
This commit is contained in:
Simone
2026-05-10 01:01:30 +02:00
parent 889bd9e41b
commit ab636a0ec1
2 changed files with 96 additions and 100 deletions
+14 -66
View File
@@ -11,11 +11,10 @@
Installs and configures (each step followed by Assert-Step validation):
Step 1 — Firewall: all profiles disabled first — removes any block on subsequent steps
Validation: Domain/Private/Public all Enabled=False
Step 2 — Defender & Antimalware disabled — before WinRM and Windows Update
Set-MpPreference + GPO registry keys; exclusions C:\CI, C:\dotnet, C:\Python
Validation: 6 GPO keys + MpPreference (if WinDefend service present)
Rationale: WU downloads + VS/Python/dotnet installers run unscanned;
saves 5-15 min; WinRM traffic not inspected
Step 2 — Defender state validation only (disabled by Deploy-WinBuild2025
during unattended install via DisableAntiSpyware GPO + RTP off).
Validation: GPO DisableAntiSpyware=1. No exclusion paths — with
Defender off the scanner isn't running, so exclusions are moot.
Step 3 — WinRM: Basic auth, AllowUnencrypted, MaxMemoryPerShellMB=2048, 2h timeout
Firewall + Defender already off — no interference during config
Validation: service Running+Automatic, AllowUnencrypted, Auth/Basic, memory
@@ -190,67 +189,16 @@ foreach ($p in 'Domain','Private','Public') {
}
}
# ── Step 2: Disable Windows Defender & Antimalware ───────────────────────────
Write-Step "Disabling Windows Defender & Antimalware"
# Disabled before WinRM config and Windows Update so that:
# - WU downloads/installs are not scanned (saves 5-15 min on update-heavy runs)
# - WinRM traffic is not intercepted by network inspection
# On an isolated CI template VM this trade-off is acceptable.
$defSvc = Get-Service -Name WinDefend -ErrorAction SilentlyContinue
if ($defSvc) {
Set-MpPreference -DisableRealtimeMonitoring $true -ErrorAction SilentlyContinue 3>$null
Set-MpPreference -DisableBehaviorMonitoring $true -ErrorAction SilentlyContinue 3>$null
Set-MpPreference -DisableIOAVProtection $true -ErrorAction SilentlyContinue 3>$null
Set-MpPreference -DisableScriptScanning $true -ErrorAction SilentlyContinue 3>$null
# Exclude CI build paths from any remaining scanning
Add-MpPreference -ExclusionPath 'C:\CI' -ErrorAction SilentlyContinue 3>$null
Add-MpPreference -ExclusionPath 'C:\dotnet' -ErrorAction SilentlyContinue 3>$null
Add-MpPreference -ExclusionPath 'C:\Python' -ErrorAction SilentlyContinue 3>$null
Write-Host "Windows Defender real-time protection disabled."
}
else {
Write-Host "Windows Defender service not found — skipping."
}
# Disable via Group Policy registry key — survives Defender auto-updates and reboots.
# This is the same key that SCCM/GPO uses in enterprise environments.
$defenderKey = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender'
if (-not (Test-Path $defenderKey)) {
New-Item -Path $defenderKey -Force | Out-Null
}
Set-ItemProperty -Path $defenderKey -Name 'DisableAntiSpyware' -Value 1 -Type DWord -Force
Set-ItemProperty -Path $defenderKey -Name 'DisableAntiVirus' -Value 1 -Type DWord -Force
# Also disable via the Windows Security Center policy subkey
$rtKey = "$defenderKey\Real-Time Protection"
if (-not (Test-Path $rtKey)) {
New-Item -Path $rtKey -Force | Out-Null
}
Set-ItemProperty -Path $rtKey -Name 'DisableRealtimeMonitoring' -Value 1 -Type DWord -Force
Set-ItemProperty -Path $rtKey -Name 'DisableBehaviorMonitoring' -Value 1 -Type DWord -Force
Set-ItemProperty -Path $rtKey -Name 'DisableIOAVProtection' -Value 1 -Type DWord -Force
Set-ItemProperty -Path $rtKey -Name 'DisableScriptScanning' -Value 1 -Type DWord -Force
Write-Host "Defender GPO registry keys written (persistent across updates)."
# Validation — GPO registry keys (always applicable, survives Defender presence)
Assert-Step 'Defender' "GPO DisableAntiSpyware=1" {
(Get-ItemProperty -Path $defenderKey -Name DisableAntiSpyware -ErrorAction Stop).DisableAntiSpyware -eq 1
}
Assert-Step 'Defender' "GPO DisableAntiVirus=1" {
(Get-ItemProperty -Path $defenderKey -Name DisableAntiVirus -ErrorAction Stop).DisableAntiVirus -eq 1
}
foreach ($rtName in 'DisableRealtimeMonitoring','DisableBehaviorMonitoring','DisableIOAVProtection','DisableScriptScanning') {
Assert-Step 'Defender' "GPO Real-Time Protection $rtName=1" {
(Get-ItemProperty -Path $rtKey -Name $rtName -ErrorAction Stop).$rtName -eq 1
}
}
# Validation — Defender preferences (only if service exists)
if ($defSvc) {
$mp = Get-MpPreference -ErrorAction SilentlyContinue
if ($mp) {
Assert-Step 'Defender' 'Set-MpPreference DisableRealtimeMonitoring=true' { $mp.DisableRealtimeMonitoring }
Assert-Step 'Defender' "ExclusionPath contains 'C:\CI'" { $mp.ExclusionPath -contains 'C:\CI' }
}
# ── Step 2: Defender — already disabled by Deploy-WinBuild2025 ───────────────
# Deploy-WinBuild2025.ps1 set RTP off + DisableAntiSpyware=1 GPO during the
# unattended install. With Defender fully off, exclusion paths are moot — the
# scanner that would respect them isn't running. Keep a sanity check on the
# tamper-protect GPO key so we throw early if someone re-enabled Defender.
Write-Step "Defender state (validation only — disabled at deploy time)"
Assert-Step 'Defender' 'GPO DisableAntiSpyware=1 (set by Deploy-WinBuild2025)' {
$key = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender'
(Test-Path $key) -and
((Get-ItemProperty -Path $key -Name DisableAntiSpyware -ErrorAction SilentlyContinue).DisableAntiSpyware -eq 1)
}
# ── Step 3: WinRM ─────────────────────────────────────────────────────────────