xetup/scripts/12-windows-update.ps1
X9 Dev 9feb7ba4e1
All checks were successful
release / build-and-release (push) Successful in 21s
feat: WU completion indicators - lock screen + desktop file
When X9-WindowsUpdate finds no more updates:
- Creates "! WU HOTOVO yyyy-MM-dd HH:mm.txt" on C:\Users\Public\Desktop
- Locks the workstation via one-shot task running as adminx9
  (login screen = unambiguous visual signal for the operator)
- One-shot lock task self-deletes after 5 minutes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 16:12:54 +02:00

157 lines
7.6 KiB
PowerShell

<#
.SYNOPSIS
Installs all available Windows Updates via PSWindowsUpdate module.
.DESCRIPTION
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.
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,
[string]$LogFile
)
$ErrorActionPreference = "Continue"
function Write-Log {
param([string]$Message, [string]$Level = "INFO")
$line = "[$(Get-Date -Format 'HH:mm:ss')] [$Level] $Message"
$null = New-Item -ItemType Directory -Force -Path (Split-Path $LogFile -Parent) -ErrorAction SilentlyContinue
Add-Content -Path $LogFile -Value $line -Encoding UTF8
Write-Output $line
}
Write-Log "=== Step 12 - Windows Update ===" -Level STEP
# -----------------------------------------------------------------------
# 1. NuGet provider + PSWindowsUpdate module
# -----------------------------------------------------------------------
Write-Log "Setting up PSWindowsUpdate module..." -Level INFO
try {
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope AllUsers | Out-Null
$existing = Get-Module -ListAvailable -Name PSWindowsUpdate | Select-Object -First 1
if ($existing) {
Write-Log " PSWindowsUpdate $($existing.Version) already installed" -Level INFO
} else {
Install-Module -Name PSWindowsUpdate -Force -Scope AllUsers -AllowClobber | Out-Null
Write-Log " PSWindowsUpdate installed" -Level OK
}
Import-Module PSWindowsUpdate -Force
} catch {
Write-Log " Module setup failed: $_" -Level ERROR
exit 1
}
# -----------------------------------------------------------------------
# 2. First update pass (no reboot)
# -----------------------------------------------------------------------
Write-Log "Running first Windows Update pass..." -Level INFO
try {
$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 {
Write-Log " System already up to date" -Level OK
}
} catch {
Write-Log " First pass failed: $_" -Level ERROR
}
# -----------------------------------------------------------------------
# 3. Enable autologon for adminx9 (temporary - disabled when updates complete)
# -----------------------------------------------------------------------
Write-Log "Enabling temporary autologon for adminx9..." -Level INFO
$winlogonPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
try {
Set-ItemProperty -Path $winlogonPath -Name "AutoAdminLogon" -Value "1" -Type String -Force
Set-ItemProperty -Path $winlogonPath -Name "DefaultUserName" -Value "adminx9" -Type String -Force
Set-ItemProperty -Path $winlogonPath -Name "DefaultPassword" -Value "" -Type String -Force
Set-ItemProperty -Path $winlogonPath -Name "DefaultDomainName" -Value "." -Type String -Force
# Safety cap: max 10 automatic logons in case the task fails to clean up
Set-ItemProperty -Path $winlogonPath -Name "AutoLogonCount" -Value 10 -Type DWord -Force
Write-Log " Autologon enabled (adminx9, max 10 rounds)" -Level OK
} catch {
Write-Log " Failed to enable autologon: $_" -Level WARN
Write-Log " Windows Update rounds will require manual login after each reboot" -Level WARN
}
# -----------------------------------------------------------------------
# 4. 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.
# When done: disables autologon and removes itself.
$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 - disable autologon and clean up
$wl = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
Set-ItemProperty -Path $wl -Name "AutoAdminLogon" -Value "0" -Type String -Force
Remove-ItemProperty -Path $wl -Name "DefaultPassword" -ErrorAction SilentlyContinue
Remove-ItemProperty -Path $wl -Name "AutoLogonCount" -ErrorAction SilentlyContinue
# Leave a visible marker on the shared Desktop so the operator knows it's done
$ts = Get-Date -Format "yyyy-MM-dd HH:mm"
$doneMsg = "Windows Update dokoncen: $ts`r`nStroj je plne aktualizovan a pripraven k predani klientovi."
[System.IO.File]::WriteAllText(
"C:\Users\Public\Desktop\! WU HOTOVO $ts.txt",
$doneMsg,
[System.Text.Encoding]::UTF8
)
# Lock the workstation - login screen = clear visual signal for the operator.
# Runs as adminx9 (interactive session) via a one-shot task, self-deletes after 5 min.
$lockAction = New-ScheduledTaskAction -Execute "rundll32.exe" -Argument "user32.dll,LockWorkStation"
$lockTrigger = New-ScheduledTaskTrigger -Once -At (Get-Date).AddSeconds(5)
$lockPrincipal = New-ScheduledTaskPrincipal -UserId "adminx9" -LogonType Interactive -RunLevel Limited
$lockSettings = New-ScheduledTaskSettingsSet -DeleteExpiredTaskAfter (New-TimeSpan -Minutes 5)
Register-ScheduledTask -TaskName "X9-WUDoneLock" -Action $lockAction -Trigger $lockTrigger `
-Principal $lockPrincipal -Settings $lockSettings -Force -ErrorAction SilentlyContinue | Out-Null
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