Files
windows-unattend/CLAUDE.md
Timmy c84e18bd9d 新增 Setup.ps1(13 段 post-install);FirstLogonCommands 改從 Ventoy USB 拷腳本
把分散在 unattend.xml inline 的網路相依步驟(OpenSSH/Telegram/RemoveAI)和
新加入的 10 個 post-install 動作整合進獨立的 Setup.ps1,由 FirstLogonCommands
Order 4 從 Ventoy USB(/ventoy/script/Setup.ps1)掃磁碟複製到 C:\Scripts\,
Order 5 執行。腳本分兩 phase:

Phase 1(不需網路):SSH 22 防火牆、RDP+3389、ICMP Echo、WinRM TrustedHosts=*、
移 OneDrive、關 Cortana/WebSearch、套 UI/IME 預設到 Default profile、user→User
改名 + 從登入畫面隱藏 Admin。

Phase 2(要網路,wait-network 後):OpenSSH Server FoD、KMS 啟用(先試
$InternalKmsServers 後 fallback 公開 KMS)、winget Chrome、RemoveWindowsAI、
Win11Debloat、22 個 UWP 移除、Telegram、刪 Panther 殘留。

每段以 Section 包裝寫獨立 START/DONE/ERR 到 C:\Windows\Temp\setup.log,方便逐段
重跑與排查。

文件全部同步:DEPLOY/QUICKSTART/SUMMARY/README/CLAUDE 都加上 Setup.ps1 的
USB 部署、cp 步驟、Order 表、排查指引。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 13:36:13 +08:00

9.0 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Repository nature

This is a configuration template repo, not a code project. It ships three artifacts:

  • unattend.xml — the Windows Setup answer file.
  • Setup.ps1 — the post-install PowerShell that runs at first logon (firewall/WinRM/RDP/ICMP, KMS, Win11Debloat, RemoveWindowsAI, OneDrive removal, Cortana off, Default-profile UI/IME defaults, account rename, Telegram, Chrome via winget). Lives in the repo and is also copied to the Ventoy USB at /ventoy/script/Setup.ps1. At first logon unattend.xml scans removable drives for \ventoy\script\Setup.ps1, copies it to C:\Scripts\Setup.ps1, then runs it.
  • ventoy.json — the Ventoy Auto Install plugin config that tells Ventoy which ISO to pair with which template.

Deployment is via Ventoy, not Rufus or direct ISO modification. The user keeps a Ventoy USB with multiple Windows ISOs; ventoy.json at /ventoy/ventoy.json on the USB, unattend.xml at /ventoy/script/unattend.xml, and Setup.ps1 at /ventoy/script/Setup.ps1 let Ventoy inject the answer file at boot without touching the ISO and have the post-install script ready next to it. Assume this Ventoy-based flow when reasoning about deploy-time behavior.

There is no build, no test suite, no package manager, and no runtime. Changes are validated by:

  1. Schema validation via Windows SIM (Windows System Image Manager, part of Windows ADK).
  2. End-to-end VM test: boot a VM from the Ventoy USB (or a Ventoy VHD) and confirm the install reaches the desktop without prompts. QUICKSTART.md has the exact steps.

There is no way to "run" this repo on the dev machine (macOS). All verification happens on a Windows target.

The three-pass mental model

unattend.xml is not a flat config — it is split into three <settings pass="..."> blocks that correspond to three distinct moments in Windows Setup. Each setting has a required pass; putting it in the wrong pass means Windows silently ignores it.

Pass When it runs System state What lives here
windowsPE Booted from USB, Windows not yet installed In-memory mini-OS Disk partitioning, image selection, product key, EULA, WinPE UI language
specialize Image applied, before first boot C:\ exists, no users yet Computer name, timezone, registered owner, domain join
oobeSystem First boot, OOBE running Full Windows, waiting for account Local account creation, AutoLogon, FirstLogonCommands, OOBE screen skips

Language settings appear twice on purpose — once in windowsPE (International-Core-WinPE) for the installer UI, once in oobeSystem (International-Core) for the OOBE UI. That is not duplication; removing either will surface an unwanted language prompt.

