HWID is hardware-tied and doesn't survive VM migration. TSforge manipulates the physical activation store directly and is portable. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
110 lines
4 KiB
Nix
110 lines
4 KiB
Nix
# Apply all available Windows Updates via the Windows Update COM API in Audit Mode.
|
|
# Handles reboots automatically — re-registers via RunOnce and continues updating.
|
|
# Compacts the image afterward to flatten the COW chain.
|
|
#
|
|
# Usage:
|
|
# essentials.windowsUpdate {}
|
|
# essentials.windowsUpdate { maxRounds = 5; }
|
|
{ pkgs, lib, ... }:
|
|
{ maxRounds ? 3 }:
|
|
{
|
|
name = "windows-update";
|
|
compact = true;
|
|
memSize = 4096;
|
|
qemuTimeout = 7200;
|
|
auditScript = ''
|
|
@echo off
|
|
setlocal
|
|
|
|
:: Track update round via a counter file
|
|
set "ROUND_FILE=C:\vmix-update-round.txt"
|
|
set "MAX_ROUNDS=${toString maxRounds}"
|
|
|
|
if exist "%ROUND_FILE%" (
|
|
set /p ROUND=<"%ROUND_FILE%"
|
|
) else (
|
|
set "ROUND=1"
|
|
)
|
|
|
|
echo === vmix: Windows Update round %ROUND% of %MAX_ROUNDS% ===
|
|
|
|
:: Ensure Windows Update service is running
|
|
net start wuauserv 2>nul
|
|
sc config wuauserv start= auto
|
|
|
|
:: Remove any update-blocking policies (LTSC may have these)
|
|
reg delete "HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" /f 2>nul
|
|
reg delete "HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" /f 2>nul
|
|
|
|
:: Give the service time to initialize on first round
|
|
if "%ROUND%"=="1" (
|
|
echo Waiting for Windows Update service to initialize...
|
|
timeout /t 30 /nobreak >nul
|
|
)
|
|
|
|
:: Run Windows Update via PowerShell COM API
|
|
powershell -ExecutionPolicy Bypass -Command ^
|
|
"try {" ^
|
|
" $session = New-Object -ComObject Microsoft.Update.Session;" ^
|
|
" $searcher = $session.CreateUpdateSearcher();" ^
|
|
" Write-Host 'Searching for updates...';" ^
|
|
" $result = $searcher.Search('IsInstalled=0');" ^
|
|
" $count = $result.Updates.Count;" ^
|
|
" Write-Host \"Found $count updates\";" ^
|
|
" if ($count -eq 0) { exit 0 };" ^
|
|
" foreach ($u in $result.Updates) { Write-Host \" - $($u.Title)\" };" ^
|
|
" $updatesToInstall = New-Object -ComObject Microsoft.Update.UpdateColl;" ^
|
|
" foreach ($u in $result.Updates) {" ^
|
|
" if ($u.EulaAccepted -eq $false) { $u.AcceptEula() };" ^
|
|
" $updatesToInstall.Add($u) | Out-Null" ^
|
|
" };" ^
|
|
" $downloader = $session.CreateUpdateDownloader();" ^
|
|
" $downloader.Updates = $updatesToInstall;" ^
|
|
" Write-Host 'Downloading...';" ^
|
|
" $downloader.Download() | Out-Null;" ^
|
|
" $installer = $session.CreateUpdateInstaller();" ^
|
|
" $installer.Updates = $updatesToInstall;" ^
|
|
" Write-Host 'Installing...';" ^
|
|
" $installResult = $installer.Install();" ^
|
|
" Write-Host \"Result: $($installResult.ResultCode)\";" ^
|
|
" for ($i = 0; $i -lt $updatesToInstall.Count; $i++) {" ^
|
|
" $hr = $installResult.GetUpdateResult($i).HResult;" ^
|
|
" Write-Host \" $($updatesToInstall.Item($i).Title): code=$hr\"" ^
|
|
" };" ^
|
|
" if ($installResult.RebootRequired) { exit 3010 } else { exit 0 }" ^
|
|
"} catch {" ^
|
|
" Write-Host \"ERROR: $_\";" ^
|
|
" exit 1" ^
|
|
"}"
|
|
|
|
set "WU_EXIT=%ERRORLEVEL%"
|
|
echo Windows Update exit code: %WU_EXIT%
|
|
|
|
:: Cleanup component store
|
|
echo Cleaning up component store...
|
|
dism /Online /Cleanup-Image /StartComponentCleanup /ResetBase /Quiet 2>nul
|
|
|
|
:: Check if we need to reboot and continue
|
|
set /a "NEXT_ROUND=%ROUND%+1"
|
|
|
|
if "%WU_EXIT%"=="3010" (
|
|
if %ROUND% LSS %MAX_ROUNDS% (
|
|
echo Reboot required, scheduling round %NEXT_ROUND%...
|
|
echo %NEXT_ROUND% > "%ROUND_FILE%"
|
|
:: Re-register ourselves for after reboot
|
|
reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce" /v vmixUpdate /t REG_SZ /d "C:\vmix-audit-script.cmd" /f
|
|
:: Immediate reboot (preempts wrapper shutdown)
|
|
shutdown /r /f /t 0
|
|
exit /b
|
|
) else (
|
|
echo Max update rounds reached.
|
|
)
|
|
)
|
|
|
|
:: Done — clean up and shutdown
|
|
:: (shutdown here handles both wrapper-invoked and RunOnce-invoked cases)
|
|
del /q "%ROUND_FILE%" 2>nul
|
|
echo === vmix: Windows Update complete ===
|
|
shutdown /s /f /t 10 /c "vmix: windows-update complete"
|
|
'';
|
|
}
|