refactor(template): §7 — move OS hardening to Deploy, Setup becomes validation-only
§7.1 — Setup Steps 1/3/4b/5b/5c converted to Assert-Step only:
- Deploy post-install.ps1 now owns: Firewall (all profiles disabled),
WinRM MaxMemory=2048MB/IdleTimeout=2h/MaxProcesses=25/StartupType=Automatic,
lock screen (NoLockScreen), SM GPO policy key + scheduled task disable,
DisableCAD in Winlogon, OOBE non-policy key.
- Setup no longer re-applies these — validates them, throws if Deploy missed any.
§7.2 — WU lifecycle Strategy B:
- Deploy: replaces sc.exe disable with GPO locks only (NoAutoUpdate=1,
DisableWindowsUpdateAccess=1); services remain Manual (Windows default).
- Setup Step 6 Step A already clears these locks before COM API; Step 6b
permanently disables services post-update. No Setup changes needed.
§7.3 — Docs updated:
- Deploy .DESCRIPTION reflects full hardening ownership.
- Setup .DESCRIPTION marks validation-only steps, updates step ordering rationale.
- WINDOWS-TEMPLATE-SETUP.md: three-script architecture table, CI-WinBuild.vmx
path, Fase C step list + rationale + validation table updated.
TODO: §7.1/7.2/7.3 marked done. §7.4 e2e pending (requires new VM snapshot).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -36,13 +36,19 @@
|
||||
|
||||
Hardening done by post-install.ps1 inside the guest:
|
||||
- UAC off (EnableLUA=0, LocalAccountTokenFilterPolicy=1)
|
||||
- Defender real-time protection off
|
||||
- Defender real-time protection off + DisableAntiSpyware=1 GPO
|
||||
- DiagTrack/Telemetry off
|
||||
- Windows Update services disabled
|
||||
- RDP enabled (no NLA, lab only) + firewall rule
|
||||
- WinRM enabled HTTP 5985 + HTTPS 5986 (self-signed cert), Basic auth,
|
||||
AllowUnencrypted, TrustedHosts=*
|
||||
- Windows Update GPO locks (NoAutoUpdate=1, DisableWindowsUpdateAccess=1);
|
||||
services remain Manual — Setup-WinBuild2025 Step 6b permanently disables
|
||||
them after applying updates (§7.2 Strategy B)
|
||||
- Windows Firewall all profiles disabled (VMnet8 NAT lab; Setup Step 1 validates)
|
||||
- RDP enabled (no NLA, lab only) + firewall rules for RDP/ICMP/WinRM-HTTPS
|
||||
- WinRM HTTP 5985 + HTTPS 5986 (self-signed cert), Basic auth,
|
||||
AllowUnencrypted, TrustedHosts=*, MaxMemory=2048MB, IdleTimeout=2h, MaxProcesses=25
|
||||
- ICMPv4 inbound allowed
|
||||
- Lock screen disabled, Server Manager (policy + HKLM + task + HKCU + Default hive),
|
||||
DisableCAD (Policies\System + Winlogon), OOBE/telemetry suppressed;
|
||||
Setup-WinBuild2025 Step 5b/5c validate all of these
|
||||
- VMware Tools installed (Complete) from Tools ISO
|
||||
- install_complete.flag dropped in C:\Windows\Temp
|
||||
|
||||
@@ -506,15 +512,22 @@ sc.exe config DiagTrack start= disabled | Out-Null
|
||||
sc.exe stop DiagTrack | Out-Null
|
||||
L 'Telemetry disabled'
|
||||
|
||||
# ── Windows Update services disabled ──────────────────────────────────────
|
||||
foreach (`$svc in 'wuauserv','UsoSvc','WaaSMedicSvc') {
|
||||
sc.exe config `$svc start= disabled | Out-Null
|
||||
sc.exe stop `$svc | Out-Null
|
||||
}
|
||||
reg add 'HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU' /v NoAutoUpdate /t REG_DWORD /d 1 /f | Out-Null
|
||||
L 'Windows Update disabled'
|
||||
# ── Windows Update GPO locks — services left Manual (§7.2 Strategy B) ───
|
||||
# Services remain at Windows default (Manual/on-demand). Setup-WinBuild2025
|
||||
# Step 6 invokes the WU COM API via a SYSTEM scheduled task; it clears these
|
||||
# GPO keys first (Step A), applies updates, then permanently disables services
|
||||
# in Step 6b (post-update lockdown). These keys block background auto-start
|
||||
# between Deploy and Setup without breaking the on-demand COM API.
|
||||
reg add 'HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' /v DisableWindowsUpdateAccess /t REG_DWORD /d 1 /f | Out-Null
|
||||
reg add 'HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU' /v NoAutoUpdate /t REG_DWORD /d 1 /f | Out-Null
|
||||
L 'Windows Update GPO locks set (services remain Manual; Setup Step 6b permanently disables)'
|
||||
|
||||
# ── Firewall: profiles stay default; allow RDP + ICMP + WinRM HTTPS ──────
|
||||
# ── Firewall: all profiles disabled + allow RDP + ICMP + WinRM HTTPS ─────
|
||||
# Disabling all profiles removes any block on subsequent WinRM config and WU
|
||||
# downloads. This VM lives on VMnet8 NAT behind the VMware NAT gateway — no
|
||||
# inbound exposure from outside the host. Setup-WinBuild2025 Step 1 validates.
|
||||
Set-NetFirewallProfile -Profile Domain,Private,Public -Enabled False
|
||||
L 'Windows Firewall disabled on all profiles'
|
||||
reg add 'HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server' /v fDenyTSConnections /t REG_DWORD /d 0 /f | Out-Null
|
||||
reg add 'HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' /v UserAuthentication /t REG_DWORD /d 0 /f | Out-Null
|
||||
Enable-NetFirewallRule -DisplayGroup 'Remote Desktop' -ErrorAction SilentlyContinue
|
||||
@@ -533,6 +546,11 @@ try {
|
||||
New-NetFirewallRule -DisplayName 'WinRM-HTTPS' -Direction Inbound -Protocol TCP -LocalPort 5986 -Action Allow -Profile Any | Out-Null
|
||||
L 'WinRM HTTPS listener configured'
|
||||
} catch { L "WinRM HTTPS failed: `$(`$_.Exception.Message)" }
|
||||
winrm set winrm/config/winrs '@{MaxProcessesPerShell="25"}' | Out-Null
|
||||
Set-Item WSMan:\localhost\Shell\MaxMemoryPerShellMB 2048 -Force 3>`$null
|
||||
Set-Item WSMan:\localhost\Shell\IdleTimeOut 7200000 -Force 3>`$null
|
||||
Set-Service -Name WinRM -StartupType Automatic 3>`$null
|
||||
L 'WinRM shell limits: MaxMemory=2048MB IdleTimeout=2h MaxProcesses=25; StartType=Automatic'
|
||||
|
||||
# ── Server Manager / WAC popup / first-logon UX ──────────────────────────
|
||||
# Server Manager auto-start off — machine-wide + current Administrator + Default
|
||||
@@ -549,6 +567,18 @@ reg add 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' /v Disa
|
||||
# OOBE privacy experience suppressed
|
||||
reg add 'HKLM\SOFTWARE\Policies\Microsoft\Windows\OOBE' /v DisablePrivacyExperience /t REG_DWORD /d 1 /f | Out-Null
|
||||
L 'Server Manager + WAC + CAD + OOBE prompts disabled'
|
||||
# Supplemental UX hardening validated by Setup-WinBuild2025 Step 5c:
|
||||
# Lock screen: suppress at CI console sessions (no interactive user ever logs in)
|
||||
reg add 'HKLM\SOFTWARE\Policies\Microsoft\Windows\Personalization' /v NoLockScreen /t REG_DWORD /d 1 /f | Out-Null
|
||||
# Server Manager GPO policy key (authoritative path on WS2025; HKLM key above is not always honoured)
|
||||
reg add 'HKLM\SOFTWARE\Policies\Microsoft\Windows NT\CurrentVersion\ServerManager' /v DoNotOpenAtLogon /t REG_DWORD /d 1 /f | Out-Null
|
||||
# Server Manager scheduled task — the actual launcher on WS2025; registry alone insufficient
|
||||
schtasks /Change /TN '\Microsoft\Windows\Server Manager\ServerManager' /Disable 2>&1 | Out-Null
|
||||
# DisableCAD in Winlogon (belt-and-suspenders with Policies\System key set above)
|
||||
reg add 'HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' /v DisableCAD /t REG_DWORD /d 1 /f | Out-Null
|
||||
# OOBE non-policy key (supplements the Policies\Windows\OOBE key set above)
|
||||
reg add 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\OOBE' /v DisablePrivacyExperience /t REG_DWORD /d 1 /f | Out-Null
|
||||
L 'Lock screen, SM GPO policy + task, CAD Winlogon, OOBE non-policy key set'
|
||||
|
||||
# ── IE Enhanced Security Configuration off (Admin) ───────────────────────
|
||||
# Long-known CI papercut: ESC blocks every URL in admin sessions.
|
||||
|
||||
+66
-170
@@ -8,40 +8,40 @@
|
||||
This script is run ONE TIME inside the template VM before taking the
|
||||
"BaseClean" snapshot. After the snapshot is taken, never modify the VM.
|
||||
|
||||
Installs and configures (each step followed by Assert-Step validation):
|
||||
Step 1 — Firewall: all profiles disabled first — removes any block on subsequent steps
|
||||
Prerequisite: Deploy-WinBuild2025.ps1 must have completed first (unattended OS
|
||||
install + post-install.ps1 hardening). Steps 1, 3, 4b, 5b, 5c are validation-only
|
||||
— they assert that Deploy already set the expected state rather than re-applying it.
|
||||
Steps 4, 5, 6/6b, 7-10 perform actual CI customisation.
|
||||
|
||||
Steps and Assert-Step validation:
|
||||
Step 1 — Firewall: validation only — Deploy disabled all profiles.
|
||||
Validation: Domain/Private/Public all Enabled=False
|
||||
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
|
||||
Step 2 — Defender: validation only — Deploy set DisableAntiSpyware=1 GPO + RTP off.
|
||||
Validation: GPO DisableAntiSpyware=1
|
||||
Step 3 — WinRM: validation only — Deploy ran Enable-PSRemoting, Basic auth,
|
||||
AllowUnencrypted, MaxMemory=2048MB, IdleTimeout=2h, MaxProcesses=25.
|
||||
Validation: service Running+Automatic, AllowUnencrypted, Auth/Basic, memory
|
||||
Step 4 — Local build user account ($BuildUsername, PasswordNeverExpires, Administrators)
|
||||
Validation: user exists, enabled, PasswordNeverExpires, admin membership
|
||||
Step 4b — UAC disabled (EnableLUA=0, LocalAccountTokenFilterPolicy=1)
|
||||
Step 4b — UAC: validation only — Deploy set EnableLUA=0, LocalAccountTokenFilterPolicy=1.
|
||||
Validation: both registry values verified
|
||||
Step 5 — CI working directories: C:\CI\build, C:\CI\output, C:\CI\scripts
|
||||
Must run before Windows Update — WU worker writes temp files to C:\CI
|
||||
Validation: each path exists as Container
|
||||
Step 5b — Explorer LaunchTo=1 (This PC) for current user + Default profile hive
|
||||
Step 5b — Explorer LaunchTo=1: validation only — Deploy set HKCU + Default hive.
|
||||
Validation: HKCU LaunchTo=1
|
||||
Step 5c — CI UX hardening (all UI tweaks before the long-running WU step):
|
||||
Server Manager auto-start off (machine policy + HKCU Administrator +
|
||||
Default User hive → ci_build inherits at first logon)
|
||||
Ctrl+Alt+Del at login disabled (DisableCAD=1) — CI VMs never need it
|
||||
OOBE privacy/telemetry prompt suppressed (DisablePrivacyExperience=1,
|
||||
AllowTelemetry=0) — prevents first-logon interactive prompt in clones
|
||||
Validation: machine policy key, HKCU key, DisableCAD, OOBE policy, telemetry
|
||||
Step 6 — Windows Update via Scheduled Task as SYSTEM (avoids WinRM token limit)
|
||||
Runs with Firewall + Defender off, C:\CI present — faster, no scan overhead
|
||||
Step 5c — CI UX hardening: validation only — Deploy set lock screen, Server Manager
|
||||
(policy + HKLM + task + HKCU + Default hive), DisableCAD (Policies\System
|
||||
+ Winlogon), OOBE/telemetry suppressed.
|
||||
Validation: NoLockScreen, SM policy, SM task, DisableCAD, OOBE, telemetry
|
||||
Step 6 — Windows Update via Scheduled Task as SYSTEM (avoids WinRM token limit).
|
||||
Clears Deploy's GPO locks (Step A), starts services (Step D), runs COM API,
|
||||
reports result code. Skipped with -SkipWindowsUpdate.
|
||||
Validation: result code 0/2/3, no reboot required (else exit 3010)
|
||||
Step 6b — Windows Update permanently disabled (post-update lockdown)
|
||||
Step 6b — Windows Update permanently disabled (post-update lockdown — always runs).
|
||||
wuauserv + UsoSvc set to Disabled; GPO keys NoAutoUpdate=1,
|
||||
DisableWindowsUpdateAccess=1; WU scheduled tasks disabled
|
||||
Runs unconditionally (even with -SkipWindowsUpdate) so snapshot
|
||||
captures WU off — linked clones never trigger background updates
|
||||
DisableWindowsUpdateAccess=1; WU scheduled tasks disabled.
|
||||
Snapshot captures WU permanently off — linked clones never auto-update.
|
||||
Validation: wuauserv StartType=Disabled, GPO keys present
|
||||
Step 7 — .NET SDK (channel $DotNetSdkVersion) via dotnet-install.ps1
|
||||
Validation: dotnet resolvable, version channel match, machine PATH
|
||||
@@ -50,28 +50,27 @@
|
||||
Step 9 — Visual Studio Build Tools 2026 via Scheduled Task as SYSTEM
|
||||
Validation: MSBuild.exe exists, executes, PATH, VC dir present
|
||||
Step 10 — Toolchain cross-check (dotnet, python, msbuild)
|
||||
Throws on any missing tool (was: warn-only)
|
||||
Throws on any missing tool
|
||||
Cleanup — Disk cleanup: cleanmgr, SoftwareDistribution, CBS, TEMP, Prefetch, DISM
|
||||
wuauserv already Disabled (Step 6b) — not restarted after cache clear
|
||||
SoftwareDistribution\Download cleared best-effort (WaaSMedicSvc may repopulate)
|
||||
Validation: wuauserv StartType Disabled (SoftwareDistribution check removed —
|
||||
WaaSMedicSvc tamper-protection restores files immediately)
|
||||
Validation: wuauserv StartType Disabled
|
||||
Final — Pre-snapshot gate: 7 cross-cutting checks across all steps
|
||||
Throws if any check fails — prevents snapshot of broken state
|
||||
|
||||
Step ordering rationale:
|
||||
Firewall first — removes any network block before WinRM config and WU downloads
|
||||
Defender second — eliminates scan overhead before WinRM, WU, and installer downloads
|
||||
WinRM third — configured clean with no Firewall/Defender interference
|
||||
User + UAC — quick registry ops; UAC off before dirs/tools avoids elevation prompts
|
||||
CI dirs — C:\CI must exist before WU worker writes temp files there
|
||||
UI tweaks — all fast registry writes batched before the slow WU step
|
||||
WU sixth — all prereqs (dirs, user, tweaks) done; Firewall+Defender off
|
||||
WU disable — immediately after WU completes; snapshot captures WU permanently off
|
||||
Toolchain — .NET / Python / VS last; WU done, no scan on installer payloads
|
||||
Firewall (Step 1) — validates Deploy disabled all profiles; assertion before WinRM/WU
|
||||
Defender (Step 2) — validates Deploy disabled RTP; assertion before tool install
|
||||
WinRM (Step 3) — validates Deploy configured WinRM; assertion before remote steps
|
||||
User (Step 4) — creates ci_build; must exist before UAC and dirs assertions
|
||||
UAC (Step 4b) — validates Deploy disabled UAC (elevation-free installs in 7-9)
|
||||
CI dirs (Step 5) — C:\CI must exist before WU worker writes temp files there
|
||||
UI tweaks (5b/5c) — validates Deploy UX settings; fast assertions before slow WU
|
||||
WU (Step 6) — all prereqs validated; WU runs with Firewall+Defender already off
|
||||
WU disable (Step 6b) — immediately after WU; snapshot captures WU permanently off
|
||||
Toolchain (7-9) — .NET/Python/VS last; WU done, no scan on installer payloads
|
||||
|
||||
NOTE: Git is NOT installed. The host clones the repository and copies
|
||||
the source tree into the VM via WinRM (Option B isolation model).
|
||||
NOTE: Git is NOT installed by default. See §6.6 (Tier-1 Toolchain) in TODO.md
|
||||
for the planned addition. The host clones and copies source via WinRM until then.
|
||||
|
||||
╔══════════════════════════════════════════════════════════════════════╗
|
||||
║ NETWORK REQUIREMENT DURING SETUP ║
|
||||
@@ -173,16 +172,12 @@ foreach ($f in $knownTempFiles) {
|
||||
}
|
||||
}
|
||||
|
||||
# ── Step 1: Firewall ──────────────────────────────────────────────────────────
|
||||
Write-Step "Disabling Windows Firewall (all profiles) — isolated lab VM"
|
||||
# ── Step 1: Firewall (validation only — disabled by Deploy-WinBuild2025) ──────
|
||||
Write-Step "Firewall state (validation only — disabled at deploy time)"
|
||||
|
||||
# Disable first: removes any block on subsequent WinRM config and WU downloads.
|
||||
# This VM lives on VMnet8 NAT behind the VMware NAT gateway — no inbound exposure
|
||||
# from outside the host, so full disable is acceptable.
|
||||
Set-NetFirewallProfile -Profile Domain,Private,Public -Enabled False
|
||||
Write-Host "Windows Firewall disabled on all profiles."
|
||||
# Deploy-WinBuild2025.ps1 post-install.ps1 called Set-NetFirewallProfile -Enabled False
|
||||
# on all profiles. Validated here before WinRM and WU assertions proceed.
|
||||
|
||||
# Validation
|
||||
foreach ($p in 'Domain','Private','Public') {
|
||||
Assert-Step 'Firewall' "$p profile disabled" {
|
||||
(Get-NetFirewallProfile -Profile $p -ErrorAction Stop).Enabled -eq $false
|
||||
@@ -201,29 +196,13 @@ Assert-Step 'Defender' 'GPO DisableAntiSpyware=1 (set by Deploy-WinBuild2025)' {
|
||||
((Get-ItemProperty -Path $key -Name DisableAntiSpyware -ErrorAction SilentlyContinue).DisableAntiSpyware -eq 1)
|
||||
}
|
||||
|
||||
# ── Step 3: WinRM ─────────────────────────────────────────────────────────────
|
||||
Write-Step "Configuring WinRM"
|
||||
# ── Step 3: WinRM (validation only — configured by Deploy-WinBuild2025) ───────
|
||||
Write-Step "WinRM state (validation only — configured at deploy time)"
|
||||
|
||||
# Firewall and Defender already off — no interference during WinRM hardening.
|
||||
Enable-PSRemoting -Force -SkipNetworkProfileCheck 3>$null | Out-Null
|
||||
# Deploy-WinBuild2025.ps1 post-install.ps1 ran Enable-PSRemoting, set Basic auth,
|
||||
# AllowUnencrypted, TrustedHosts=*, MaxMemory=2048MB, IdleTimeout=2h, MaxProcesses=25,
|
||||
# and StartupType=Automatic. Validated here before the build user and toolchain steps.
|
||||
|
||||
# Allow unencrypted (HTTP/5985) — acceptable for an isolated lab network.
|
||||
# Migrate to HTTPS/5986 with a self-signed cert for hardened environments.
|
||||
winrm set winrm/config/service '@{AllowUnencrypted="true"}' | Out-Null
|
||||
winrm set winrm/config/service/auth '@{Basic="true"}' | Out-Null
|
||||
|
||||
# Increase shell memory and timeout limits for long builds
|
||||
winrm set winrm/config/winrs '@{MaxProcessesPerShell="25"}' | Out-Null
|
||||
Set-Item WSMan:\localhost\Shell\MaxMemoryPerShellMB 2048 -Force 3>$null
|
||||
Set-Item WSMan:\localhost\Shell\IdleTimeOut 7200000 -Force 3>$null # 2 hours
|
||||
|
||||
# Ensure WinRM starts automatically
|
||||
Set-Service -Name WinRM -StartupType Automatic 3>$null
|
||||
Start-Service WinRM 3>$null
|
||||
|
||||
Write-Host "WinRM configured."
|
||||
|
||||
# Validation
|
||||
Assert-Step 'WinRM' 'service Running' {
|
||||
(Get-Service WinRM -ErrorAction Stop).Status -eq 'Running'
|
||||
}
|
||||
@@ -294,19 +273,13 @@ Assert-Step 'User' "user '$BuildUsername' member of Administrators" {
|
||||
[bool](& net localgroup Administrators 2>&1 | Select-String -SimpleMatch $BuildUsername)
|
||||
}
|
||||
|
||||
# ── Step 4b: Disable UAC ─────────────────────────────────────────────────────
|
||||
Write-Step "Disabling UAC (isolated lab VM)"
|
||||
# ── Step 4b: UAC (validation only — disabled by Deploy-WinBuild2025) ─────────
|
||||
Write-Step "UAC state (validation only — disabled at deploy time)"
|
||||
|
||||
# EnableLUA=0 disables UAC entirely — admin processes get full token without prompt.
|
||||
# LocalAccountTokenFilterPolicy=1 ensures remote admin connections (WinRM) also
|
||||
# get an elevated token (avoids the filtered-token issue with runProgramInGuest).
|
||||
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' `
|
||||
-Name EnableLUA -Value 0 -Type DWord -Force
|
||||
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' `
|
||||
-Name LocalAccountTokenFilterPolicy -Value 1 -Type DWord -Force
|
||||
Write-Host "UAC disabled."
|
||||
# Deploy-WinBuild2025.ps1 post-install.ps1 set EnableLUA=0 (no prompt for admin
|
||||
# processes) and LocalAccountTokenFilterPolicy=1 (WinRM remote connections get full
|
||||
# elevated token, required for runProgramInGuest and remote admin commands).
|
||||
|
||||
# Validation
|
||||
$uacPolicyKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System'
|
||||
Assert-Step 'UAC' 'EnableLUA=0' {
|
||||
(Get-ItemProperty -Path $uacPolicyKey -Name EnableLUA -ErrorAction Stop).EnableLUA -eq 0
|
||||
@@ -335,109 +308,32 @@ foreach ($dir in $ciDirs) {
|
||||
}
|
||||
}
|
||||
|
||||
# ── Step 5b: Explorer settings ───────────────────────────────────────────────
|
||||
Write-Step "Configuring Explorer defaults"
|
||||
# ── Step 5b: Explorer settings (validation only — configured by Deploy-WinBuild2025) ──
|
||||
Write-Step "Explorer defaults (validation only — configured at deploy time)"
|
||||
|
||||
# Deploy-WinBuild2025.ps1 post-install.ps1 set LaunchTo=1 for HKCU (Administrator)
|
||||
# and the Default User hive (so ci_build and any new user inherits at first logon).
|
||||
|
||||
# Open Explorer to 'This PC' (Devices and Drives) instead of Quick Access.
|
||||
# LaunchTo=1 = This PC, LaunchTo=2 = Quick Access (default)
|
||||
$explorerAdvKey = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced'
|
||||
Set-ItemProperty -Path $explorerAdvKey -Name 'LaunchTo' -Value 1 -Type DWord -Force
|
||||
|
||||
# Also apply to the Default User profile so ci_build and any new user inherits it.
|
||||
$defaultHive = 'C:\Users\Default\NTUSER.DAT'
|
||||
$mountName = 'TempDefaultUser'
|
||||
if (Test-Path $defaultHive) {
|
||||
reg load "HKU\$mountName" $defaultHive | Out-Null
|
||||
$defKey = "Registry::HKU\$mountName\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced"
|
||||
if (-not (Test-Path $defKey)) { New-Item -Path $defKey -Force | Out-Null }
|
||||
Set-ItemProperty -Path $defKey -Name 'LaunchTo' -Value 1 -Type DWord -Force
|
||||
[gc]::Collect()
|
||||
reg unload "HKU\$mountName" | Out-Null
|
||||
Write-Host "Explorer: 'This PC' set for current user and default profile."
|
||||
}
|
||||
else {
|
||||
Write-Host "Explorer: 'This PC' set for current user (default hive not found)."
|
||||
}
|
||||
|
||||
# Validation
|
||||
Assert-Step 'Explorer' "LaunchTo=1 (HKCU current user)" {
|
||||
(Get-ItemProperty -Path $explorerAdvKey -Name LaunchTo -ErrorAction Stop).LaunchTo -eq 1
|
||||
}
|
||||
|
||||
# ── Step 5c: CI UX hardening ─────────────────────────────────────────────────
|
||||
Write-Step "CI UX hardening (lock screen off, Server Manager off, no CAD, no OOBE)"
|
||||
# ── Step 5c: CI UX hardening (validation only — configured by Deploy-WinBuild2025) ─
|
||||
Write-Step "CI UX hardening state (validation only — configured at deploy time)"
|
||||
|
||||
# All UI tweaks batched here — fast registry writes before the slow WU step.
|
||||
# Deploy-WinBuild2025.ps1 post-install.ps1 set: lock screen (NoLockScreen=1), Server
|
||||
# Manager (policy + HKLM + task + HKCU + Default hive), DisableCAD (Policies\System +
|
||||
# Winlogon), OOBE DisablePrivacyExperience (policy + non-policy), AllowTelemetry=0.
|
||||
|
||||
# 1 — Lock screen: disable entirely (CI VMs never need a lock screen)
|
||||
# NoLockScreen=1 suppresses the lock screen shown before login on console sessions.
|
||||
$personPolicyKey = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\Personalization'
|
||||
if (-not (Test-Path $personPolicyKey)) { New-Item -Path $personPolicyKey -Force | Out-Null }
|
||||
Set-ItemProperty -Path $personPolicyKey -Name 'NoLockScreen' -Value 1 -Type DWord -Force
|
||||
Write-Host "Lock screen disabled."
|
||||
|
||||
# 2 — Server Manager: do not auto-open at logon
|
||||
# Three layers needed on WS2025:
|
||||
# a) Machine policy key (GPO path — read by ServerManager policy check)
|
||||
# b) HKLM non-policy key (read directly by ServerManager.exe on WS2025)
|
||||
# c) Scheduled Task '\Microsoft\Windows\Server Manager\ServerManager'
|
||||
# — this is the actual launcher; registry alone is insufficient on WS2025
|
||||
# d) HKCU + Default hive for per-user belt-and-suspenders
|
||||
$smPolicyKey = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\CurrentVersion\ServerManager'
|
||||
if (-not (Test-Path $smPolicyKey)) { New-Item -Path $smPolicyKey -Force | Out-Null }
|
||||
Set-ItemProperty -Path $smPolicyKey -Name 'DoNotOpenAtLogon' -Value 1 -Type DWord -Force
|
||||
|
||||
$smHKLMKey = 'HKLM:\SOFTWARE\Microsoft\ServerManager'
|
||||
if (-not (Test-Path $smHKLMKey)) { New-Item -Path $smHKLMKey -Force | Out-Null }
|
||||
Set-ItemProperty -Path $smHKLMKey -Name 'DoNotOpenServerManagerAtLogon' -Value 1 -Type DWord -Force
|
||||
|
||||
Disable-ScheduledTask -TaskPath '\Microsoft\Windows\Server Manager\' `
|
||||
-TaskName 'ServerManager' -ErrorAction SilentlyContinue | Out-Null
|
||||
|
||||
# Current session user (Administrator)
|
||||
$smUserKey = 'HKCU:\SOFTWARE\Microsoft\ServerManager'
|
||||
if (-not (Test-Path $smUserKey)) { New-Item -Path $smUserKey -Force | Out-Null }
|
||||
Set-ItemProperty -Path $smUserKey -Name 'DoNotOpenServerManagerAtLogon' -Value 1 -Type DWord -Force
|
||||
|
||||
# Default User hive — ci_build and any future account inherits at first logon
|
||||
$smDefaultHive = 'C:\Users\Default\NTUSER.DAT'
|
||||
$smMountName = 'TempDefaultUserSM'
|
||||
if (Test-Path $smDefaultHive) {
|
||||
reg load "HKU\$smMountName" $smDefaultHive | Out-Null
|
||||
$smDefKey = "Registry::HKU\$smMountName\SOFTWARE\Microsoft\ServerManager"
|
||||
if (-not (Test-Path $smDefKey)) { New-Item -Path $smDefKey -Force | Out-Null }
|
||||
Set-ItemProperty -Path $smDefKey -Name 'DoNotOpenServerManagerAtLogon' -Value 1 -Type DWord -Force
|
||||
[gc]::Collect()
|
||||
reg unload "HKU\$smMountName" | Out-Null
|
||||
Write-Host "Server Manager: policy + HKLM + task + Administrator HKCU + Default hive set."
|
||||
}
|
||||
else {
|
||||
Write-Host "Server Manager: policy + HKLM + task + Administrator HKCU set (Default hive not found)."
|
||||
}
|
||||
|
||||
# 3 — Disable Ctrl+Alt+Del at interactive login
|
||||
# Set in both locations: Winlogon (classic) + Policies\System (policy, authoritative on WS2025).
|
||||
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' `
|
||||
-Name 'DisableCAD' -Value 1 -Type DWord -Force
|
||||
$smPolicyKey = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\CurrentVersion\ServerManager'
|
||||
$smHKLMKey = 'HKLM:\SOFTWARE\Microsoft\ServerManager'
|
||||
$smUserKey = 'HKCU:\SOFTWARE\Microsoft\ServerManager'
|
||||
$systemPolicyKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System'
|
||||
Set-ItemProperty -Path $systemPolicyKey -Name 'DisableCAD' -Value 1 -Type DWord -Force
|
||||
Write-Host "Ctrl+Alt+Del at login disabled (Winlogon + Policies\System)."
|
||||
$oobePolicyKey = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\OOBE'
|
||||
$dcKey = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\DataCollection'
|
||||
|
||||
# 4 — Suppress OOBE privacy/telemetry prompt
|
||||
$oobePolicyKey = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\OOBE'
|
||||
if (-not (Test-Path $oobePolicyKey)) { New-Item -Path $oobePolicyKey -Force | Out-Null }
|
||||
Set-ItemProperty -Path $oobePolicyKey -Name 'DisablePrivacyExperience' -Value 1 -Type DWord -Force
|
||||
|
||||
$oobeKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\OOBE'
|
||||
if (-not (Test-Path $oobeKey)) { New-Item -Path $oobeKey -Force | Out-Null }
|
||||
Set-ItemProperty -Path $oobeKey -Name 'DisablePrivacyExperience' -Value 1 -Type DWord -Force
|
||||
|
||||
$dcKey = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\DataCollection'
|
||||
if (-not (Test-Path $dcKey)) { New-Item -Path $dcKey -Force | Out-Null }
|
||||
Set-ItemProperty -Path $dcKey -Name 'AllowTelemetry' -Value 0 -Type DWord -Force
|
||||
Write-Host "OOBE privacy prompt and telemetry disabled."
|
||||
|
||||
# Validation
|
||||
Assert-Step 'CIUX' 'lock screen disabled (NoLockScreen=1)' {
|
||||
(Get-ItemProperty -Path $personPolicyKey -Name NoLockScreen -ErrorAction Stop).NoLockScreen -eq 1
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user