改用 Ventoy Auto Install plugin 部署;DiskID 改成 Ventoy 變數避免誤格化 USB

- 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) <noreply@anthropic.com>
This commit is contained in:
2026-04-19 10:48:03 +08:00
parent 96ea0d5832
commit fd795f3d66
5 changed files with 124 additions and 48 deletions

50
CLAUDE.md Normal file
View File

@@ -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 `<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.
- **`%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.

View File

@@ -1,8 +1,8 @@
# QUICKSTART — 從零到自動安裝 Windows # 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) 取得) - [ ] Windows 10 或 11 官方 ISO從 [microsoft.com](https://www.microsoft.com/software-download) 取得)
- [ ] 一支 ≥ 8 GB 的 USB 隨身碟(會被格式化 - [ ] 已刷好 Ventoy 的 USB 隨身碟(≥ 16 GB 建議,因為會同時放 ISO 與 script
- [ ] 一台測試機或 VM強烈建議先在 VM 驗證) - [ ] 一台測試機或 VM強烈建議先在 VM 驗證)
- [ ] 本專案的 `unattend.xml` - [ ] 本專案的 `unattend.xml``ventoy.json`
**編輯 `unattend.xml` 的必改項目:** **編輯 `unattend.xml` 的必改項目:**
@@ -23,56 +23,71 @@
| 管理員密碼 | `P@ssw0rd!`(兩處:`LocalAccount``AutoLogon` | 你的密碼 | | 管理員密碼 | `P@ssw0rd!`(兩處:`LocalAccount``AutoLogon` | 你的密碼 |
| 組織名 | `YourCompany` | 你的組織 | | 組織名 | `YourCompany` | 你的組織 |
| 腳本路徑 | `C:\Scripts\Setup.ps1` | 留著或改路徑;不要執行就刪 `<SynchronousCommand Order=1>` | | 腳本路徑 | `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
### 選項 ARufus最快Windows 原生) 假設 Ventoy 已刷好(若還沒,從 [ventoy.net](https://www.ventoy.net/) 下載 `Ventoy2Disk` 跑一次即可。Ventoy USB 通常有兩個分割區,資料分割區掛上後會是一個大容量 exFAT/NTFS 碟。
1. 下載 [Rufus](https://rufus.ie/) 並開啟 把檔案放到 Ventoy 資料分割區:
2. 選 ISO、選 USB、分割配置選 **GPT**、目標選 **UEFI**
3. 按「開始」寫入
4. 寫入完成後,把 `unattend.xml` **改名為 `autounattend.xml`** 放到 USB 根目錄
### 選項 BmacOS / Linux 手動方式 ```
<Ventoy USB 根目錄>/
```bash ├── Win11.iso ← 你的 Windows ISO檔名要對上 ventoy.json 的 image
# macOS找到 USB └── ventoy/
diskutil list ├── ventoy.json ← 本 repo 的 ventoy.json
# 假設是 /dev/disk4 └── script/
diskutil eraseDisk MS-DOS "WINUSB" GPT /dev/disk4 └── unattend.xml ← 本 repo 的 unattend.xml改完的
# 掛載 ISO 並複製
hdiutil mount Windows.iso
cp -R /Volumes/CCCOMA_X64FRE_ZH-TW_DV9/* /Volumes/WINUSB/
# 放入 autounattend.xml
cp unattend.xml /Volumes/WINUSB/autounattend.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 試跑(強烈建議) ## 2. 先在 VM 試跑(強烈建議)
跳過這步 = 在真機上賭運氣。 跳過這步 = 在真機上賭運氣。最關鍵的是驗證 `$$VT_WINDOWS_DISK_1ST_NONVTOY$$` 有沒有挑對磁碟。
### Hyper-V ### 最省事:直接把 Ventoy USB 傳入 VM
```powershell - **VirtualBox**(裝 Extension PackVM 設定 → USB → 加上你的 Ventoy USB開機順序把 USB 放最前。
New-VM -Name "WinTest" -Generation 2 -MemoryStartupBytes 4GB -NewVHDPath "C:\VM\WinTest.vhdx" -NewVHDSizeBytes 60GB - **VMware Workstation / Fusion**VM → Removable Devices → 連接 Ventoy USBBIOS 改開機順序。
Set-VMDvdDrive -VMName "WinTest" -Path "C:\ISO\Windows.iso" - **QEMU**`-drive file=/dev/diskN,format=raw,if=none,id=usb ...`macOS 先 `diskutil unmountDisk`)。
Set-VMFirmware -VMName "WinTest" -EnableSecureBoot Off # 測試期間關掉比較省事
Start-VM "WinTest"
```
`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 | | Ventoy 沒跳「Auto Install Template」選單 | `ventoy.json` 的 `image` 路徑對不上 ISO 實際路徑 | 路徑以 `/` 開頭、相對 USB 根目錄;或改用 fuzzy / `parent` 比對 |
| 磁碟分割失敗 | 目標機是 BIOS/MBR | 改 `<DiskConfiguration>`:拿掉 EFI/MSR用 MBR + System Reserved | | 跳了選單但跳過後仍停在選語系畫面 | `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>` 節點 | | `ProductKey` 錯誤 | 序號無效 | 改正確序號、用 KMS 通用金鑰、或刪掉整個 `<ProductKey>` 節點 |
| OOBE 沒略過 | Windows 版本不支援該欄位 | 比對當前版本 SIM 可用欄位 | | OOBE 沒略過 | Windows 版本不支援該欄位 | 比對當前版本 SIM 可用欄位 |
| FirstLogonCommands 沒跑 | `AutoLogon` 沒設或 `Order` 重複 | 確認 `AutoLogon.Enabled=true` 且 `Order` 不重複 | | FirstLogonCommands 沒跑 | `AutoLogon` 沒設或 `Order` 重複 | 確認 `AutoLogon.Enabled=true` 且 `Order` 不重複 |

View File

@@ -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-International-Core-WinPE** — `SetupUILanguage` / `InputLocale` / `SystemLocale` / `UILanguage` / `UserLocale` 皆為 `zh-TW``InputLocale``0404:00000404`(繁中鍵盤)。
- **Microsoft-Windows-Setup** - **Microsoft-Windows-Setup**
- `DiskConfiguration` — 清空 Disk 0,建立三個分割: - `DiskConfiguration` — 清空第一顆非-Ventoy 磁碟(`$$VT_WINDOWS_DISK_1ST_NONVTOY$$`,由 Ventoy Auto Install plugin 於執行期替換成實際 DiskID,建立三個分割:
- EFI 500 MBFAT32, Label `System` - EFI 500 MBFAT32, Label `System`
- MSR 128 MB - MSR 128 MB
- Primary 其餘全部NTFS, Label `Windows`, 掛 `C:` - 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` - `UserData``ProductKey`(範本為 `XXXXX-XXXXX-XXXXX-XXXXX-XXXXX`KMS 可用通用金鑰)、`AcceptEula=true``FullName``Organization`
### specialize ### specialize
@@ -76,11 +76,12 @@ Windows Setup 在不同階段套用不同 `<settings pass="...">`
## 放置位置(擇一) ## 放置位置(擇一)
| 位置 | 檔名 | | 方式 | 位置 | 檔名 |
|---|---| |---|---|---|
| USB 安裝隨身碟根目錄 | `autounattend.xml` | | **Ventoy Auto Install plugin本 repo 預設)** | Ventoy USB 的 `/ventoy/script/`,並在 `/ventoy/ventoy.json` 指定 | 任意(本 repo 用 `unattend.xml` |
| ISO `sources\` 目錄 | `autounattend.xml` | | 傳統 USB 安裝碟 | USB 根目錄 | `autounattend.xml` |
| DISM 離線注入已掛載映像 | 任意名稱,用 `/Apply-Unattend:` 指定 | | ISO `sources\` 目錄 | ISO 根 | `autounattend.xml` |
| DISM 離線注入已掛載映像 | — | 任意名稱,用 `/Apply-Unattend:` 指定 |
--- ---

View File

@@ -22,10 +22,10 @@
publicKeyToken="31bf3856ad364e35" publicKeyToken="31bf3856ad364e35"
language="neutral" language="neutral"
versionScope="nonSxS"> versionScope="nonSxS">
<!-- 磁碟分割UEFI + GPT--> <!-- 磁碟分割UEFI + GPT。Ventoy 變數:避開 Ventoy USB 自己,挑第一顆非-Ventoy 磁碟 -->
<DiskConfiguration> <DiskConfiguration>
<Disk wcm:action="add"> <Disk wcm:action="add">
<DiskID>0</DiskID> <DiskID>$$VT_WINDOWS_DISK_1ST_NONVTOY$$</DiskID>
<WillWipeDisk>true</WillWipeDisk> <WillWipeDisk>true</WillWipeDisk>
<CreatePartitions> <CreatePartitions>
<CreatePartition wcm:action="add"> <CreatePartition wcm:action="add">
@@ -54,7 +54,7 @@
<ImageInstall> <ImageInstall>
<OSImage> <OSImage>
<InstallTo> <InstallTo>
<DiskID>0</DiskID><PartitionID>3</PartitionID> <DiskID>$$VT_WINDOWS_DISK_1ST_NONVTOY$$</DiskID><PartitionID>3</PartitionID>
</InstallTo> </InstallTo>
<InstallToAvailablePartition>false</InstallToAvailablePartition> <InstallToAvailablePartition>false</InstallToAvailablePartition>
</OSImage> </OSImage>

8
ventoy.json Normal file
View File

@@ -0,0 +1,8 @@
{
"auto_install": [
{
"image": "/Win11.iso",
"template": "/ventoy/script/unattend.xml"
}
]
}