From fd795f3d66d769d41dc033015b41fa7f48f2f6e9 Mon Sep 17 00:00:00 2001 From: Timmy Date: Sun, 19 Apr 2026 10:48:03 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E7=94=A8=20Ventoy=20Auto=20Install=20?= =?UTF-8?q?plugin=20=E9=83=A8=E7=BD=B2=EF=BC=9BDiskID=20=E6=94=B9=E6=88=90?= =?UTF-8?q?=20Ventoy=20=E8=AE=8A=E6=95=B8=E9=81=BF=E5=85=8D=E8=AA=A4?= =?UTF-8?q?=E6=A0=BC=E5=8C=96=20USB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - unattend.xml: DiskID 由硬寫 0 改成 $$VT_WINDOWS_DISK_1ST_NONVTOY$$, 防止在 Ventoy 下把 USB 本身當成安裝目標 - ventoy.json: 新增 Auto Install plugin 設定範本 - QUICKSTART.md: 用 Ventoy 流程重寫 USB 佈置與 VM 測試章節 - README.md: 放置位置表新增 Ventoy 路徑為預設 - CLAUDE.md: 新增專案導覽文件,標註 Ventoy-only 前提與 DiskID 變數 Co-Authored-By: Claude Opus 4.7 (1M context) --- CLAUDE.md | 50 +++++++++++++++++++++++++++ QUICKSTART.md | 93 ++++++++++++++++++++++++++++++--------------------- README.md | 15 +++++---- unattend.xml | 6 ++-- ventoy.json | 8 +++++ 5 files changed, 124 insertions(+), 48 deletions(-) create mode 100644 CLAUDE.md create mode 100644 ventoy.json diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..52fbbc2 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,50 @@ +# 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 two artifacts: + +- `unattend.xml` — the Windows Setup answer file. +- `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. + +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 `` 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_$$`) 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 ``; there is no runtime branching. +- **`processorArchitecture="amd64"`** is hardcoded on every ``. ARM64 deployments require a global find-and-replace to `arm64`. +- **Password is 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. +- **`%RAND:5%`** in `ComputerName` is a Windows Setup built-in, not a shell variable — don't try to "fix" it. (Distinct from `$$VT_*$$` which is a 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). + +## 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. diff --git a/QUICKSTART.md b/QUICKSTART.md index 3c05dbd..6352712 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -1,8 +1,8 @@ # QUICKSTART — 從零到自動安裝 Windows -照著做一次就能跑出一個「插上 USB → 自動裝完 Windows → 自動登入 → 跑 Setup.ps1 → 重開機」的流程。 +照著做一次就能跑出一個「Ventoy 開機 → 選 ISO → 自動裝完 Windows → 自動登入 → 跑 Setup.ps1 → 重開機」的流程。 -> 預設情境:x64、UEFI+GPT、繁體中文、單一 Disk 0。 +> 預設情境:x64、UEFI+GPT、繁體中文、**Ventoy USB**([ventoy.net](https://www.ventoy.net/))載入多個 ISO。 --- @@ -11,9 +11,9 @@ 需要準備: - [ ] Windows 10 或 11 官方 ISO(從 [microsoft.com](https://www.microsoft.com/software-download) 取得) -- [ ] 一支 ≥ 8 GB 的 USB 隨身碟(會被格式化) +- [ ] 已刷好 Ventoy 的 USB 隨身碟(≥ 16 GB 建議,因為會同時放 ISO 與 script) - [ ] 一台測試機或 VM(強烈建議先在 VM 驗證) -- [ ] 本專案的 `unattend.xml` +- [ ] 本專案的 `unattend.xml` 與 `ventoy.json` **編輯 `unattend.xml` 的必改項目:** @@ -23,56 +23,71 @@ | 管理員密碼 | `P@ssw0rd!`(兩處:`LocalAccount` 與 `AutoLogon`) | 你的密碼 | | 組織名 | `YourCompany` | 你的組織 | | 腳本路徑 | `C:\Scripts\Setup.ps1` | 留著或改路徑;不要執行就刪 `<SynchronousCommand Order=1>` | +| `DiskID` | `$$VT_WINDOWS_DISK_1ST_NONVTOY$$`(Ventoy 變數) | **保留不動**。Ventoy 會在執行期替換成「第一顆非 Ventoy USB 的磁碟」 | + +**編輯 `ventoy.json` 的必改項目:** + +| 欄位 | 原值 | 要改成 | +|---|---|---| +| `image` | `/Win11.iso` | USB 上實際的 ISO 路徑(相對 USB 根目錄,開頭要有 `/`) | --- -## 1. 做一支自動安裝 USB +## 1. 佈置 Ventoy USB -### 選項 A:Rufus(最快,Windows 原生) +假設 Ventoy 已刷好(若還沒,從 [ventoy.net](https://www.ventoy.net/) 下載 `Ventoy2Disk` 跑一次即可)。Ventoy USB 通常有兩個分割區,資料分割區掛上後會是一個大容量 exFAT/NTFS 碟。 -1. 下載 [Rufus](https://rufus.ie/) 並開啟 -2. 選 ISO、選 USB、分割配置選 **GPT**、目標選 **UEFI** -3. 按「開始」寫入 -4. 寫入完成後,把 `unattend.xml` **改名為 `autounattend.xml`** 放到 USB 根目錄 +把檔案放到 Ventoy 資料分割區: -### 選項 B:macOS / Linux 手動方式 - -```bash -# macOS:找到 USB -diskutil list -# 假設是 /dev/disk4 -diskutil eraseDisk MS-DOS "WINUSB" GPT /dev/disk4 - -# 掛載 ISO 並複製 -hdiutil mount Windows.iso -cp -R /Volumes/CCCOMA_X64FRE_ZH-TW_DV9/* /Volumes/WINUSB/ - -# 放入 autounattend.xml -cp unattend.xml /Volumes/WINUSB/autounattend.xml +``` +<Ventoy USB 根目錄>/ +├── Win11.iso ← 你的 Windows ISO(檔名要對上 ventoy.json 的 image) +└── ventoy/ + ├── ventoy.json ← 本 repo 的 ventoy.json + └── script/ + └── unattend.xml ← 本 repo 的 unattend.xml(改完的) ``` -> Linux 用 `wimlib-imagex split` 處理超過 FAT32 4GB 限制的 `install.wim`。 +macOS / Linux 範例: + +```bash +# 假設 Ventoy 資料分割區掛在 /Volumes/Ventoy +cp Windows.iso /Volumes/Ventoy/Win11.iso +mkdir -p /Volumes/Ventoy/ventoy/script +cp ventoy.json /Volumes/Ventoy/ventoy/ventoy.json +cp unattend.xml /Volumes/Ventoy/ventoy/script/unattend.xml +``` + +> 不用改名成 `autounattend.xml`,也不用把 XML 塞進 ISO 根目錄。Ventoy 的 Auto Install plugin 會在開機時依 `ventoy.json` 把 XML 注入給 Windows Setup。 + +**多個 ISO / 多套範本**:`ventoy.json` 的 `auto_install` 是陣列,每個 ISO 一條目;`template` 也可以是陣列,開機時 Ventoy 會顯示選單讓你挑。 + +驗證:USB 插上目標機開機 → Ventoy 選單選 ISO → 如果 `ventoy.json` 寫對,畫面會出現「Select Auto Install Template」→ 選 `unattend.xml`。 --- ## 2. 先在 VM 試跑(強烈建議) -跳過這步 = 在真機上賭運氣。 +跳過這步 = 在真機上賭運氣。最關鍵的是驗證 `$$VT_WINDOWS_DISK_1ST_NONVTOY$$` 有沒有挑對磁碟。 -### Hyper-V +### 最省事:直接把 Ventoy USB 傳入 VM -```powershell -New-VM -Name "WinTest" -Generation 2 -MemoryStartupBytes 4GB -NewVHDPath "C:\VM\WinTest.vhdx" -NewVHDSizeBytes 60GB -Set-VMDvdDrive -VMName "WinTest" -Path "C:\ISO\Windows.iso" -Set-VMFirmware -VMName "WinTest" -EnableSecureBoot Off # 測試期間關掉比較省事 -Start-VM "WinTest" -``` +- **VirtualBox**(裝 Extension Pack):VM 設定 → USB → 加上你的 Ventoy USB;開機順序把 USB 放最前。 +- **VMware Workstation / Fusion**:VM → Removable Devices → 連接 Ventoy USB;BIOS 改開機順序。 +- **QEMU**:`-drive file=/dev/diskN,format=raw,if=none,id=usb ...`(macOS 先 `diskutil unmountDisk`)。 -把 `autounattend.xml` 注入 ISO:用 [AnyBurn](https://www.anyburn.com/) / `oscdimg` 在 ISO 根目錄加入該檔,或在 VM 掛第二顆 CD。 +VM 要 UEFI 開機、建議 ≥ 60 GB 虛擬硬碟(才能看到分割成功)、測試時可先關 Secure Boot。 -### VirtualBox / VMware +### 另一種:用 Ventoy 的 VHD 模式 -建一台 UEFI 開機的 VM,掛改好的 ISO 開機即可。 +Ventoy 支援寫進 VHD/VDI,可直接掛進 Hyper-V / VirtualBox 當第一顆硬碟,不用實體 USB。細節看 Ventoy 官方文件「Install Ventoy to Local Disk」。 + +### 驗證重點 + +1. Ventoy 選單正確顯示 ISO +2. 選了 ISO 後有跳「Auto Install Template」選單 +3. 安裝過程中**沒有跳語系或分割畫面** +4. 裝完後看 `C:\` 的磁碟編號 — 不是 Ventoy USB 的那顆 --- @@ -131,8 +146,10 @@ Shift + F10 在任何安裝畫面會開出命令提示字元,可以 `notepad` | 現象 | 原因 | 解法 | |---|---|---| -| 停在選語系畫面 | `autounattend.xml` 沒讀到 | 檔名拼錯/不在根目錄;確認 USB 格式 FAT32 | -| 磁碟分割失敗 | 目標機是 BIOS/MBR | 改 `<DiskConfiguration>`:拿掉 EFI/MSR,用 MBR + System Reserved | +| Ventoy 沒跳「Auto Install Template」選單 | `ventoy.json` 的 `image` 路徑對不上 ISO 實際路徑 | 路徑以 `/` 開頭、相對 USB 根目錄;或改用 fuzzy / `parent` 比對 | +| 跳了選單但跳過後仍停在選語系畫面 | `unattend.xml` 路徑錯誤或 XML schema 有誤 | 用 Windows SIM 驗證 XML;確認 `template` 指到的檔案真的存在 | +| 磁碟分割失敗或裝到 Ventoy USB 上 | `DiskID` 挑到 Ventoy USB | 確認用的是 `$$VT_WINDOWS_DISK_1ST_NONVTOY$$`,不是硬寫 `0`;多硬碟環境可改用 `$$VT_WINDOWS_DISK_MAX_SIZE$$` | +| 磁碟分割失敗(所有硬碟都試過) | 目標機是 BIOS/MBR | 改 `<DiskConfiguration>`:拿掉 EFI/MSR,用 MBR + System Reserved | | `ProductKey` 錯誤 | 序號無效 | 改正確序號、用 KMS 通用金鑰、或刪掉整個 `<ProductKey>` 節點 | | OOBE 沒略過 | Windows 版本不支援該欄位 | 比對當前版本 SIM 可用欄位 | | FirstLogonCommands 沒跑 | `AutoLogon` 沒設或 `Order` 重複 | 確認 `AutoLogon.Enabled=true` 且 `Order` 不重複 | diff --git a/README.md b/README.md index 9dc3cb3..8a6b139 100644 --- a/README.md +++ b/README.md @@ -32,11 +32,11 @@ Windows Setup 在不同階段套用不同 `<settings pass="...">`: - **Microsoft-Windows-International-Core-WinPE** — `SetupUILanguage` / `InputLocale` / `SystemLocale` / `UILanguage` / `UserLocale` 皆為 `zh-TW`,`InputLocale` 為 `0404:00000404`(繁中鍵盤)。 - **Microsoft-Windows-Setup** - - `DiskConfiguration` — 清空 Disk 0,建立三個分割: + - `DiskConfiguration` — 清空第一顆非-Ventoy 磁碟(`$$VT_WINDOWS_DISK_1ST_NONVTOY$$`,由 Ventoy Auto Install plugin 於執行期替換成實際 DiskID),建立三個分割: - EFI 500 MB(FAT32, Label `System`) - MSR 128 MB - Primary 其餘全部(NTFS, Label `Windows`, 掛 `C:`) - - `ImageInstall` — 安裝到 Disk 0 Partition 3 + - `ImageInstall` — 安裝到同一顆磁碟的 Partition 3 - `UserData` — `ProductKey`(範本為 `XXXXX-XXXXX-XXXXX-XXXXX-XXXXX`,KMS 可用通用金鑰)、`AcceptEula=true`、`FullName`、`Organization` ### specialize @@ -76,11 +76,12 @@ Windows Setup 在不同階段套用不同 `<settings pass="...">`: ## 放置位置(擇一) -| 位置 | 檔名 | -|---|---| -| USB 安裝隨身碟根目錄 | `autounattend.xml` | -| ISO `sources\` 目錄 | `autounattend.xml` | -| DISM 離線注入已掛載映像 | 任意名稱,用 `/Apply-Unattend:` 指定 | +| 方式 | 位置 | 檔名 | +|---|---|---| +| **Ventoy Auto Install plugin(本 repo 預設)** | Ventoy USB 的 `/ventoy/script/`,並在 `/ventoy/ventoy.json` 指定 | 任意(本 repo 用 `unattend.xml`) | +| 傳統 USB 安裝碟 | USB 根目錄 | `autounattend.xml` | +| ISO `sources\` 目錄 | ISO 根 | `autounattend.xml` | +| DISM 離線注入已掛載映像 | — | 任意名稱,用 `/Apply-Unattend:` 指定 | --- diff --git a/unattend.xml b/unattend.xml index 85b9759..21da813 100644 --- a/unattend.xml +++ b/unattend.xml @@ -22,10 +22,10 @@ publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> - <!-- 磁碟分割(UEFI + GPT)--> + <!-- 磁碟分割(UEFI + GPT)。Ventoy 變數:避開 Ventoy USB 自己,挑第一顆非-Ventoy 磁碟 --> <DiskConfiguration> <Disk wcm:action="add"> - <DiskID>0</DiskID> + <DiskID>$$VT_WINDOWS_DISK_1ST_NONVTOY$$</DiskID> <WillWipeDisk>true</WillWipeDisk> <CreatePartitions> <CreatePartition wcm:action="add"> @@ -54,7 +54,7 @@ <ImageInstall> <OSImage> <InstallTo> - <DiskID>0</DiskID><PartitionID>3</PartitionID> + <DiskID>$$VT_WINDOWS_DISK_1ST_NONVTOY$$</DiskID><PartitionID>3</PartitionID> </InstallTo> <InstallToAvailablePartition>false</InstallToAvailablePartition> </OSImage> diff --git a/ventoy.json b/ventoy.json new file mode 100644 index 0000000..0e1aa94 --- /dev/null +++ b/ventoy.json @@ -0,0 +1,8 @@ +{ + "auto_install": [ + { + "image": "/Win11.iso", + "template": "/ventoy/script/unattend.xml" + } + ] +}