A version-controlled, idempotent PowerShell/winget kit that provisions a clean Windows 11 IoT
Enterprise LTSC 2024 dual-boot gaming + workstation install and brings it back to ~100%
mostly automatically - the Windows companion to
arch-linux-setup-scripts, and deliberately modeled on it
(a shared common.ps1, aggregator modules, one update wrapper, checked-in config).
Host: prowsw880acese (ASUS Pro WS W880-ACE SE, RTX 5080, fTPM/PTT). Windows is kept for native
HDR + ray tracing + DLSS; Arch remains the daily driver / media server / dev box.
Honest scope. A declarative config reproduces ~80-90% of a fresh box. The true 100% restore is a disk image (Macrium/
wbadmin), captured once the box is dialed in. Updates are ~3 surfaces, not one: winget (apps) + Windows Update (OS) + the NVIDIA App (GPU driver). Two are automated byUpdate-System.ps1; the driver is reported.
It is the full 24H2 / build 26100 gaming stack, but feature-frozen with security-only monthly updates through 2034-10-10 (~10 years - 5 more than non-IoT Enterprise LTSC). No annual feature-update churn = no "fight the big update." Ships with no Copilot, Recall, Cortana, Widgets, Xbox app, Teams-consumer, or Microsoft Store.
| Technology | Verdict | This kit does |
|---|---|---|
| Windows Update (LTSC) | Trust - security-only | keeps it on; scripts it via PSWindowsUpdate |
| Microsoft Defender | Trust / keep (AV-TEST 6/6/6) | keeps it; adds no 3rd-party AV |
| Telemetry | Mitigate | AllowTelemetry=0 (Security floor, Enterprise/IoT only) + O&O ShutUp10 |
| Microsoft account | Avoid | local account via autounattend.xml |
| Recall / Copilot | Absent (good) | n/a - excluded from the SKU |
| Microsoft Store | Not installed | provisions winget manually; Store optional/off-path |
| BitLocker | Use - TPM+PIN | see below |
| GeForce Experience | Replaced | NVIDIA App |
On a rig where Secure Boot must stay on - for kernel anti-cheat and because the Arch side
already runs it (sbctl enroll-keys -m) - VeraCrypt system encryption is the wrong tool: it fights
Secure Boot, and Microsoft broke VeraCrypt's boot-signing in early 2026. BitLocker TPM+PIN is the
near-exact analog of the Linux systemd-cryptenroll TPM2 + PIN on PCR 7 design:
- Seals to PCR 7 (Secure Boot state) + 11 with a startup PIN.
- The fTPM/PTT is integrated in the CPU - no external bus to sniff. TPM+PIN defeats Bitpixie / CVE-2023-21563.
- The recovery key is exported locally and never escrowed to a Microsoft account or AD
(
security/Enable-BitLocker.ps1deliberately does not callBackup-BitLockerKeyProtector).
Want open-source crypto for specific files? Keep them on a separate VeraCrypt data volume - it never touches the bootloader, so Secure Boot is unaffected. (Not scripted here.)
windows-setup-scripts/
|-- common.ps1 # constants + helpers - CUSTOMIZE THIS FIRST (<-> common.sh)
|-- Update-System.ps1 # one-stop update wrapper (<-> maintenance.sh)
|-- answer-file/autounattend.xml # clean install, local account, no MS account (dual-boot safe)
|-- bootstrap/
| |-- Install-WinGet.ps1 # provision winget on LTSC (no Store) - idempotent
| `-- Add-MicrosoftStore.ps1 # OPTIONAL/standalone: Xbox/Game Pass (unsupported on LTSC)
|-- install/
| |-- Install.ps1 # aggregator: winget configure -> baseline -> apps
| |-- configuration.dsc.yaml # declarative app manifest (<-> user/packages/install.sh)
| |-- baseline/ (privacy, dualboot, explorer, shutup10 + .cfg)
| `-- apps/ (git, vscode, makemkv, tresorit; standalone: emby, nvidia)
|-- security/ # STANDALONE - run by hand (<-> tpm2-unlock.sh)
| |-- Enable-BitLocker.ps1 # TPM+PIN, XTS-AES-256, local recovery key, NO escrow
| `-- Confirm-Readiness.ps1 # read-only: SecureBoot/TPM/BitLocker/telemetry/anti-cheat
`-- redist/ # downloaded, hash-pinned binaries (gitignored)
Conventions (from arch-linux-setup-scripts): every script dot-sources common.ps1 at the right
relative depth; Assert-Admin <-> require_root; Set-RegistryValue/Get-Redist are the idempotent
guards / verified-download; Invoke-Modules <-> run_scripts (a module is Foo.ps1 or
Foo/Install.ps1; extend by dropping a script in and appending its name to the parent array).
Customize first: edit host constants at the top of common.ps1 ($UserName,
$HostName, $TimeZoneId, $TrustedLanCidr, $TelemetryLevel, $RecoveryKeyDir, $KeyDbSource).
Recommended - a dedicated Windows NVMe with its own ESP. Independent boot chains, zero ESP contention, clean firmware boot-menu switching between "Linux Boot Manager" (systemd-boot) and "Windows Boot Manager." Confirm your M.2 population supports it before you start.
Shared-ESP fallback works but is discouraged: a Windows install/major update can overwrite
\EFI\Boot\BOOTX64.EFI and reorder NVRAM. If you share the ESP, back it up first and re-assert
systemd-boot afterward from Linux (bootctl set-default / efibootmgr -o); watch free space
(your UKIs are large on a 512 MiB ESP).
Each step satisfies the next step's precondition - order matters.
- Back up the target Windows disk. Confirm Arch, systemd-boot, and the btrfs
/datapool are healthy. (Windows can't read btrfs, so/datais never at risk - the ESP is the only shared surface.) - ISO + USB. Get the IoT Enterprise LTSC 2024 ISO (90-day eval, or your VS-subscription license).
Write to USB. Copy
answer-file/autounattend.xmlto the USB root. - Firmware (confirm, don't re-enroll). Secure Boot ON - keep your
sbctlkeys: you enrolled with-m, so Microsoft's PCA already signs the Windows Boot Manager; no Secure Boot change is needed. PTT/fTPM ON (already on for Linux). Do not switch OS Type to "Other OS." - Clean install to the dedicated Windows NVMe (interactive disk pick - the answer file does not
auto-partition). Local
georgeaccount is created; never sign into a Microsoft account. -> Set a login password immediately (the account ships blank). - First-boot sanity. Confirm the machine still dual-boots and Arch still boots under Secure Boot. This validates key-coexistence before you invest in configuration.
- Open an elevated PowerShell, allow scripts for the session, and clone/copy this kit:
If winget isn't on PATH afterward, open a new elevated terminal before continuing.
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass cd <path-to>\windows-setup-scripts .\bootstrap\Install-WinGet.ps1 # provision winget (LTSC has no Store)
- Apply the declarative + baseline + apps layers:
.\install\Install.ps1 # winget configure -> privacy/dualboot/explorer/shutup10 -> git/vscode/makemkv/tresorit - NVIDIA App + driver (manual; not in winget):
.\install\apps\nvidia.ps1prints the download link (or auto-launches if you pin its hash). Install the RTX 5080 driver. Do this before the image. - Verify:
.\security\Confirm-Readiness.ps1- Secure BootTrue, TPM 2.0 ready (the anti-cheat- BitLocker preconditions).
- Capture the golden disk image (Macrium Reflect X / Veeam /
wbadmin) to external/NTFS media - never the btrfs pool. Do this before BitLocker, so the image is decrypted/portable. - Enable encryption (last):
.\security\Enable-BitLocker.ps1 # TPM+PIN; recovery key -> $RecoveryKeyDir; MOVE IT OFFLINE
- Dual-boot final check. Reboot Linux<->Windows and confirm the clock agrees (the kit set
RealTimeIsUniversal=1). If you shared the ESP, re-assert the systemd-boot default from Linux.
.\Update-System.ps1- (a) apps -
winget upgrade --all --include-unknown(+ Chocolatey if present). Theyay -Syuanalog for apps; schedule it in Task Scheduler for set-and-forget. - (b) OS - Windows Update via
PSWindowsUpdate(LTSC = security-only monthly, no feature updates);-IgnoreReboot, so you reboot deliberately. - (c) GPU driver - cannot be unified. The NVIDIA App self-updates; the wrapper only reports the version. DDU for a clean reinstall.
Four apps are not in winget and are handled out of band:
| App | How |
|---|---|
| Tresorit | install/apps/tresorit.ps1 (vendor installer; pin its SHA256 first) |
| Emby Server | install/apps/emby.ps1 (standalone/optional; LAN-only firewall rule) |
| NVIDIA App | install/apps/nvidia.ps1 (driver; interactive) |
| Macrium Reflect | manual (disk-image backstop) |
The bootstrap/shutup10/tresorit/emby/nvidia scripts fetch binaries via Get-Redist, which
verifies a pinned SHA256 and refuses to run un-pinned. First run: download once, Get-FileHash <file> -Algorithm SHA256, paste the hash into the script, commit. Downloads land in redist/
(gitignored).
- PCR 7 is shared between both OSes. Rotating
sbctlkeys, re-runningenroll-keys, a firmware reset, or a dbx revocation update (Windows Update orfwupd) changes PCR 7 -> breaks both Linux TPM2 unlock and Windows BitLocker (each falls back to passphrase/recovery key, then re-seal). Suspend BitLocker across firmware/dbx changes:manage-bde -protectors -disable C: -RebootCount 1. Keep both recovery secrets reachable. - Secure Boot key coexistence is sound but fragile - verify at step 5; a firmware update that
clears keys means re-running
sbctl enroll-keys -mbefore Windows boots again. - winget IDs drift - verify with
winget show <Id>before a big rebuild. IDs here were verified againstmicrosoft/winget-pkgs(note: Pinta =Pinta.Pinta, mpv =shinchiro.mpv). - VBS/HVCI <-> Vanguard <-> VirtualBox tension - leave VBS/HVCI at default; don't run VirtualBox VMs while playing Vanguard/EAC titles (Vanguard refuses active virtualization).
- Declarative != 100%.
winget configure~ 80-90%; activation, game saves, per-app state, and drivers are excluded -> the Macrium image is the only true 100% restore. - Execution policy / PATH. Run elevated with
-ExecutionPolicy Bypass(step 6); apps winget just installed need a fresh shell to appear on PATH (Invoke-Apps.ps1refreshes PATH in-session forgit). - LTSC Store re-add is unsupported - only if you want Xbox/Game Pass (
Add-MicrosoftStore.ps1, off the default path). - UE5 on Windows needs Visual Studio 2022 + C++ workloads (winget can't fully express workloads in
a one-liner - document a
.vsconfig).
- Lifecycle: https://learn.microsoft.com/en-us/lifecycle/products/windows-11-iot-enterprise-ltsc-2024
- winget on IoT: https://learn.microsoft.com/en-us/windows/iot/iot-enterprise/deployment/install-winget-windows-iot
- WinGet Configuration (DSC): https://learn.microsoft.com/en-us/windows/package-manager/configuration/
- BitLocker recovery: https://learn.microsoft.com/en-us/windows/security/operating-system-security/data-protection/bitlocker/recovery-overview
- Anti-cheat Secure Boot/TPM: Riot/Valorant, Activision, EA/BF6, Epic/Fortnite support pages.