# SUMMARY — 這份 unattend.xml 是怎麼長出來的 ## 為什麼需要它 手動裝一台 Windows 是這樣的:按「下一步」、選語系、接受 EULA、分割磁碟、填序號、建帳號、略過 OOBE 一堆隱私問題、登入後開 PowerShell 裝軟體。一台大約 20–30 分鐘,十台就是半天。**而且每次點的選項還不一定一樣。** 部署這件事的本質是:**把「一次性的人工操作」變成「可重複的檔案」。** Windows 給的答案叫 **Answer File**(回應檔,通常是 `unattend.xml`)。開機時 Setup 會去讀這份 XML,把原本需要人類回答的問題一題一題自己答完。 --- ## 設計的三條主線 這份 `unattend.xml` 不是隨便把欄位湊齊,而是圍繞三個目標組出來的: ### 1. 零互動(Zero Touch) 從 USB 開機到桌面出現,**中間不應該有任何畫面需要鍵盤滑鼠**。 為了做到這件事,檔案裡有一連串「否決畫面」的開關: - `HideEULAPage` / `HideOEMRegistrationScreen` / `HideOnlineAccountScreens` / `HideWirelessSetupInOOBE` — 把 OOBE 那些煩人的頁面全部隱藏 - `SkipMachineOOBE` / `SkipUserOOBE` — 機器層與使用者層的 OOBE 都略過 - `AcceptEula=true` — 安裝時的授權合約預先接受 - `WillShowUI=OnError` on `ProductKey` — 序號正確就不跳視窗,錯了才問 這些不是可有可無的選項,而是「零互動」這個目標的具體落實。 ### 2. 確定性(Deterministic) 同一份 `unattend.xml` 套上去,結果應該一樣。 - **磁碟分割是硬寫的**:清空 Disk 0 → EFI 500 MB → MSR 128 MB → 剩下全給 Windows。不讓 Setup 自己猜,不處理遷移場景。 - **語系/時區是硬寫的**:`zh-TW` + `Taipei Standard Time`。不依賴 OEM image 預設。 - **唯一的例外是電腦名稱**:XML 裡設 `*` 讓 Windows Setup 先給一個隨機名(否則 specialize 階段會因為 `%RAND:5%` 之類的假巨集字面送入而失敗),再由 `FirstLogonCommands` 的 `Rename-Computer` 把它改成 `PC-XXXXX` 格式並在收尾 `shutdown /r` 時一起生效。 這是一個取捨:確定性優先,但在會造成衝突的地方(主機名)讓步。 ### 3. 交棒到腳本(Hand-off to Scripts) XML 能做的事有上限。超過這條線(裝軟體、設 Registry、拉 Git repo、裝驅動)就不該硬塞 XML,應該交給 PowerShell。 `FirstLogonCommands` 就是交棒點: ``` Order 1 → net accounts (一行搞定的雜事直接 cmd,密碼永不過期) Order 2 → Rename-Computer (主機名改 PC-XXXXX,重開後生效) Order 3 → net user User 1234 /add (建立一般使用者 User,強制首次登入改密) Order 4 → Copy Setup.ps1 from Ventoy USB (掃磁碟找 \ventoy\script\Setup.ps1 → C:\Scripts\) Order 5 → powershell -File Setup.ps1 (13 段 post-install,看下表) Order 6 → shutdown /r (重開機收尾,讓 rename 生效) ``` `Setup.ps1` 內部分兩個 phase(用空的 `wait-network` 段切開): ``` [Phase 1:本機設定,不需網路] firewall-ssh-22 → SSH 22/TCP inbound 防火牆規則 rdp-enable-3389 → 啟用 RDP + 3389 防火牆群組 icmp-echo-allow → ICMPv4 Echo Request inbound(外部 ping 得到) winrm-enable → Enable-PSRemoting + winrm quickconfig + TrustedHosts=* onedrive-remove → /uninstall + 清啟動鍵 + 移除 CLSID cortana-websearch-off → AllowCortana=0、DisableWebSearch=1、SearchBoxSuggestions=1 apply-ui-ime-default-profile → reg load Default profile,套 Dark mode / 工作列靠左 / 顯示副檔名 / 預設英數鍵盤 / 停 IME 切換熱鍵 hide-admin-account → 從登入畫面隱藏 Admin(帳號仍存在仍可用) [Phase 2:需要網路] wait-network → 等 ICMP 通到 8.8.8.8,上限 60 秒 openssh-server-install → Add-WindowsCapability OpenSSH.Server + Start-Service sshd kms-activate → 試 $InternalKmsServers → $PublicKmsServers,第一個 successful 就停 winget-install-chrome → winget install Google.Chrome --silent --scope machine remove-windows-ai → irm zoicware/RemoveWindowsAI | iex -nonInteractive -AllOptions win11debloat → 下載 Raphire/Win11Debloat → -Silent -Config remove-uwp-apps → 22 個 AppxPackage(BingNews、Xbox*、MSTeams、Photos…) install-telegram → 下載 telegram.org/dl/desktop/win64 + /VERYSILENT cleanup-unattend-files → 刪 C:\Windows\Panther\unattend.xml(清明碼密碼殘留) ``` 這讓 `unattend.xml` 保持「只負責 Windows 本身的安裝+簡單到不會壞的 cmd」,其他事情外包給 `Setup.ps1`。職責分離之後,改軟體清單不需要動 XML,改部署流程也不需要動 Setup 邏輯。 > 為什麼把 `Setup.ps1` 放在 Ventoy USB 上、不直接 base64 內嵌進 `unattend.xml`:(1) `unattend.xml` 仍是 Windows SIM 可驗證的純 schema XML;(2) `Setup.ps1` 改完只要 `cp` 一次就更新,不用重 escape;(3) 每段 `Section` 寫到 `setup.log` 才方便排查。代價是:USB 必須留到第一次登入完成才能拔。 > > `remove-windows-ai` 跑的是 `main` 分支的腳本,**每次部署抓的版本可能不同**。要確定性、可稽核的部署,把 `Setup.ps1` 中對應 URL 的 `/main/` 換成 commit SHA。 --- ## 為什麼分成三個 Pass Windows Setup 有七個 pass,本檔只用三個。不是偷懶,是**三個 pass 對應三個時機**,各自只能做某些事: | Pass | 時機 | 此時系統長什麼樣 | 只能在這裡做的事 | |---|---|---|---| | `windowsPE` | 從 USB 開機、Windows 還沒裝 | 記憶體中的迷你作業系統 | 磁碟分割、選要裝哪個映像、產品序號 | | `specialize` | 映像展開完、第一次開機前 | C:\ 已存在但沒有使用者 | 電腦名稱、加網域、硬體相關 | | `oobeSystem` | 首次開機、OOBE 跑起來時 | 完整 Windows、等使用者建帳號 | 建本機帳號、AutoLogon、FirstLogonCommands | **每個欄位都有「必須」出現的 pass,放錯地方 Windows 就不讀。** 語系要在 `windowsPE` 設(給 WinPE 用)也要在 `oobeSystem` 設(給 OOBE 用)——這就是為什麼 `International-Core` 在檔案裡出現兩次,不是冗餘。 --- ## 妥協的地方 幾個「明知道不理想、但為了能動先這樣」的決定: | 妥協 | 為什麼這樣 | 正式環境怎麼改 | |---|---|---| | **密碼明碼** | 方便第一次測試,看得懂 | 用 Windows SIM 產生 Base64,或交給 MDT/SCCM 管 | | **本機帳號 + AutoLogon** | 純離線可跑 | 改成 Domain Join + 網域管理員登入 | | **ProtectYourPC=3** | 略過 OOBE 的隱私頁,預設行為 | 依公司政策調整,或用 GPO 蓋掉 | | **單一 Disk 0 + 整碟清空** | 對測試 VM 最安全的假設 | 多硬碟環境要加 `DiskID` 判斷邏輯 | | **`Setup.ps1` 用磁碟掃描從 Ventoy USB 拷貝** | 不需要架網路服務、跨網段也能跑、USB 拔掉也能在現場補救 | 內部 IT 場合改用 SCCM/Intune 派送,或乾脆 base64 內嵌進 XML 拿到完全自包含 | | **`$PublicKmsServers` 用第三方 KMS** | 沒授權主機也能跑通 | 把 `$InternalKmsServers` 改成自家 KMS 主機,公開那串不再被 fallback | 這些不是 bug,是**當前脈絡下的合理起點**。要產品化就對照這張表一個一個升級。 --- ## 對話的起點 這份檔案最初是從一個問題開始的: > 「透過回應檔案(Answer files,通常檔名為 unattend.xml)來自動化配置 Windows 安裝設定。自動處理像是語系選擇、建立使用者帳號、輸入序號,甚至是執行自定義的腳本。」 答案就是上面講的那套設計。剩下的事(產 USB、跑 VM、除錯)都寫在 `QUICKSTART.md`,每個欄位做什麼事寫在 `README.md`。 這份 `SUMMARY.md` 只想講一件事:**為什麼這份 XML 長這樣。** 以後如果要改,知道每條線在撐著什麼,就不會拆錯地方。