feat: windows-update step handles multiple reboot rounds via scheduled task
All checks were successful
release / build-and-release (push) Successful in 22s

First pass runs during deployment (no reboot). Then registers X9-WindowsUpdate
scheduled task that fires on every logon, installs remaining updates, and
self-deletes when system is fully up to date - covers the typical 2-3 reboot
cycles needed on a fresh Windows install.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
X9 Dev 2026-04-16 14:25:50 +02:00
parent b508ec4b3e
commit 2236fe48e9

View file

@ -3,14 +3,21 @@
Installs all available Windows Updates via PSWindowsUpdate module.
.DESCRIPTION
Installs the PSWindowsUpdate module from PSGallery and runs a full
Windows Update pass. Does not auto-reboot - the operator restarts
manually after all deployment steps complete. Skips drivers (handled
by step 11 Dell Command Update or Windows Update for Business).
First pass: installs all currently available updates without rebooting.
Then registers a scheduled task "X9-WindowsUpdate" that runs on every
logon (as SYSTEM) until no more updates are found - handles the typical
2-3 reboot cycles required on a fresh Windows installation.
Operator workflow:
1. xetup completes all steps
2. Operator reboots manually
3. On each subsequent logon the scheduled task runs another update pass
4. Task removes itself automatically when system is fully up to date
.ITEMS
nainstalovat-pswindowsupdate-modul: Installs NuGet provider and PSWindowsUpdate module from PSGallery. Required only on first run - subsequent runs reuse the cached module.
spustit-windows-update-vsechny-aktualizace: Calls Install-WindowsUpdate -AcceptAll -IgnoreReboot. Installs all Quality, Security and Feature updates. Skips reboot - operator restarts manually after deployment completes.
nainstalovat-pswindowsupdate-modul: Installs NuGet provider and PSWindowsUpdate module from PSGallery.
spustit-prvni-kolo-windows-update: First update pass without reboot - installs all currently available updates.
registrovat-scheduled-task-pro-dalsi-kola: Registers X9-WindowsUpdate scheduled task that runs on logon, handles post-reboot update rounds, and self-deletes when no more updates are found.
#>
param(
[object]$Config,
@ -30,21 +37,11 @@ function Write-Log {
Write-Log "=== Step 12 - Windows Update ===" -Level STEP
# -----------------------------------------------------------------------
# 1. NuGet provider (required for Install-Module from PSGallery)
# 1. NuGet provider + PSWindowsUpdate module
# -----------------------------------------------------------------------
Write-Log "Installing NuGet provider..." -Level INFO
Write-Log "Setting up PSWindowsUpdate module..." -Level INFO
try {
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope AllUsers | Out-Null
Write-Log " NuGet provider ready" -Level OK
} catch {
Write-Log " NuGet provider install failed: $_" -Level WARN
}
# -----------------------------------------------------------------------
# 2. PSWindowsUpdate module
# -----------------------------------------------------------------------
Write-Log "Installing PSWindowsUpdate module..." -Level INFO
try {
$existing = Get-Module -ListAvailable -Name PSWindowsUpdate | Select-Object -First 1
if ($existing) {
Write-Log " PSWindowsUpdate $($existing.Version) already installed" -Level INFO
@ -54,35 +51,65 @@ try {
}
Import-Module PSWindowsUpdate -Force
} catch {
Write-Log " PSWindowsUpdate module setup failed: $_" -Level ERROR
Write-Log " Skipping Windows Update step" -Level WARN
Write-Log " Module setup failed: $_" -Level ERROR
exit 1
}
# -----------------------------------------------------------------------
# 3. Run Windows Update
# 2. First update pass (no reboot)
# -----------------------------------------------------------------------
Write-Log "Checking for available updates..." -Level INFO
Write-Log "Running first Windows Update pass..." -Level INFO
try {
$updates = Get-WindowsUpdate -AcceptAll -IgnoreReboot 2>&1
if (-not $updates) {
Write-Log " No updates available - system is up to date" -Level OK
$result = Install-WindowsUpdate -AcceptAll -IgnoreReboot -Verbose 2>&1
$installed = @($result | Where-Object { $_ -match 'KB\d+|Downloaded|Installed' })
if ($installed.Count -gt 0) {
$result | Where-Object { "$_" -match '\S' } | ForEach-Object { Write-Log " $_" -Level INFO }
Write-Log " First pass complete - reboot required for remaining rounds" -Level OK
} else {
$count = ($updates | Measure-Object).Count
Write-Log " Found $count update(s) - installing..." -Level INFO
Install-WindowsUpdate -AcceptAll -IgnoreReboot -Verbose 2>&1 | ForEach-Object {
if ($_ -match '\S') {
Write-Log " $_" -Level INFO
}
}
Write-Log " Windows Update complete ($count updates processed)" -Level OK
Write-Log " System already up to date" -Level OK
}
} catch {
Write-Log " Windows Update failed: $_" -Level ERROR
exit 1
Write-Log " First pass failed: $_" -Level ERROR
}
# -----------------------------------------------------------------------
# 3. Scheduled task for post-reboot update rounds (self-deleting)
# -----------------------------------------------------------------------
Write-Log "Registering post-reboot update task..." -Level INFO
$taskName = "X9-WindowsUpdate"
# PowerShell block that runs on each logon until no more updates found
$updateScript = @'
Import-Module PSWindowsUpdate -Force -ErrorAction Stop
$updates = Get-WindowsUpdate -AcceptAll -IgnoreReboot
if ($updates) {
Install-WindowsUpdate -AcceptAll -IgnoreReboot | Out-File "C:\Windows\Setup\Scripts\wu-pass-$(Get-Date -Format 'yyyyMMdd-HHmmss').log" -Encoding UTF8
} else {
# No more updates - remove this task
Unregister-ScheduledTask -TaskName "X9-WindowsUpdate" -Confirm:$false
}
'@
try {
$action = New-ScheduledTaskAction -Execute "powershell.exe" `
-Argument "-NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -Command `"$updateScript`""
$trigger = New-ScheduledTaskTrigger -AtLogOn
$settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Hours 2) `
-MultipleInstances IgnoreNew
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest
# Remove existing task first (idempotent)
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue
Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger `
-Settings $settings -Principal $principal -Force | Out-Null
Write-Log " Task '$taskName' registered - runs on each logon until fully updated" -Level OK
} catch {
Write-Log " Failed to register scheduled task: $_" -Level WARN
Write-Log " Manual Windows Update rounds will be needed after reboot" -Level WARN
}
Write-Log "Step 12 - Windows Update complete" -Level OK
Write-Log " ACTION REQUIRED: Reboot the machine to complete remaining update rounds" -Level WARN