Non-obvious constraints when editing

  • Deployment is via Ventoy, not filename-based auto-detection. The old "rename to autounattend.xml at USB root" trick does not apply here — Ventoy boots the ISO directly and injects the template per ventoy.json. Keep the repo filename as unattend.xml and keep the ventoy.json template path in sync.
  • DiskID uses the Ventoy variable $$VT_WINDOWS_DISK_1ST_NONVTOY$$, not a numeric literal. Ventoy's Auto Install plugin substitutes this at runtime with the first non-Ventoy disk, so Windows doesn't wipe the Ventoy USB itself. Never replace it with 0 or any hardcoded number. Other Ventoy-provided variables ($$VT_WINDOWS_DISK_MAX_SIZE$$, $$VT_WINDOWS_DISK_CLOSEST_<N>$$) are valid substitutes for different selection policies but only work under Ventoy — they are meaningless if the XML is ever deployed via a non-Ventoy path.
  • Passwords appear twice: once in UserAccounts/LocalAccount and once in AutoLogon. Both must match — changing one silently breaks AutoLogon.
  • FirstLogonCommands only fires if AutoLogon.Enabled=true. Removing AutoLogon also disables the post-install script hand-off.
  • Order values within FirstLogonCommands / CreatePartitions / ModifyPartitions must be unique and contiguous — duplicates cause silent skips.
  • The disk config assumes UEFI+GPT and wipes the selected disk unconditionally (WillWipeDisk=true). Any BIOS/MBR target requires rewriting <DiskConfiguration>; there is no runtime branching.
  • processorArchitecture="amd64" is hardcoded on every <component>. ARM64 deployments require a global find-and-replace to arm64.
  • Password is plaintext (<PlainText>true</PlainText>). This is a deliberate testing-only choice documented in SUMMARY.md. For production, generate Base64 via Windows SIM; do not hand-encode.
  • ComputerName does not support %RAND:5% or any other macro. Windows Setup only accepts a literal name, * (auto-generate a random name like DESKTOP-ABC123D), or an empty value. %RAND:N% is an MDT/SCCM task-sequence variable — under the Ventoy → raw unattend.xml path it is passed verbatim as a literal string, fails NetBIOS character validation (% is not allowed), and causes the specialize pass to abort with a Microsoft-Windows-Shell-Setup error. If a PC-XXXXX style name is required, set <ComputerName>*</ComputerName> here and do the rename in FirstLogonCommands via Rename-Computer (a reboot is needed for it to take effect — combine with the existing shutdown /r step). (Distinct from $$VT_*$$, which is a real Ventoy pre-processor substitution.)
  • Setup.ps1 is shipped via the Ventoy USB, not embedded in unattend.xml. FirstLogonCommands Order 4 scans every mounted filesystem drive for \ventoy\script\Setup.ps1 and copies the first match to C:\Scripts\Setup.ps1; Order 5 runs it. Two consequences: (1) the Ventoy USB must remain plugged in until first logon completes — yanking it after WinPE means Setup.ps1 never makes it to disk and the post-install steps silently skip (C:\Windows\Temp\firstlogon-copysetup.log will say NOT FOUND); (2) editing Setup.ps1 in the repo is not enough — re-copy to /Volumes/Ventoy/ventoy/script/Setup.ps1 before each test deploy. Both firstlogon-copysetup.log and setup.log live under C:\Windows\Temp\.
  • Setup.ps1 runs as the AutoLogon Admin session in FirstLogonCommands. It modifies the Default profile's NTUSER.DAT (loaded via reg load HKU\SetupDefault C:\Users\Default\NTUSER.DAT) to push UI/IME defaults to future user logins — it does not touch the AutoLogon Admin session itself, and it cannot reach the just-created user/User profile because that profile dir doesn't exist until that user first logs in. Anything you want applied to Admin's already-active session has to go through HKCU directly.
  • The userUser rename happens inside Setup.ps1, not in unattend.xml. unattend.xml Order 3 creates the lowercase user (so it's bulletproof even if Setup.ps1 never runs); Setup.ps1's rename-hide-accounts section then Rename-LocalUsers it to capitalised User and hides Admin from the login screen via SpecialAccounts\UserList. If you change the account name convention, both places must be edited.

When making changes

  • Preserve the xmlns="urn:schemas-microsoft-com:unattend" namespace and the wcm:action="add" attributes on list items — Windows SIM emits these and Setup requires them.
  • Every <component> needs all four identity attributes: name, processorArchitecture, publicKeyToken="31bf3856ad364e35", language="neutral", versionScope="nonSxS".
  • The Chinese-language docs (README.md, QUICKSTART.md, SUMMARY.md, DEPLOY.md) explain the why behind design decisions — consult SUMMARY.md before proposing structural changes, as several current choices are explicit tradeoffs, not oversights.

When making changes to Setup.ps1

  • Sections are wrapped with the Section helper (Section 'name' { ... }) which logs START / DONE / ERR to C:\Windows\Temp\setup.log. Each one is idempotent and tolerates partial earlier runs — preserve that property when adding new sections, because deploys do get re-tested by re-running Setup.ps1 manually.
  • Phase ordering is enforced by file order, not by metadata: Phase 1 (no network — firewall, registry, Default profile, account rename) runs first; wait-network then blocks up to 60 s; Phase 2 (network — OpenSSH FoD, KMS, winget, RemoveAI, Win11Debloat, UWP removal, Telegram) follows. Don't move a network-dependent section above wait-network.
  • $Win11DebloatConfigJson and $UwpRemoveList are mirrored from windows-remote-toolkit/config/win11debloat-config.json and remove-apps-config.json. They were intentionally inlined rather than fetched at runtime so the script is self-contained on the Ventoy USB. If you change one, update the comment pointing at the toolkit source so the next sync isn't accidental.
  • $InternalKmsServers is intentionally empty — the public KMS list (kms.digiboy.ir, kms8.msguides.com, kms.03k.org, kms.chinancce.com) is the fallback. If a deployment is on a network with its own KMS host, that's the only line to change.