新增 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>
This commit is contained in:
2026-04-27 13:36:13 +08:00
parent b3329d4889
commit c84e18bd9d
7 changed files with 565 additions and 108 deletions

View File

@@ -4,12 +4,13 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Repository nature
This is a **configuration template repo**, not a code project. It ships two artifacts:
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](https://www.ventoy.net/)**, 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 and `unattend.xml` at `/ventoy/script/unattend.xml` let Ventoy inject the answer file at boot without touching the ISO. Assume this Ventoy-based flow when reasoning about deploy-time behavior.
Deployment is via **[Ventoy](https://www.ventoy.net/)**, 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:
@@ -41,10 +42,19 @@ There is no way to "run" this repo on the dev machine (macOS). All verification
- **`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.)
- **`FirstLogonCommands` references `C:\Scripts\Setup.ps1`** but this repo does **not** contain that script. It is expected to be placed on the target by other means (baked into the image, copied from USB earlier in `FirstLogonCommands`, or pulled from network).
- **`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 `user``User` 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-LocalUser`s 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`) explain the *why* behind design decisions — consult `SUMMARY.md` before proposing structural changes, as several current choices are explicit tradeoffs, not oversights.
- 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.