新增 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:
18
CLAUDE.md
18
CLAUDE.md
@@ -4,12 +4,13 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||||||
|
|
||||||
## Repository nature
|
## 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.
|
- `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.
|
- `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:
|
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`.
|
- **`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.
|
- **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.)
|
- **`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
|
## 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.
|
- 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"`.
|
- 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.
|
||||||
|
|||||||
142
DEPLOY.md
142
DEPLOY.md
@@ -26,9 +26,12 @@ USB 根目錄兩個 ISO:
|
|||||||
└── ventoy/
|
└── ventoy/
|
||||||
├── ventoy.json ← 對應兩個 ISO,都指向同一份 template
|
├── ventoy.json ← 對應兩個 ISO,都指向同一份 template
|
||||||
└── script/
|
└── script/
|
||||||
└── unattend.xml ← 與 repo 根目錄的 unattend.xml 相同
|
├── unattend.xml ← 與 repo 根目錄的 unattend.xml 相同
|
||||||
|
└── Setup.ps1 ← 與 repo 根目錄的 Setup.ps1 相同(首次登入自動複製到 C:\Scripts\)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`Setup.ps1` **必須跟 unattend.xml 放在同一個 `/ventoy/script/` 目錄下**。`unattend.xml` 的 `FirstLogonCommands` Order 4 會掃所有掛載的磁碟,找到第一個有 `\ventoy\script\Setup.ps1` 的就複製到 `C:\Scripts\Setup.ps1`,所以這個路徑寫死,不能搬。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## USB 上的 `ventoy.json`
|
## USB 上的 `ventoy.json`
|
||||||
@@ -80,6 +83,7 @@ USB 根目錄兩個 ISO:
|
|||||||
# 從 repo 根目錄執行
|
# 從 repo 根目錄執行
|
||||||
mkdir -p /Volumes/Ventoy/ventoy/script
|
mkdir -p /Volumes/Ventoy/ventoy/script
|
||||||
cp unattend.xml /Volumes/Ventoy/ventoy/script/unattend.xml
|
cp unattend.xml /Volumes/Ventoy/ventoy/script/unattend.xml
|
||||||
|
cp Setup.ps1 /Volumes/Ventoy/ventoy/script/Setup.ps1
|
||||||
|
|
||||||
# 寫 ventoy.json(見上一節內容,或直接手編)
|
# 寫 ventoy.json(見上一節內容,或直接手編)
|
||||||
# 注意:image 檔名必須跟 USB 上的 ISO 檔名完全一致
|
# 注意:image 檔名必須跟 USB 上的 ISO 檔名完全一致
|
||||||
@@ -93,17 +97,20 @@ dot_clean /Volumes/Ventoy/ventoy
|
|||||||
```bash
|
```bash
|
||||||
ls /Volumes/Ventoy/ventoy/ /Volumes/Ventoy/ventoy/script/
|
ls /Volumes/Ventoy/ventoy/ /Volumes/Ventoy/ventoy/script/
|
||||||
diff unattend.xml /Volumes/Ventoy/ventoy/script/unattend.xml # 應無輸出
|
diff unattend.xml /Volumes/Ventoy/ventoy/script/unattend.xml # 應無輸出
|
||||||
|
diff Setup.ps1 /Volumes/Ventoy/ventoy/script/Setup.ps1 # 應無輸出
|
||||||
cat /Volumes/Ventoy/ventoy/ventoy.json
|
cat /Volumes/Ventoy/ventoy/ventoy.json
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**改 `Setup.ps1` 後沒重新 `cp` 是部署最常見的失誤**:FirstLogonCommands 從 USB 讀的是 `/ventoy/script/Setup.ps1` 那份,repo 裡改了不會自己同步上去。每次調 Setup.ps1 都要記得跑 `cp Setup.ps1 /Volumes/Ventoy/ventoy/script/Setup.ps1` 再 `dot_clean`。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 換 ISO 時要改什麼
|
## 換 ISO 時要改什麼
|
||||||
|
|
||||||
1. 把新 ISO 丟到 USB 根目錄。
|
1. 把新 ISO 丟到 USB 根目錄。
|
||||||
2. 編輯 `/Volumes/Ventoy/ventoy/ventoy.json`,把對應條目的 `image` 換成新檔名(或新增一條)。
|
2. 編輯 `/Volumes/Ventoy/ventoy/ventoy.json`,把對應條目的 `image` 換成新檔名(或新增一條)。
|
||||||
3. **不用**動 `/Volumes/Ventoy/ventoy/script/unattend.xml`,除非要改 Windows 版本特有設定。
|
3. **不用**動 `/Volumes/Ventoy/ventoy/script/unattend.xml` 與 `Setup.ps1`,除非要改 Windows 版本特有設定。
|
||||||
4. 若 repo 裡的 `unattend.xml` 有更新,重跑一次 `cp` 與 `dot_clean` 即可。
|
4. 若 repo 裡的 `unattend.xml` 或 `Setup.ps1` 有更新,重跑一次 `cp` 與 `dot_clean` 即可。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -113,53 +120,97 @@ cat /Volumes/Ventoy/ventoy/ventoy.json
|
|||||||
|
|
||||||
| Order | 動作 | 需要網路? |
|
| Order | 動作 | 需要網路? |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| 1 | `powershell -File C:\Scripts\Setup.ps1` | 看腳本內容 |
|
| 1 | `net accounts /maxpwage:unlimited`(密碼永不過期) | 否 |
|
||||||
| 2 | `net accounts /maxpwage:unlimited`(密碼永不過期) | 否 |
|
| 2 | `Rename-Computer` 把主機名改成 `PC-XXXXX`(重開後生效) | 否 |
|
||||||
| 3 | `Rename-Computer` 把主機名改成 `PC-XXXXX`(重開後生效) | 否 |
|
| 3 | `net user user 1234 /add`+`/logonpasswordchg:yes`(建立一般使用者 `user`,首次登入強制改密;之後 Setup.ps1 會把它改名成 `User`) | 否 |
|
||||||
| 4 | `net user user 1234 /add`+`/logonpasswordchg:yes`(建立一般使用者 `user`,首次登入強制改密) | 否 |
|
| 4 | 掃磁碟找 `\ventoy\script\Setup.ps1` 並複製到 `C:\Scripts\Setup.ps1`(log: `firstlogon-copysetup.log`) | 否(從 Ventoy USB 讀) |
|
||||||
| 5 | 等網路→`Add-WindowsCapability OpenSSH.Server`+`Set-Service sshd Automatic`+`Start-Service sshd`(log: `firstlogon-sshd.log`) | **是** |
|
| 5 | `powershell -File C:\Scripts\Setup.ps1`(13 段 post-install,見下節,log: `setup.log`) | 大部分 **是** |
|
||||||
| 6 | 等網路→下載 `https://telegram.org/dl/desktop/win64` → 靜默安裝(`/VERYSILENT /SUPPRESSMSGBOXES /NORESTART`,log: `firstlogon-telegram.log`) | **是** |
|
| 6 | `shutdown /r /t 5` 重開機 | 否 |
|
||||||
| 7 | `shutdown /r /t 5` 重開機 | 否 |
|
|
||||||
|
|
||||||
### 新加入的 `user` 帳號
|
Order 4/5 是改版重點:把原本散在 `FirstLogonCommands` 裡的 OpenSSH / Telegram / RemoveWindowsAI 三條搬進 `Setup.ps1`,並補上 10 個新動作。
|
||||||
|
|
||||||
|
### `Setup.ps1` 做了什麼(13 段,按執行順序)
|
||||||
|
|
||||||
|
每段都用 `Section` 包裝,會把 `=== <name> START/DONE/ERR ===` 寫到 `C:\Windows\Temp\setup.log`,可以照名字逐段排查。
|
||||||
|
|
||||||
|
**Phase 1(不需網路、本機設定):**
|
||||||
|
|
||||||
|
| 段名 | 在做什麼 |
|
||||||
|
|---|---|
|
||||||
|
| `firewall-ssh-22` | 建 `SSH TCP 22 (Allow Inbound)` 防火牆規則,三個 profile 都啟用 |
|
||||||
|
| `rdp-enable-3389` | `fDenyTSConnections=0`、`UserAuthentication=1`、啟用 `Remote Desktop` 防火牆群組 |
|
||||||
|
| `icmp-echo-allow` | 允許 ICMPv4 Echo Request inbound(讓外部 ping 得到) |
|
||||||
|
| `winrm-enable` | `Enable-PSRemoting`+`winrm quickconfig`+`Basic`/`AllowUnencrypted=true`+`TrustedHosts=*` |
|
||||||
|
| `onedrive-remove` | 殺 OneDrive process、跑兩處 `OneDriveSetup.exe /uninstall`、清啟動鍵與 CLSID、刪殘留資料夾 |
|
||||||
|
| `cortana-websearch-off` | `AllowCortana=0`、`DisableWebSearch=1`、`ConnectedSearchUseWeb=0`、`DisableSearchBoxSuggestions=1` |
|
||||||
|
| `apply-ui-ime-default-profile` | `reg load` Default profile NTUSER.DAT,寫入:顯示副檔名/隱藏檔、工作列靠左、Dark mode、隱藏搜尋框、停 Spotlight、英數鍵盤排第一+注音排第二、CHT 預設英數模式、停切換 Hotkey |
|
||||||
|
| `rename-hide-accounts` | `Rename-LocalUser user → User`、`SpecialAccounts\UserList\Admin=0`(從登入畫面隱藏 Admin) |
|
||||||
|
|
||||||
|
**Phase 2(需網路、最多等 60 秒):**
|
||||||
|
|
||||||
|
| 段名 | 在做什麼 |
|
||||||
|
|---|---|
|
||||||
|
| `wait-network` | 最多 30 回合×2 秒等 ICMP 通到 8.8.8.8 |
|
||||||
|
| `openssh-server-install` | `Add-WindowsCapability OpenSSH.Server`+`Set-Service sshd Automatic`+`Start-Service sshd` |
|
||||||
|
| `kms-activate` | 依序試 `$InternalKmsServers` → `$PublicKmsServers`(kms.digiboy.ir 等),跑 `slmgr /skms`+`/ato`,遇到第一個 `successful/成功` 就跳出 |
|
||||||
|
| `winget-install-chrome` | `winget install Google.Chrome --silent --scope machine`(Win10 沒裝 App Installer 會 skip) |
|
||||||
|
| `remove-windows-ai` | `irm https://raw.githubusercontent.com/zoicware/RemoveWindowsAI/main/RemoveWindowsAi.ps1` 後 `& -nonInteractive -AllOptions -EnableLogging` |
|
||||||
|
| `win11debloat` | 下載 [Raphire/Win11Debloat](https://github.com/Raphire/Win11Debloat) `master.zip`、解壓、寫出 `unattend-config.json`(清單與 `windows-remote-toolkit/config/win11debloat-config.json` 同步)、跑 `Win11Debloat.ps1 -Silent -Config ... -NoRestartExplorer` |
|
||||||
|
| `remove-uwp-apps` | 依 `$UwpRemoveList`(22 個 AppxPackage:BingNews/BingWeather/Xbox*/MSTeams/Photos/...)逐個 `Remove-AppxPackage -AllUsers` 與 `Remove-AppxProvisionedPackage` |
|
||||||
|
| `install-telegram` | 下載 `https://telegram.org/dl/desktop/win64` 跑 `/VERYSILENT /SUPPRESSMSGBOXES /NORESTART` |
|
||||||
|
| `cleanup-unattend-files` | 刪掉 `C:\Windows\Panther\unattend.xml` 與 `C:\Windows\Panther\Unattend\unattend.xml`(含明碼密碼) |
|
||||||
|
|
||||||
|
### `user` → `User` 帳號
|
||||||
|
|
||||||
- **用途**:給使用者日常使用,和部署用的管理員 `Admin` 分開。
|
- **用途**:給使用者日常使用,和部署用的管理員 `Admin` 分開。
|
||||||
- **權限**:一般 `Users` 群組(非 Administrators)。需要管理員權限時走 UAC。
|
- **建立**:`unattend.xml` Order 3 用 `net user user 1234 /add`(首字小寫)。
|
||||||
|
- **改名**:`Setup.ps1` 的 `rename-hide-accounts` 段把 `user` 改成 `User`(首字大寫)。要先建小寫再改名是因為 `Rename-LocalUser` 必須對既有帳號操作;如果 `Setup.ps1` 沒跑完,最差情況是有個 `user` 還能登入。
|
||||||
|
- **權限**:一般 `Users` 群組(非 Administrators)。需要管理員權限時走 UAC(用 `Admin/P@ssw0rd!`)。
|
||||||
- **首次登入強制改密**:`/logonpasswordchg:yes` 會在下次登入時跳「密碼必須變更」。可以改成複雜密碼。
|
- **首次登入強制改密**:`/logonpasswordchg:yes` 會在下次登入時跳「密碼必須變更」。可以改成複雜密碼。
|
||||||
- **如果 `net user` 因為 Windows 密碼政策拒絕 `1234`**:整條 Order 4 會失敗但不會擋後面,手動於 PowerShell 以 `New-LocalUser`+更寬的密碼重跑。
|
- **如果 `net user` 因為 Windows 密碼政策拒絕 `1234`**:Order 3 會失敗但不會擋後面,手動於 PowerShell 以 `New-LocalUser`+更寬的密碼重跑,再讓 `Setup.ps1` 改名。
|
||||||
|
|
||||||
### OpenSSH Server(Order 5)
|
### Order 4 的磁碟掃描沒找到 Setup.ps1 怎麼辦
|
||||||
|
|
||||||
- 走 Windows 內建 Feature on Demand,**會從 Windows Update 下載** `OpenSSH.Server~~~~0.0.1.0`。需要外網。
|
Order 4 會逐個檢查所有掛載中的 FileSystem PSDrive,找 `\ventoy\script\Setup.ps1`。沒找到就寫 `NOT FOUND` 到 `C:\Windows\Temp\firstlogon-copysetup.log`,Order 5 接著用 `if exist` 判斷,找不到就寫 `SETUP_NOT_FOUND` 到 `firstlogon-runsetup.log` 然後跳過。原因通常是:
|
||||||
- 完成後 `sshd` 服務設成 `Automatic`(每次開機自動啟動),防火牆規則由 FoD 自動建立(TCP 22 inbound allow)。
|
|
||||||
- 驗證:`Get-Service sshd` 應該 `Running`;`Get-NetFirewallRule -Name OpenSSH-Server-In-TCP` 應該 `Enabled=True`。
|
|
||||||
- 用來遠端登入:`ssh Admin@<機器 IP>` 或 `ssh user@<機器 IP>`。初次使用要先在 `%ProgramData%\ssh\administrators_authorized_keys` 加公鑰(Windows OpenSSH 對 Administrators 群組的授權金鑰路徑特殊,不走 `~/.ssh/authorized_keys`)。
|
|
||||||
|
|
||||||
### Telegram(Order 6)的注意事項
|
| 現象 | 原因 | 對策 |
|
||||||
|
|
||||||
- **要有網路**:OOBE 裡 `SkipMachineOOBE=true` 跳過了網路設定精靈,所以目標機必須有 Ethernet+DHCP,或本身已記住 Wi-Fi。這次有 log 可以驗證—`C:\Windows\Temp\firstlogon-telegram.log` 會寫 `NetWait=?s`、`Downloaded ?B`、`Exit ?`。
|
|
||||||
- **網路等待上限 60 秒**:Order 5 和 Order 6 都各跑一次 `Test-Connection 8.8.8.8` 迴圈,最多等 30 回合×2 秒;ICMP 被擋住的環境會等完整 60 秒但 HTTPS 其實可用,也能跑下去。
|
|
||||||
- **per-user 安裝**:Telegram 安裝程式預設裝到 Admin 這個帳號底下,**新建的 `user` 看不到**。要全機安裝就在 `ArgumentList` 加 `'/ALLUSERS'`。
|
|
||||||
- **抓的是 latest 版**:每次部署抓當下最新版。要鎖版本改用 `https://updates.tdesktop.com/tsetup/tsetup-x64.X.Y.Z.exe` 直鏈。
|
|
||||||
- **順序不能搬到 reboot 後**:`shutdown /r /t 5` 只是排程重開機、立即回傳,若放在 Telegram 前面,下載安裝會被 5 秒後的關機砍掉。
|
|
||||||
|
|
||||||
### 上次部署 Telegram 無聲失敗如何排查
|
|
||||||
|
|
||||||
這次每一條都有 log:登入 `Admin` 後看
|
|
||||||
|
|
||||||
```
|
|
||||||
notepad C:\Windows\Temp\firstlogon-telegram.log
|
|
||||||
notepad C:\Windows\Temp\firstlogon-sshd.log
|
|
||||||
```
|
|
||||||
|
|
||||||
常見會看到的值:
|
|
||||||
|
|
||||||
| Log 內容 | 代表 | 對策 |
|
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `NetWait=60s` + `ERR ...` | 60 秒內 ICMP 沒通,後面可能也失敗 | 接網路線、或在 OOBE 後手動連 Wi-Fi 後重跑指令 |
|
| `NOT FOUND` | Ventoy USB 在 OOBE 完成前被拔掉 | 別拔,等 reboot 後再拔 |
|
||||||
| `Downloaded 0B` 或 `ERR` 含 `SSL/TLS` | TLS 交握失敗 | 檢查系統時間是否對;老機器可能要裝 Windows Update for TLS 1.2 |
|
| `NOT FOUND` | `Setup.ps1` 沒 cp 到 `/Volumes/Ventoy/ventoy/script/` | 重新 `cp` + `dot_clean` 後再插上重裝 |
|
||||||
| `Exit 0` 但桌面沒 Telegram 捷徑 | 裝成功但 per-user 安裝、不是登入那個帳號 | 以 Admin 登入,捷徑在 `%APPDATA%\Microsoft\Windows\Start Menu\Programs\Telegram Desktop` |
|
| 找到但 Order 5 報 `Cannot run script ... not digitally signed` | 機器 ExecutionPolicy 是 Restricted | 不會發生,Order 5 用 `-ExecutionPolicy Bypass` 強蓋 |
|
||||||
| 完全沒 log 檔 | 這條 `SynchronousCommand` 根本沒跑(通常是 `AutoLogon.Enabled=false` 或 XML 沒注入) | 查 `C:\Windows\Panther\UnattendGC\setupact.log` |
|
| 找到了但 Setup.ps1 早早 ERR | 看 `C:\Windows\Temp\setup.log` 的 `=== <name> ERR ===` 行 | 對照上面的段名表逐段重跑 |
|
||||||
|
|
||||||
|
### 排查 Setup.ps1 失敗
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
notepad C:\Windows\Temp\firstlogon-copysetup.log # Order 4:USB 掃描結果
|
||||||
|
notepad C:\Windows\Temp\firstlogon-runsetup.log # Order 5:找不到 Setup.ps1 才會有
|
||||||
|
notepad C:\Windows\Temp\setup.log # Setup.ps1 主 log
|
||||||
|
```
|
||||||
|
|
||||||
|
`setup.log` 每段都有 `=== <name> START === ... === <name> DONE === ` 或 `=== <name> ERR: <msg> ===`。失敗段可以登入 `Admin` 後手動:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
powershell -ExecutionPolicy Bypass -File C:\Scripts\Setup.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
整支重跑(每段都是冪等的);或只挑某段:把 `Setup.ps1` 開起來,把那段 `Section '...'` 區塊的內容貼進 PowerShell 視窗即可。
|
||||||
|
|
||||||
|
### RemoveWindowsAI 補充
|
||||||
|
|
||||||
|
- **在做什麼**:跑 [zoicware/RemoveWindowsAI](https://github.com/zoicware/RemoveWindowsAI) 的 `RemoveWindowsAi.ps1`,一次把 Copilot(系統+Edge+Office)、Recall+排程任務、Input Insights、AI Fabric、Click to Do、Paint 實驗、Voice Access、Gaming Copilot、Notepad Rewrite、Photos AI 等 AI 元件全數關掉或移除(AppX、CBS、Registry、Group Policy 都會動)。
|
||||||
|
- **參數**:`-nonInteractive`(不跳互動選單)+`-AllOptions`(全部選項都跑)+`-EnableLogging`(讓腳本自己也寫 log)。
|
||||||
|
- **執行時間**:視目標機速度,通常 30 秒–3 分鐘。移除 CBS package 那段最慢。
|
||||||
|
- **抓的是 `main` 分支**:每次部署可能拿到不同版本。要鎖定版本就把 `Setup.ps1` 中對應 URL 的 `/main/` 換成 commit SHA。
|
||||||
|
- **可逆性**:腳本本身有 `-revertMode` 與 `-backupMode`,要還原的話可以登入後手動以 `-revertMode` 重跑;但 `-AllOptions` 跑完有些 CBS package 已被刪,`-revertMode` 不一定救得回,需要搭配 `-RunWinUpdateRepair` 從 Windows Update 重取。
|
||||||
|
- **風險面**:移除「Nonremovable」AppX、刪 CBS package 屬於侵入性操作,可能導致未來 Windows Update 失敗或特定功能殘缺。部署前建議先在 VM 跑過一次。
|
||||||
|
|
||||||
|
### Telegram 補充
|
||||||
|
|
||||||
|
- **要有網路**:OOBE 裡 `SkipMachineOOBE=true` 跳過了網路設定精靈,所以目標機必須有 Ethernet+DHCP,或本身已記住 Wi-Fi。
|
||||||
|
- **網路等待上限 60 秒**:`Setup.ps1` 開頭的 `wait-network` 跑一次 `Test-Connection 8.8.8.8` 迴圈,最多等 30 回合×2 秒;ICMP 被擋住的環境會等完整 60 秒但 HTTPS 其實可用,也能跑下去。
|
||||||
|
- **per-user 安裝**:Telegram 安裝程式預設裝到 Admin 這個帳號底下,**新建的 `User` 看不到**。要全機安裝就在 `Setup.ps1` 的 `install-telegram` 段把 `ArgumentList` 加上 `'/ALLUSERS'`。
|
||||||
|
- **抓的是 latest 版**:每次部署抓當下最新版。要鎖版本改用 `https://updates.tdesktop.com/tsetup/tsetup-x64.X.Y.Z.exe` 直鏈。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -168,7 +219,8 @@ notepad C:\Windows\Temp\firstlogon-sshd.log
|
|||||||
沿用 repo 預設值,下列情況會失敗,改用前要先處理:
|
沿用 repo 預設值,下列情況會失敗,改用前要先處理:
|
||||||
|
|
||||||
- **目標機若是 BIOS/MBR**:`<DiskConfiguration>` 目前寫死 UEFI+GPT,分割階段會報錯。
|
- **目標機若是 BIOS/MBR**:`<DiskConfiguration>` 目前寫死 UEFI+GPT,分割階段會報錯。
|
||||||
- **ARM64 機器**:所有 `<component>` 的 `processorArchitecture="amd64"` 要改成 `arm64`。
|
- **ARM64 機器**:所有 `<component>` 的 `processorArchitecture="amd64"` 要改成 `arm64`;同時 `Setup.ps1` 內的 `OneDriveSetup.exe` SysWOW64 路徑、`winget` Chrome `--scope machine` 等部分行為要重驗。
|
||||||
- **`C:\Scripts\Setup.ps1` 未提供**:`FirstLogonCommands` 會在首次登入時報「找不到檔案」。解法:在 `FirstLogonCommands` 前段加一條從 USB 複製的指令,或另外烘進 image,或乾脆刪掉 `Order=1` 那條 `SynchronousCommand`。
|
- **Ventoy USB 在 reboot 前被拔掉**:Order 4 會找不到 `Setup.ps1`,13 段 post-install 全部 skip。請等到第二次重開機進到登入畫面後才拔。
|
||||||
- **密碼是明碼**(`<PlainText>true</PlainText>`):僅適合測試用途;正式部署請用 Windows SIM 產 Base64。
|
- **密碼是明碼**(`<PlainText>true</PlainText>`):僅適合測試用途;正式部署請用 Windows SIM 產 Base64。
|
||||||
- **Telegram 安裝需要外網**:目標機沒網路的話,Order 4 會失敗但不影響整個流程走完。
|
- **Phase 2 全部需要外網**:目標機沒網路的話,從 `wait-network` 之後 7 段都會 ERR;Phase 1 的 8 段(防火牆、RDP、ICMP、WinRM、OneDrive 移除、Cortana 關閉、Default profile UI/IME、改名隱藏帳號)仍會成功完成。
|
||||||
|
- **公開 KMS 啟用**:`Setup.ps1` 預設用 `kms.digiboy.ir` 等第三方 KMS,這對內網/正式環境不適合。把 `$InternalKmsServers` 改成你自己的 KMS 主機就好。
|
||||||
|
|||||||
@@ -13,18 +13,25 @@
|
|||||||
- [ ] Windows 10 或 11 官方 ISO(從 [microsoft.com](https://www.microsoft.com/software-download) 取得)
|
- [ ] Windows 10 或 11 官方 ISO(從 [microsoft.com](https://www.microsoft.com/software-download) 取得)
|
||||||
- [ ] 已刷好 Ventoy 的 USB 隨身碟(≥ 16 GB 建議,因為會同時放 ISO 與 script)
|
- [ ] 已刷好 Ventoy 的 USB 隨身碟(≥ 16 GB 建議,因為會同時放 ISO 與 script)
|
||||||
- [ ] 一台測試機或 VM(強烈建議先在 VM 驗證)
|
- [ ] 一台測試機或 VM(強烈建議先在 VM 驗證)
|
||||||
- [ ] 本專案的 `unattend.xml` 與 `ventoy.json`
|
- [ ] 本專案的 `unattend.xml`、`Setup.ps1`、`ventoy.json`
|
||||||
|
|
||||||
**編輯 `unattend.xml` 的必改項目:**
|
**編輯 `unattend.xml` 的必改項目:**
|
||||||
|
|
||||||
| 行為 | 原值 | 要改成 |
|
| 行為 | 原值 | 要改成 |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| 產品序號 | `XXXXX-XXXXX-XXXXX-XXXXX-XXXXX` | 你的序號,或 KMS 通用金鑰,或整個 `<ProductKey>` 節點刪掉 |
|
| 產品序號 | Win11/10 Pro GVLK(`W269N-WFGWX-...`) | 你的序號,或留 GVLK 用 KMS,或整個 `<ProductKey>` 節點刪掉 |
|
||||||
| 管理員密碼 | `P@ssw0rd!`(兩處:`LocalAccount` 與 `AutoLogon`) | 你的密碼 |
|
| 管理員密碼 | `P@ssw0rd!`(兩處:`LocalAccount` 與 `AutoLogon`) | 你的密碼 |
|
||||||
| 組織名 | `YourCompany` | 你的組織 |
|
| 組織名 | `YourCompany` | 你的組織 |
|
||||||
| 腳本路徑 | `C:\Scripts\Setup.ps1` | 留著或改路徑;不要執行就刪 `<SynchronousCommand Order=1>` |
|
|
||||||
| `DiskID` | `$$VT_WINDOWS_DISK_1ST_NONVTOY$$`(Ventoy 變數) | **保留不動**。Ventoy 會在執行期替換成「第一顆非 Ventoy USB 的磁碟」 |
|
| `DiskID` | `$$VT_WINDOWS_DISK_1ST_NONVTOY$$`(Ventoy 變數) | **保留不動**。Ventoy 會在執行期替換成「第一顆非 Ventoy USB 的磁碟」 |
|
||||||
|
|
||||||
|
**編輯 `Setup.ps1` 的常改項目(在檔案頂部):**
|
||||||
|
|
||||||
|
| 變數 | 預設 | 要改成 |
|
||||||
|
|---|---|---|
|
||||||
|
| `$InternalKmsServers` | `@()` 空陣列 | 你內網 KMS 主機,例:`@('kms.your-corp.com')`;空陣列就 fallback 到公開 KMS |
|
||||||
|
| `$UwpRemoveList` | 22 個 Microsoft 預載 app | 想多移/少移就改這個陣列 |
|
||||||
|
| `$Win11DebloatConfigJson` | toolkit 的預設(暗色、工作列靠左、停 Bing/Copilot/Recall…) | 不喜歡哪個 tweak 就把該行 `Value` 改成 `false` |
|
||||||
|
|
||||||
**編輯 `ventoy.json` 的必改項目:**
|
**編輯 `ventoy.json` 的必改項目:**
|
||||||
|
|
||||||
| 欄位 | 原值 | 要改成 |
|
| 欄位 | 原值 | 要改成 |
|
||||||
@@ -45,7 +52,8 @@
|
|||||||
└── ventoy/
|
└── ventoy/
|
||||||
├── ventoy.json ← 本 repo 的 ventoy.json
|
├── ventoy.json ← 本 repo 的 ventoy.json
|
||||||
└── script/
|
└── script/
|
||||||
└── unattend.xml ← 本 repo 的 unattend.xml(改完的)
|
├── unattend.xml ← 本 repo 的 unattend.xml(改完的)
|
||||||
|
└── Setup.ps1 ← 本 repo 的 Setup.ps1(首次登入會被自動複製到 C:\Scripts\)
|
||||||
```
|
```
|
||||||
|
|
||||||
macOS / Linux 範例:
|
macOS / Linux 範例:
|
||||||
@@ -56,8 +64,11 @@ cp Windows.iso /Volumes/Ventoy/Win11.iso
|
|||||||
mkdir -p /Volumes/Ventoy/ventoy/script
|
mkdir -p /Volumes/Ventoy/ventoy/script
|
||||||
cp ventoy.json /Volumes/Ventoy/ventoy/ventoy.json
|
cp ventoy.json /Volumes/Ventoy/ventoy/ventoy.json
|
||||||
cp unattend.xml /Volumes/Ventoy/ventoy/script/unattend.xml
|
cp unattend.xml /Volumes/Ventoy/ventoy/script/unattend.xml
|
||||||
|
cp Setup.ps1 /Volumes/Ventoy/ventoy/script/Setup.ps1
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **改 `Setup.ps1` 後記得重 `cp`**。`unattend.xml` 的 FirstLogonCommands 從 USB 上的 `/ventoy/script/Setup.ps1` 抓檔案,不是從 repo。沒同步的話部署到的 Windows 會跑舊版。
|
||||||
|
|
||||||
> 不用改名成 `autounattend.xml`,也不用把 XML 塞進 ISO 根目錄。Ventoy 的 Auto Install plugin 會在開機時依 `ventoy.json` 把 XML 注入給 Windows Setup。
|
> 不用改名成 `autounattend.xml`,也不用把 XML 塞進 ISO 根目錄。Ventoy 的 Auto Install plugin 會在開機時依 `ventoy.json` 把 XML 注入給 Windows Setup。
|
||||||
|
|
||||||
**多個 ISO / 多套範本**:`ventoy.json` 的 `auto_install` 是陣列,每個 ISO 一條目;`template` 也可以是陣列,開機時 Ventoy 會顯示選單讓你挑。
|
**多個 ISO / 多套範本**:`ventoy.json` 的 `auto_install` 是陣列,每個 ISO 一條目;`template` 也可以是陣列,開機時 Ventoy 會顯示選單讓你挑。
|
||||||
@@ -102,9 +113,9 @@ Ventoy 支援寫進 VHD/VDI,可直接掛進 Hyper-V / VirtualBox 當第一顆
|
|||||||
| 第一次重開 | 黑屏 → 「請稍候」 | 1–2 分鐘 |
|
| 第一次重開 | 黑屏 → 「請稍候」 | 1–2 分鐘 |
|
||||||
| OOBE 略過 | **不該出現任何選單**;直接跳過 | 秒殺 |
|
| OOBE 略過 | **不該出現任何選單**;直接跳過 | 秒殺 |
|
||||||
| AutoLogon | 以 `Admin` 自動登入到桌面(顯示名稱是「Administrator」) | — |
|
| AutoLogon | 以 `Admin` 自動登入到桌面(顯示名稱是「Administrator」) | — |
|
||||||
| FirstLogonCommands | 閃過一連串 PowerShell / cmd 黑視窗(`Setup.ps1`、建 user 帳號、裝 OpenSSH、裝 Telegram) | 1–3 分鐘(含 FoD 下載) |
|
| FirstLogonCommands | 閃過一連串 PowerShell / cmd 黑視窗(密碼永不過期、改主機名、建 user 帳號、從 USB 拷 Setup.ps1、跑 Setup.ps1 的 13 段 post-install) | 5–15 分鐘(含 FoD 下載、Win11Debloat 解壓、winget Chrome、AI 元件移除) |
|
||||||
| 重開機 | 5 秒倒數後重啟 | — |
|
| 重開機 | 5 秒倒數後重啟 | — |
|
||||||
| 第二次開機 | 看到登入畫面,有 `Administrator`(= `Admin` 帳號)和 `user` 兩個選項 | — |
|
| 第二次開機 | 看到登入畫面,有 `User` 一個選項(`Admin` 已被 SpecialAccounts 隱藏;輸入帳號名仍可登入) | — |
|
||||||
|
|
||||||
**正常就是全程不需要碰鍵盤滑鼠。** 如果中途卡在 OOBE 畫面要你選語系/建帳號 → 去看第 5 節除錯。
|
**正常就是全程不需要碰鍵盤滑鼠。** 如果中途卡在 OOBE 畫面要你選語系/建帳號 → 去看第 5 節除錯。
|
||||||
|
|
||||||
@@ -126,14 +137,23 @@ Ventoy 支援寫進 VHD/VDI,可直接掛進 Hyper-V / VirtualBox 當第一顆
|
|||||||
reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v AutoAdminLogon
|
reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v AutoAdminLogon
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **改密碼**:範本的 `Admin/P@ssw0rd!` 和 `user/1234` 都是給自動化用的,上線前換掉。`user` 登入時會被強制改密,`Admin` 要自己 `net user Admin *` 改。
|
3. **改密碼**:範本的 `Admin/P@ssw0rd!` 和 `User/1234` 都是給自動化用的,上線前換掉。`User` 登入時會被強制改密,`Admin` 要自己 `net user Admin *` 改。
|
||||||
|
|
||||||
4. **看一下 FirstLogon log**(本輪新增):
|
4. **看一下 FirstLogon / Setup log**:
|
||||||
```powershell
|
```powershell
|
||||||
notepad C:\Windows\Temp\firstlogon-sshd.log # OpenSSH 安裝結果
|
notepad C:\Windows\Temp\firstlogon-copysetup.log # Order 4:Setup.ps1 是否從 Ventoy USB 複製成功
|
||||||
notepad C:\Windows\Temp\firstlogon-telegram.log # Telegram 下載與安裝結果
|
notepad C:\Windows\Temp\firstlogon-runsetup.log # Order 5:Setup.ps1 找不到才會有此檔
|
||||||
|
notepad C:\Windows\Temp\setup.log # Setup.ps1 主 log,13 段每段都寫 START/DONE/ERR
|
||||||
|
```
|
||||||
|
找 `=== <name> ERR: ... ===` 行就是失敗段;`Setup.ps1` 每段都冪等,可以登入 `Admin` 後 `powershell -ExecutionPolicy Bypass -File C:\Scripts\Setup.ps1` 整支重跑,或挑特定段貼進視窗。
|
||||||
|
|
||||||
|
5. **驗證遠端管理可用**(從別台機器試):
|
||||||
|
```bash
|
||||||
|
ping <機器 IP> # ICMP 應該回應
|
||||||
|
ssh Admin@<機器 IP> # OpenSSH 22/TCP,密碼 P@ssw0rd!(金鑰要先放 administrators_authorized_keys)
|
||||||
|
# RDP:用任何 RDP client 連 <機器 IP>:3389
|
||||||
|
# WinRM:5985(HTTP,AllowUnencrypted=true,TrustedHosts=*)
|
||||||
```
|
```
|
||||||
`Exit 0` 才是真的成功。前一輪 Telegram 無聲失敗就是因為當時沒有 log、無從判斷。
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -163,7 +183,8 @@ Shift + F10 在任何安裝畫面會開出命令提示字元,可以 `notepad`
|
|||||||
| `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` 不重複 |
|
||||||
| `Setup.ps1` 找不到 | 路徑不存在 | 先用其他方式把腳本放到 `C:\Scripts\`,或改用 `FirstLogonCommands` 從 USB 複製 |
|
| `Setup.ps1` 找不到(`firstlogon-copysetup.log` 寫 `NOT FOUND`) | Ventoy USB 太早被拔掉,或 `Setup.ps1` 沒 cp 到 `/ventoy/script/` | 等到第二次重開機進登入畫面後才拔;或重新 `cp Setup.ps1 /Volumes/Ventoy/ventoy/script/` |
|
||||||
|
| `Setup.ps1` 跑了但某段 ERR | 看 `C:\Windows\Temp\setup.log` 的 `=== <name> ERR: ... ===` | 修一修對應段,登入 `Admin` 後 `powershell -ExecutionPolicy Bypass -File C:\Scripts\Setup.ps1` 整支重跑 |
|
||||||
|
|
||||||
### 驗證 XML 語法
|
### 驗證 XML 語法
|
||||||
|
|
||||||
|
|||||||
42
README.md
42
README.md
@@ -8,8 +8,11 @@
|
|||||||
|
|
||||||
| 檔案 | 用途 |
|
| 檔案 | 用途 |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `unattend.xml` | 主回應檔,放入 USB/ISO 時需改名為 `autounattend.xml` |
|
| `unattend.xml` | 主回應檔,部署到 Ventoy USB 的 `/ventoy/script/unattend.xml`(**不**改名為 `autounattend.xml`,Ventoy Auto Install plugin 走 `ventoy.json` 注入) |
|
||||||
|
| `Setup.ps1` | 首次登入後的 post-install PowerShell;部署到 USB 的 `/ventoy/script/Setup.ps1`,FirstLogonCommands 會掃磁碟自動複製到 `C:\Scripts\` 後執行 |
|
||||||
|
| `ventoy.json` | Ventoy Auto Install plugin 設定,把 ISO 對到 `unattend.xml` |
|
||||||
| `QUICKSTART.md` | 操作步驟:如何產生 USB、掛載 ISO、跑 VM 測試 |
|
| `QUICKSTART.md` | 操作步驟:如何產生 USB、掛載 ISO、跑 VM 測試 |
|
||||||
|
| `DEPLOY.md` | 本機 USB 實機部署紀錄+ FirstLogonCommands / Setup.ps1 詳細排查 |
|
||||||
| `SUMMARY.md` | 本專案的來由與設計脈絡 |
|
| `SUMMARY.md` | 本專案的來由與設計脈絡 |
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -42,7 +45,7 @@ Windows Setup 在不同階段套用不同 `<settings pass="...">`:
|
|||||||
### specialize
|
### specialize
|
||||||
|
|
||||||
- **Microsoft-Windows-Shell-Setup**
|
- **Microsoft-Windows-Shell-Setup**
|
||||||
- `ComputerName` — `PC-%RAND:5%`(5 位隨機數字)
|
- `ComputerName` — `*`(Setup 自動產生隨機名,`FirstLogonCommands` 再用 `Rename-Computer` 改成 `PC-XXXXX`,重開後生效)。**不可寫 `PC-%RAND:5%`**——那是 MDT/SCCM 變數,直接放這裡會被當字面字串送進 NetBIOS 驗證而失敗
|
||||||
- `TimeZone` — `Taipei Standard Time`
|
- `TimeZone` — `Taipei Standard Time`
|
||||||
- `RegisteredOwner` / `RegisteredOrganization`
|
- `RegisteredOwner` / `RegisteredOrganization`
|
||||||
|
|
||||||
@@ -54,13 +57,18 @@ Windows Setup 在不同階段套用不同 `<settings pass="...">`:
|
|||||||
- `UserAccounts.LocalAccounts` — 建立 `Admin`(Administrators 群組,密碼 `P@ssw0rd!`,明碼)
|
- `UserAccounts.LocalAccounts` — 建立 `Admin`(Administrators 群組,密碼 `P@ssw0rd!`,明碼)
|
||||||
- `AutoLogon` — 以 `Admin` 自動登入一次(`LogonCount=1`)
|
- `AutoLogon` — 以 `Admin` 自動登入一次(`LogonCount=1`)
|
||||||
- `FirstLogonCommands` — 按 `Order` 依序執行:
|
- `FirstLogonCommands` — 按 `Order` 依序執行:
|
||||||
1. `powershell.exe -ExecutionPolicy Bypass -File C:\Scripts\Setup.ps1`
|
1. `cmd.exe /c net accounts /maxpwage:unlimited`(密碼永不過期)
|
||||||
2. `cmd.exe /c net accounts /maxpwage:unlimited`(密碼永不過期)
|
2. `Rename-Computer` 改主機名成 `PC-XXXXX`(重開後生效)
|
||||||
3. `Rename-Computer` 改主機名成 `PC-XXXXX`(重開後生效)
|
3. 建立本機帳號 `user`(密碼 `1234`,`net user /logonpasswordchg:yes` 強制首次登入改密;之後 `Setup.ps1` 會把它改名成 `User`)
|
||||||
4. 建立本機帳號 `user`(密碼 `1234`,`net user /logonpasswordchg:yes` 強制首次登入改密)
|
4. 掃所有掛載中的 FileSystem PSDrive 找 `\ventoy\script\Setup.ps1`,複製到 `C:\Scripts\Setup.ps1`(log 寫到 `C:\Windows\Temp\firstlogon-copysetup.log`)
|
||||||
5. 等網路→`Add-WindowsCapability OpenSSH.Server` 安裝並 `Set-Service sshd Automatic` / `Start-Service sshd`(log 寫到 `C:\Windows\Temp\firstlogon-sshd.log`)
|
5. 跑 `C:\Scripts\Setup.ps1`(13 段 post-install 詳見 `Setup.ps1` 與 `DEPLOY.md`,log 寫到 `C:\Windows\Temp\setup.log`;找不到檔案時改寫 `firstlogon-runsetup.log` 並跳過)
|
||||||
6. 等網路→下載 `https://telegram.org/dl/desktop/win64` 並以 `/VERYSILENT /SUPPRESSMSGBOXES /NORESTART` 靜默安裝 Telegram(log 寫到 `C:\Windows\Temp\firstlogon-telegram.log`)
|
6. `shutdown /r /t 5`(重開機)
|
||||||
7. `shutdown /r /t 5`(重開機)
|
|
||||||
|
### `Setup.ps1` 涵蓋的 13 段(首次登入由 Order 5 執行)
|
||||||
|
|
||||||
|
**Phase 1(不需網路):** SSH 22/TCP 防火牆、RDP+3389、ICMP Echo Inbound、WinRM(`TrustedHosts=*`)、OneDrive 解除安裝、Cortana/Web Search 關閉、Default profile 套 Dark mode+顯示副檔名/隱藏檔+工作列靠左+預設英數鍵盤+停 IME 切換熱鍵、`user`→`User` 改名+從登入畫面隱藏 `Admin`。
|
||||||
|
|
||||||
|
**Phase 2(需網路,`wait-network` 後執行):** OpenSSH Server FoD、KMS 啟用(先試 `$InternalKmsServers` 再 fallback 公開 KMS)、winget 裝 Chrome、`zoicware/RemoveWindowsAI -AllOptions`、`Raphire/Win11Debloat -Silent -Config`、22 個 UWP AppxPackage 移除(與 `windows-remote-toolkit/config/remove-apps-config.json` 同步)、Telegram 靜默安裝、刪 `C:\Windows\Panther\unattend.xml` 殘留。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -82,10 +90,10 @@ Windows Setup 在不同階段套用不同 `<settings pass="...">`:
|
|||||||
|
|
||||||
| 方式 | 位置 | 檔名 |
|
| 方式 | 位置 | 檔名 |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **Ventoy Auto Install plugin(本 repo 預設)** | Ventoy USB 的 `/ventoy/script/`,並在 `/ventoy/ventoy.json` 指定 | 任意(本 repo 用 `unattend.xml`) |
|
| **Ventoy Auto Install plugin(本 repo 預設)** | Ventoy USB 的 `/ventoy/script/unattend.xml`+`/ventoy/script/Setup.ps1`,並在 `/ventoy/ventoy.json` 指定 | 任意(本 repo 用 `unattend.xml`) |
|
||||||
| 傳統 USB 安裝碟 | USB 根目錄 | `autounattend.xml` |
|
| 傳統 USB 安裝碟 | USB 根目錄 | `autounattend.xml`(注意:FirstLogonCommands Order 4 的磁碟掃描還是會找 `\ventoy\script\Setup.ps1`,要嘛把 `Setup.ps1` 放對位置,要嘛改 Order 4 的搜尋路徑) |
|
||||||
| ISO `sources\` 目錄 | ISO 根 | `autounattend.xml` |
|
| ISO `sources\` 目錄 | ISO 根 | `autounattend.xml`(同上) |
|
||||||
| DISM 離線注入已掛載映像 | — | 任意名稱,用 `/Apply-Unattend:` 指定 |
|
| DISM 離線注入已掛載映像 | — | 任意名稱,用 `/Apply-Unattend:` 指定(這條路徑下 `Setup.ps1` 通常會直接烘進 image 而不再走 USB 掃描) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -99,11 +107,9 @@ Windows Setup 在不同階段套用不同 `<settings pass="...">`:
|
|||||||
## 安全性
|
## 安全性
|
||||||
|
|
||||||
- 本範本密碼為明碼,僅適合內網測試;生產環境請改 Base64 或交由 MDT/SCCM 管理。
|
- 本範本密碼為明碼,僅適合內網測試;生產環境請改 Base64 或交由 MDT/SCCM 管理。
|
||||||
- 部署結束後應刪除殘留的 unattend 檔,例如在 `Setup.ps1` 尾端:
|
- `Setup.ps1` 的 `cleanup-unattend-files` 段已經會在收尾時刪掉 `C:\Windows\Panther\unattend.xml` 與 `Panther\Unattend\unattend.xml`(含明碼密碼)。
|
||||||
```powershell
|
- WinRM `AllowUnencrypted=true` + `TrustedHosts=*` 是測試環境設定;正式環境要改 HTTPS(5986)+ 限制 TrustedHosts。
|
||||||
Remove-Item C:\Windows\Panther\unattend.xml -Force -ErrorAction SilentlyContinue
|
- KMS 預設 fallback 到第三方公開伺服器(`kms.digiboy.ir` 等);正式環境改 `Setup.ps1` 的 `$InternalKmsServers`。
|
||||||
Remove-Item C:\Windows\Panther\Unattend\unattend.xml -Force -ErrorAction SilentlyContinue
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
49
SUMMARY.md
49
SUMMARY.md
@@ -44,20 +44,46 @@ XML 能做的事有上限。超過這條線(裝軟體、設 Registry、拉 Git
|
|||||||
`FirstLogonCommands` 就是交棒點:
|
`FirstLogonCommands` 就是交棒點:
|
||||||
|
|
||||||
```
|
```
|
||||||
Order 1 → Setup.ps1 (所有「後續設定」的預留入口)
|
Order 1 → net accounts (一行搞定的雜事直接 cmd,密碼永不過期)
|
||||||
Order 2 → net accounts (一行搞定的雜事直接 cmd)
|
Order 2 → Rename-Computer (主機名改 PC-XXXXX,重開後生效)
|
||||||
Order 3 → Rename-Computer (主機名改 PC-XXXXX,重開後生效)
|
Order 3 → net user user 1234 /add (建立一般使用者,強制首次登入改密)
|
||||||
Order 4 → net user user 1234 /add (建立一般使用者,強制首次登入改密)
|
Order 4 → Copy Setup.ps1 from Ventoy USB (掃磁碟找 \ventoy\script\Setup.ps1 → C:\Scripts\)
|
||||||
Order 5 → Add-WindowsCapability OpenSSH (等網路→安裝並啟動 sshd)
|
Order 5 → powershell -File Setup.ps1 (13 段 post-install,看下表)
|
||||||
Order 6 → Telegram 靜默安裝 (等網路→下載 tsetup.exe + /VERYSILENT)
|
Order 6 → shutdown /r (重開機收尾,讓 rename 生效)
|
||||||
Order 7 → shutdown /r (重開機收尾,讓 rename 生效)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
這讓 `unattend.xml` 保持「只負責 Windows 本身的安裝」,其他事情外包給腳本。職責分離之後,改軟體清單不需要動 XML,改部署流程也不需要動 Setup 邏輯。
|
`Setup.ps1` 內部分兩個 phase(用空的 `wait-network` 段切開):
|
||||||
|
|
||||||
> Order 5、6(OpenSSH、Telegram)是「交棒到腳本」這條原則的例外:**單一套件、需求穩定**的情況下,直接在 `FirstLogonCommands` 裡下載安裝比起硬塞進 `Setup.ps1` 單純。一旦要裝的東西變成清單(3+ 個套件、要版本鎖定、要錯誤處理),就該整批移進 `Setup.ps1` 或改用 winget/choco。
|
```
|
||||||
|
[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 切換熱鍵
|
||||||
|
rename-hide-accounts → user → User,從登入畫面隱藏 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 必須留到第一次登入完成才能拔。
|
||||||
>
|
>
|
||||||
> Order 5、6 共用「等 ICMP 通到 8.8.8.8、最多 60 秒」的網路等待迴圈,並且各自把結果寫到 `C:\Windows\Temp\firstlogon-*.log`,避免前一輪 Telegram 無聲失敗的情況再發生。
|
> 為什麼是「先建小寫 user 再改 User」:`unattend.xml` 的 `net user user 1234 /add` 鐵打不會壞;`Setup.ps1` 的 `Rename-LocalUser` 萬一沒跑完,最差結果是有個小寫 `user` 還能登入。先建後改 = bulletproof。
|
||||||
|
>
|
||||||
|
> `remove-windows-ai` 跑的是 `main` 分支的腳本,**每次部署抓的版本可能不同**。要確定性、可稽核的部署,把 `Setup.ps1` 中對應 URL 的 `/main/` 換成 commit SHA。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -85,7 +111,8 @@ Windows Setup 有七個 pass,本檔只用三個。不是偷懶,是**三個 p
|
|||||||
| **本機帳號 + AutoLogon** | 純離線可跑 | 改成 Domain Join + 網域管理員登入 |
|
| **本機帳號 + AutoLogon** | 純離線可跑 | 改成 Domain Join + 網域管理員登入 |
|
||||||
| **ProtectYourPC=3** | 略過 OOBE 的隱私頁,預設行為 | 依公司政策調整,或用 GPO 蓋掉 |
|
| **ProtectYourPC=3** | 略過 OOBE 的隱私頁,預設行為 | 依公司政策調整,或用 GPO 蓋掉 |
|
||||||
| **單一 Disk 0 + 整碟清空** | 對測試 VM 最安全的假設 | 多硬碟環境要加 `DiskID` 判斷邏輯 |
|
| **單一 Disk 0 + 整碟清空** | 對測試 VM 最安全的假設 | 多硬碟環境要加 `DiskID` 判斷邏輯 |
|
||||||
| **`Setup.ps1` 路徑硬寫 C:\Scripts\** | 簡單 | 改從 USB/網路拉,或用 MDT 的 `Applications` 機制 |
|
| **`Setup.ps1` 用磁碟掃描從 Ventoy USB 拷貝** | 不需要架網路服務、跨網段也能跑、USB 拔掉也能在現場補救 | 內部 IT 場合改用 SCCM/Intune 派送,或乾脆 base64 內嵌進 XML 拿到完全自包含 |
|
||||||
|
| **`$PublicKmsServers` 用第三方 KMS** | 沒授權主機也能跑通 | 把 `$InternalKmsServers` 改成自家 KMS 主機,公開那串不再被 fallback |
|
||||||
|
|
||||||
這些不是 bug,是**當前脈絡下的合理起點**。要產品化就對照這張表一個一個升級。
|
這些不是 bug,是**當前脈絡下的合理起點**。要產品化就對照這張表一個一個升級。
|
||||||
|
|
||||||
|
|||||||
340
Setup.ps1
Normal file
340
Setup.ps1
Normal file
@@ -0,0 +1,340 @@
|
|||||||
|
# Setup.ps1 — Windows 自動部署完成後的 post-install 腳本
|
||||||
|
#
|
||||||
|
# 入口:unattend.xml 的 FirstLogonCommands 會把這份從 Ventoy USB(/ventoy/script/Setup.ps1)
|
||||||
|
# 複製到 C:\Scripts\Setup.ps1,然後以 Admin 帳號執行。
|
||||||
|
#
|
||||||
|
# Log: C:\Windows\Temp\setup.log(每段都寫 START/DONE/ERR)
|
||||||
|
#
|
||||||
|
# 順序:先做不需網路的本機設定(防火牆 / 註冊機碼 / Default profile),
|
||||||
|
# 再等網路,最後跑下載安裝(OpenSSH FoD / KMS / winget / RemoveAI / Win11Debloat / Telegram)。
|
||||||
|
|
||||||
|
$ErrorActionPreference = 'Continue'
|
||||||
|
$ProgressPreference = 'SilentlyContinue'
|
||||||
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||||
|
|
||||||
|
$LogFile = 'C:\Windows\Temp\setup.log'
|
||||||
|
|
||||||
|
# 內網 KMS 主機請改下面這列;保持空陣列就會用公開 KMS 清單輪流試
|
||||||
|
$InternalKmsServers = @()
|
||||||
|
$PublicKmsServers = @('kms.digiboy.ir', 'kms8.msguides.com', 'kms.03k.org', 'kms.chinancce.com')
|
||||||
|
|
||||||
|
# 要批次移除的 UWP(與 windows-remote-toolkit/config/remove-apps-config.json 同步)
|
||||||
|
$UwpRemoveList = @(
|
||||||
|
'Microsoft.BingNews','Microsoft.BingWeather','Microsoft.GetHelp',
|
||||||
|
'Microsoft.MicrosoftSolitaireCollection','Microsoft.Todos','Microsoft.Windows.DevHome',
|
||||||
|
'Microsoft.WindowsAlarms','Microsoft.WindowsCamera','Microsoft.WindowsFeedbackHub',
|
||||||
|
'Microsoft.WindowsSoundRecorder','Microsoft.Xbox.TCUI','Microsoft.XboxGameCallableUI',
|
||||||
|
'Microsoft.XboxIdentityProvider','Microsoft.XboxSpeechToTextOverlay','Microsoft.YourPhone',
|
||||||
|
'Microsoft.ZuneMusic','MicrosoftCorporationII.QuickAssist',
|
||||||
|
'MicrosoftWindows.Client.WebExperience','MicrosoftWindows.CrossDevice','MSTeams',
|
||||||
|
'Microsoft.PowerAutomateDesktop','Microsoft.Windows.Photos'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Win11Debloat 的設定 JSON(與 toolkit/config/win11debloat-config.json 同步)
|
||||||
|
$Win11DebloatConfigJson = @'
|
||||||
|
{
|
||||||
|
"Version": "1.0",
|
||||||
|
"Tweaks": [
|
||||||
|
{ "Name": "RemoveApps", "Value": true },
|
||||||
|
{ "Name": "RemoveGamingApps", "Value": true },
|
||||||
|
{ "Name": "RemoveCommApps", "Value": true },
|
||||||
|
{ "Name": "RemoveW11Outlook", "Value": true },
|
||||||
|
{ "Name": "DisableTelemetry", "Value": true },
|
||||||
|
{ "Name": "DisableSearchHistory", "Value": true },
|
||||||
|
{ "Name": "DisableBing", "Value": true },
|
||||||
|
{ "Name": "DisableStoreSearchSuggestions", "Value": true },
|
||||||
|
{ "Name": "DisableSearchHighlights", "Value": true },
|
||||||
|
{ "Name": "DisableLockscreenTips", "Value": true },
|
||||||
|
{ "Name": "DisableSuggestions", "Value": true },
|
||||||
|
{ "Name": "DisableDesktopSpotlight", "Value": true },
|
||||||
|
{ "Name": "DisableFindMyDevice", "Value": true },
|
||||||
|
{ "Name": "DisableEdgeAds", "Value": true },
|
||||||
|
{ "Name": "DisableBraveBloat", "Value": true },
|
||||||
|
{ "Name": "DisableSettings365Ads", "Value": true },
|
||||||
|
{ "Name": "DisableSettingsHome", "Value": true },
|
||||||
|
{ "Name": "DisableCopilot", "Value": true },
|
||||||
|
{ "Name": "DisableRecall", "Value": true },
|
||||||
|
{ "Name": "DisableClickToDo", "Value": true },
|
||||||
|
{ "Name": "DisableAISvcAutoStart", "Value": true },
|
||||||
|
{ "Name": "DisablePaintAI", "Value": true },
|
||||||
|
{ "Name": "DisableNotepadAI", "Value": true },
|
||||||
|
{ "Name": "DisableEdgeAI", "Value": true },
|
||||||
|
{ "Name": "PreventUpdateAutoReboot", "Value": true },
|
||||||
|
{ "Name": "DisableModernStandbyNetworking", "Value": true },
|
||||||
|
{ "Name": "DisableDVR", "Value": true },
|
||||||
|
{ "Name": "DisableGameBarIntegration", "Value": true },
|
||||||
|
{ "Name": "ShowHiddenFolders", "Value": true },
|
||||||
|
{ "Name": "ShowKnownFileExt", "Value": true },
|
||||||
|
{ "Name": "EnableDarkMode", "Value": true },
|
||||||
|
{ "Name": "TaskbarAlignLeft", "Value": true },
|
||||||
|
{ "Name": "HideSearchTb", "Value": true },
|
||||||
|
{ "Name": "HideTaskview", "Value": true },
|
||||||
|
{ "Name": "HideChat", "Value": true },
|
||||||
|
{ "Name": "DisableWidgets", "Value": true },
|
||||||
|
{ "Name": "DisableStartRecommended", "Value": true },
|
||||||
|
{ "Name": "Hide3dObjects", "Value": true },
|
||||||
|
{ "Name": "ExplorerToThisPC", "Value": true },
|
||||||
|
{ "Name": "HideGallery", "Value": true },
|
||||||
|
{ "Name": "DisableDragTray", "Value": true }
|
||||||
|
],
|
||||||
|
"Deployment": [
|
||||||
|
{ "Name": "CreateRestorePoint", "Value": true },
|
||||||
|
{ "Name": "RestartExplorer", "Value": false },
|
||||||
|
{ "Name": "AppRemovalScopeIndex", "Value": 0 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
'@
|
||||||
|
|
||||||
|
# ---- helpers ----
|
||||||
|
|
||||||
|
function Log {
|
||||||
|
param([string]$msg)
|
||||||
|
$line = '[{0}] {1}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $msg
|
||||||
|
Add-Content -Path $LogFile -Value $line -Encoding UTF8
|
||||||
|
}
|
||||||
|
|
||||||
|
function Section {
|
||||||
|
param([string]$name, [scriptblock]$block)
|
||||||
|
Log "=== $name START ==="
|
||||||
|
try {
|
||||||
|
& $block
|
||||||
|
Log "=== $name DONE ==="
|
||||||
|
} catch {
|
||||||
|
Log "=== $name ERR: $($_.Exception.Message) ==="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Wait-Network {
|
||||||
|
param([int]$timeoutSec = 60)
|
||||||
|
$i = 0
|
||||||
|
$max = [int]($timeoutSec / 2)
|
||||||
|
while ($i -lt $max -and -not (Test-Connection 8.8.8.8 -Count 1 -Quiet -ErrorAction SilentlyContinue)) {
|
||||||
|
Start-Sleep 2
|
||||||
|
$i++
|
||||||
|
}
|
||||||
|
Log "Network wait: $($i*2)s"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Set-RegValue {
|
||||||
|
param([string]$Path, [string]$Name, $Value, [string]$Type = 'DWord')
|
||||||
|
if (-not (Test-Path $Path)) { New-Item -Path $Path -Force | Out-Null }
|
||||||
|
New-ItemProperty -Path $Path -Name $Name -Value $Value -PropertyType $Type -Force | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
# 開頭橫線(log 開始)
|
||||||
|
"`n========== Setup.ps1 starting at $(Get-Date -Format o) ==========" | Add-Content $LogFile -Encoding UTF8
|
||||||
|
|
||||||
|
# ---- Phase 1: 本機設定(不需網路)----
|
||||||
|
|
||||||
|
Section 'firewall-ssh-22' {
|
||||||
|
Remove-NetFirewallRule -DisplayName 'SSH TCP 22 (Allow Inbound)' -ErrorAction SilentlyContinue
|
||||||
|
New-NetFirewallRule -DisplayName 'SSH TCP 22 (Allow Inbound)' `
|
||||||
|
-Direction Inbound -Protocol TCP -LocalPort 22 -Action Allow -Profile Any -Enabled True | Out-Null
|
||||||
|
Set-NetFirewallProfile -Profile Domain,Private,Public -Enabled True
|
||||||
|
}
|
||||||
|
|
||||||
|
Section 'rdp-enable-3389' {
|
||||||
|
Set-RegValue 'HKLM:\System\CurrentControlSet\Control\Terminal Server' 'fDenyTSConnections' 0
|
||||||
|
Set-RegValue 'HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' 'UserAuthentication' 1
|
||||||
|
Enable-NetFirewallRule -DisplayGroup 'Remote Desktop' -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
Section 'icmp-echo-allow' {
|
||||||
|
Remove-NetFirewallRule -DisplayName 'ICMPv4 Echo Request (Allow Inbound)' -ErrorAction SilentlyContinue
|
||||||
|
New-NetFirewallRule -DisplayName 'ICMPv4 Echo Request (Allow Inbound)' `
|
||||||
|
-Direction Inbound -Protocol ICMPv4 -IcmpType 8 -Action Allow -Profile Any -Enabled True | Out-Null
|
||||||
|
Get-NetFirewallRule -ErrorAction SilentlyContinue |
|
||||||
|
Where-Object { $_.DisplayName -match 'Echo Request' } |
|
||||||
|
Enable-NetFirewallRule -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
Section 'winrm-enable' {
|
||||||
|
Enable-PSRemoting -Force -SkipNetworkProfileCheck | Out-Null
|
||||||
|
Set-Service -Name WinRM -StartupType Automatic
|
||||||
|
Start-Service -Name WinRM -ErrorAction SilentlyContinue
|
||||||
|
cmd.exe /c 'winrm quickconfig -quiet -force' | Out-Null
|
||||||
|
cmd.exe /c 'winrm set winrm/config/service/auth @{Basic="true"}' | Out-Null
|
||||||
|
cmd.exe /c 'winrm set winrm/config/service @{AllowUnencrypted="true"}' | Out-Null
|
||||||
|
cmd.exe /c 'winrm set winrm/config/client @{TrustedHosts="*"}' | Out-Null
|
||||||
|
Enable-NetFirewallRule -DisplayGroup 'Windows Remote Management' -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
Section 'onedrive-remove' {
|
||||||
|
Get-Process OneDrive -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
|
||||||
|
foreach ($p in @("$env:SystemRoot\System32\OneDriveSetup.exe", "$env:SystemRoot\SysWOW64\OneDriveSetup.exe")) {
|
||||||
|
if (Test-Path $p) { Start-Process $p -ArgumentList '/uninstall' -Wait -ErrorAction SilentlyContinue }
|
||||||
|
}
|
||||||
|
Remove-ItemProperty -Path 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Run' -Name 'OneDrive' -ErrorAction SilentlyContinue
|
||||||
|
Remove-Item 'HKLM:\SOFTWARE\Classes\CLSID\{018D5C66-4533-4307-9B53-224DE2ED1FE6}' -Recurse -ErrorAction SilentlyContinue
|
||||||
|
Remove-Item 'HKLM:\SOFTWARE\Wow6432Node\Classes\CLSID\{018D5C66-4533-4307-9B53-224DE2ED1FE6}' -Recurse -ErrorAction SilentlyContinue
|
||||||
|
Remove-Item -Path 'C:\OneDriveTemp', "$env:USERPROFILE\OneDrive", "$env:LOCALAPPDATA\Microsoft\OneDrive" -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
Section 'cortana-websearch-off' {
|
||||||
|
$k1 = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\Windows Search'
|
||||||
|
Set-RegValue $k1 'AllowCortana' 0
|
||||||
|
Set-RegValue $k1 'DisableWebSearch' 1
|
||||||
|
Set-RegValue $k1 'ConnectedSearchUseWeb' 0
|
||||||
|
Set-RegValue $k1 'AllowSearchToUseLocation' 0
|
||||||
|
|
||||||
|
$k2 = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\Explorer'
|
||||||
|
Set-RegValue $k2 'DisableSearchBoxSuggestions' 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# 把 UI / IME 預設套到 Default profile,新使用者首次登入會繼承
|
||||||
|
Section 'apply-ui-ime-default-profile' {
|
||||||
|
$hivePath = 'C:\Users\Default\NTUSER.DAT'
|
||||||
|
$mountKey = 'HKU\SetupDefault'
|
||||||
|
reg.exe load $mountKey $hivePath | Out-Null
|
||||||
|
try {
|
||||||
|
$base = "Registry::$mountKey"
|
||||||
|
|
||||||
|
$adv = "$base\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"
|
||||||
|
Set-RegValue $adv 'HideFileExt' 0 # 顯示副檔名
|
||||||
|
Set-RegValue $adv 'Hidden' 1 # 顯示隱藏檔
|
||||||
|
Set-RegValue $adv 'TaskbarAl' 0 # 工作列靠左
|
||||||
|
Set-RegValue $adv 'ShowTaskViewButton' 0 # 隱藏 Task View 按鈕
|
||||||
|
Set-RegValue $adv 'LaunchTo' 1 # 開啟 This PC
|
||||||
|
Set-RegValue $adv 'Start_TrackProgs' 0 # 不追蹤最近開啟的程式
|
||||||
|
|
||||||
|
$theme = "$base\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"
|
||||||
|
Set-RegValue $theme 'AppsUseLightTheme' 0
|
||||||
|
Set-RegValue $theme 'SystemUseLightTheme' 0
|
||||||
|
|
||||||
|
# 工作列搜尋框:0=隱藏, 1=圖示, 2=框, 3=圖示+標籤
|
||||||
|
$search = "$base\Software\Microsoft\Windows\CurrentVersion\Search"
|
||||||
|
Set-RegValue $search 'SearchboxTaskbarMode' 0
|
||||||
|
|
||||||
|
$cloud = "$base\Software\Policies\Microsoft\Windows\CloudContent"
|
||||||
|
Set-RegValue $cloud 'DisableSpotlightCollectionOnDesktop' 1
|
||||||
|
Set-RegValue $cloud 'DisableWindowsSpotlightFeatures' 1
|
||||||
|
|
||||||
|
# 鍵盤排序:英文鍵盤排第一(預設英數),注音排第二
|
||||||
|
$kbPre = "$base\Keyboard Layout\Preload"
|
||||||
|
Set-RegValue $kbPre '1' '00000409' 'String'
|
||||||
|
Set-RegValue $kbPre '2' '00000404' 'String'
|
||||||
|
|
||||||
|
# CHT 注音:預設英數模式、關掉雲端候選與預測
|
||||||
|
$cht = "$base\Software\Microsoft\InputMethod\Settings\CHT"
|
||||||
|
Set-RegValue $cht 'Enable Default Input Mode' 0
|
||||||
|
Set-RegValue $cht 'Enable Cand Predict' 0
|
||||||
|
Set-RegValue $cht 'Enable Cloud Cand' 0
|
||||||
|
Set-RegValue $cht 'Enable Cloud Input' 0
|
||||||
|
|
||||||
|
# 把系統 Hotkey 切換鍵停掉(3 = 不指定鍵)
|
||||||
|
$tg = "$base\Keyboard Layout\Toggle"
|
||||||
|
Set-RegValue $tg 'Hotkey' '3' 'String'
|
||||||
|
Set-RegValue $tg 'Language Hotkey' '3' 'String'
|
||||||
|
Set-RegValue $tg 'Layout Hotkey' '3' 'String'
|
||||||
|
} finally {
|
||||||
|
[GC]::Collect()
|
||||||
|
Start-Sleep 1
|
||||||
|
reg.exe unload $mountKey | Out-Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Section 'rename-hide-accounts' {
|
||||||
|
$u = Get-LocalUser -Name 'user' -ErrorAction SilentlyContinue
|
||||||
|
if ($u -and $u.Name -ceq 'user') {
|
||||||
|
Rename-LocalUser -Name 'user' -NewName 'User'
|
||||||
|
Log 'Renamed user -> User'
|
||||||
|
} else {
|
||||||
|
Log "Local user 'user' not found or already renamed; skip"
|
||||||
|
}
|
||||||
|
|
||||||
|
$userListKey = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList'
|
||||||
|
Set-RegValue $userListKey 'Admin' 0 # 0 = 從登入畫面隱藏(帳號仍存在仍可用)
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- Phase 2: 需要網路 ----
|
||||||
|
|
||||||
|
Section 'wait-network' { Wait-Network -timeoutSec 60 }
|
||||||
|
|
||||||
|
Section 'openssh-server-install' {
|
||||||
|
Add-WindowsCapability -Online -Name 'OpenSSH.Server~~~~0.0.1.0' -ErrorAction Stop | Out-Null
|
||||||
|
Set-Service sshd -StartupType Automatic
|
||||||
|
Start-Service sshd
|
||||||
|
}
|
||||||
|
|
||||||
|
Section 'kms-activate' {
|
||||||
|
$servers = @()
|
||||||
|
if ($InternalKmsServers.Count -gt 0) { $servers += $InternalKmsServers }
|
||||||
|
$servers += $PublicKmsServers
|
||||||
|
$activated = $false
|
||||||
|
foreach ($kms in $servers) {
|
||||||
|
Log "Try KMS: $kms"
|
||||||
|
cscript.exe //nologo C:\Windows\System32\slmgr.vbs /skms $kms | ForEach-Object { Log " skms: $_" }
|
||||||
|
$out = cscript.exe //nologo C:\Windows\System32\slmgr.vbs /ato 2>&1
|
||||||
|
foreach ($line in $out) { Log " ato : $line" }
|
||||||
|
if ($out -match 'successful|成功') {
|
||||||
|
Log "Activated via $kms"
|
||||||
|
$activated = $true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (-not $activated) { Log 'All KMS servers failed; remaining in grace period' }
|
||||||
|
}
|
||||||
|
|
||||||
|
Section 'winget-install-chrome' {
|
||||||
|
$winget = (Get-Command winget -ErrorAction SilentlyContinue).Source
|
||||||
|
if (-not $winget) { Log 'winget not found (Win10 沒裝 App Installer?)'; return }
|
||||||
|
& $winget install --id Google.Chrome -e --silent `
|
||||||
|
--accept-source-agreements --accept-package-agreements --scope machine 2>&1 |
|
||||||
|
ForEach-Object { Log " winget: $_" }
|
||||||
|
}
|
||||||
|
|
||||||
|
Section 'remove-windows-ai' {
|
||||||
|
$sb = [scriptblock]::Create((Invoke-RestMethod 'https://raw.githubusercontent.com/zoicware/RemoveWindowsAI/main/RemoveWindowsAi.ps1'))
|
||||||
|
& $sb -nonInteractive -AllOptions -EnableLogging | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
Section 'win11debloat' {
|
||||||
|
$work = Join-Path $env:TEMP 'win11debloat'
|
||||||
|
$zip = "$work.zip"
|
||||||
|
if (Test-Path $work) { Remove-Item $work -Recurse -Force }
|
||||||
|
Invoke-WebRequest -UseBasicParsing -Uri 'https://github.com/Raphire/Win11Debloat/archive/refs/heads/master.zip' -OutFile $zip
|
||||||
|
Expand-Archive -Path $zip -DestinationPath $work -Force
|
||||||
|
$dir = Get-ChildItem $work -Directory | Select-Object -First 1
|
||||||
|
if (-not $dir) { throw 'Win11Debloat zip extracted but no inner dir' }
|
||||||
|
$config = Join-Path $dir.FullName 'unattend-config.json'
|
||||||
|
Set-Content -Path $config -Value $Win11DebloatConfigJson -Encoding UTF8
|
||||||
|
& "$($dir.FullName)\Win11Debloat.ps1" -Silent -Config $config -NoRestartExplorer
|
||||||
|
}
|
||||||
|
|
||||||
|
Section 'remove-uwp-apps' {
|
||||||
|
foreach ($name in $UwpRemoveList) {
|
||||||
|
Get-AppxPackage -AllUsers -Name $name -ErrorAction SilentlyContinue | ForEach-Object {
|
||||||
|
try {
|
||||||
|
Remove-AppxPackage -AllUsers -Package $_.PackageFullName -ErrorAction Stop
|
||||||
|
Log "Removed AppxPackage $name"
|
||||||
|
} catch {
|
||||||
|
Log "Skip AppxPackage $name : $($_.Exception.Message)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Get-AppxProvisionedPackage -Online -ErrorAction SilentlyContinue |
|
||||||
|
Where-Object { $_.DisplayName -eq $name } |
|
||||||
|
ForEach-Object {
|
||||||
|
try {
|
||||||
|
Remove-AppxProvisionedPackage -Online -PackageName $_.PackageName -ErrorAction Stop | Out-Null
|
||||||
|
Log "Removed Provisioned $name"
|
||||||
|
} catch {
|
||||||
|
Log "Skip Provisioned $name : $($_.Exception.Message)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Section 'install-telegram' {
|
||||||
|
$p = Join-Path $env:TEMP 'tsetup.exe'
|
||||||
|
Invoke-WebRequest -UseBasicParsing -Uri 'https://telegram.org/dl/desktop/win64' -OutFile $p
|
||||||
|
$proc = Start-Process -FilePath $p -ArgumentList '/VERYSILENT','/SUPPRESSMSGBOXES','/NORESTART' -Wait -PassThru
|
||||||
|
Log "Telegram installer exit code: $($proc.ExitCode)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 結尾:刪掉殘留的明碼回應檔
|
||||||
|
Section 'cleanup-unattend-files' {
|
||||||
|
Remove-Item 'C:\Windows\Panther\unattend.xml' -Force -ErrorAction SilentlyContinue
|
||||||
|
Remove-Item 'C:\Windows\Panther\Unattend\unattend.xml' -Force -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
"========== Setup.ps1 finished at $(Get-Date -Format o) ==========" | Add-Content $LogFile -Encoding UTF8
|
||||||
37
unattend.xml
37
unattend.xml
@@ -148,44 +148,45 @@
|
|||||||
</Password>
|
</Password>
|
||||||
</AutoLogon>
|
</AutoLogon>
|
||||||
|
|
||||||
<!-- 首次登入執行的自訂腳本 -->
|
<!-- 首次登入執行的自訂腳本
|
||||||
|
|
||||||
|
前 4 條為「不需網路、可獨立成功」的本機指令;
|
||||||
|
Order 5 從 Ventoy USB 把 Setup.ps1 複製到 C:\Scripts\,
|
||||||
|
Order 6 跑 Setup.ps1(13 項 post-install 動作集中在那裡),
|
||||||
|
Order 7 重開機。
|
||||||
|
|
||||||
|
各條都會寫獨立 log 到 C:\Windows\Temp\,方便排查無聲失敗。 -->
|
||||||
<FirstLogonCommands>
|
<FirstLogonCommands>
|
||||||
<SynchronousCommand wcm:action="add">
|
<SynchronousCommand wcm:action="add">
|
||||||
<Order>1</Order>
|
<Order>1</Order>
|
||||||
<CommandLine>powershell.exe -ExecutionPolicy Bypass -File C:\Scripts\Setup.ps1</CommandLine>
|
|
||||||
<Description>執行部署後設定腳本</Description>
|
|
||||||
<RequiresUserInput>false</RequiresUserInput>
|
|
||||||
</SynchronousCommand>
|
|
||||||
<SynchronousCommand wcm:action="add">
|
|
||||||
<Order>2</Order>
|
|
||||||
<CommandLine>cmd.exe /c net accounts /maxpwage:unlimited</CommandLine>
|
<CommandLine>cmd.exe /c net accounts /maxpwage:unlimited</CommandLine>
|
||||||
<Description>密碼永不過期</Description>
|
<Description>密碼永不過期</Description>
|
||||||
</SynchronousCommand>
|
</SynchronousCommand>
|
||||||
<SynchronousCommand wcm:action="add">
|
<SynchronousCommand wcm:action="add">
|
||||||
<Order>3</Order>
|
<Order>2</Order>
|
||||||
<CommandLine>powershell.exe -NoProfile -Command "Rename-Computer -NewName ('PC-' + (Get-Random -Minimum 10000 -Maximum 100000)) -Force"</CommandLine>
|
<CommandLine>powershell.exe -NoProfile -Command "Rename-Computer -NewName ('PC-' + (Get-Random -Minimum 10000 -Maximum 100000)) -Force"</CommandLine>
|
||||||
<Description>主機名改成 PC-XXXXX(重開後生效)</Description>
|
<Description>主機名改成 PC-XXXXX(重開後生效)</Description>
|
||||||
</SynchronousCommand>
|
</SynchronousCommand>
|
||||||
<SynchronousCommand wcm:action="add">
|
<SynchronousCommand wcm:action="add">
|
||||||
<Order>4</Order>
|
<Order>3</Order>
|
||||||
<CommandLine>cmd.exe /c "net user user 1234 /add & net user user /logonpasswordchg:yes"</CommandLine>
|
<CommandLine>cmd.exe /c "net user user 1234 /add & net user user /logonpasswordchg:yes"</CommandLine>
|
||||||
<Description>建立本機帳號 user(密碼 1234,首次登入強制改密)</Description>
|
<Description>建立本機帳號 user(密碼 1234,首次登入強制改密;之後 Setup.ps1 會把它改名成 User)</Description>
|
||||||
|
<RequiresUserInput>false</RequiresUserInput>
|
||||||
|
</SynchronousCommand>
|
||||||
|
<SynchronousCommand wcm:action="add">
|
||||||
|
<Order>4</Order>
|
||||||
|
<CommandLine>powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "$log='C:\Windows\Temp\firstlogon-copysetup.log'; New-Item -ItemType Directory -Force -Path C:\Scripts | Out-Null; $src=$null; foreach ($d in (Get-PSDrive -PSProvider FileSystem -EA 0).Root) { $cand=Join-Path $d 'ventoy\script\Setup.ps1'; if (Test-Path $cand) { $src=$cand; break } }; if ($src) { Copy-Item $src 'C:\Scripts\Setup.ps1' -Force; ('OK from ' + $src + ' at ' + (Get-Date -Format o)) | Out-File $log -Append } else { ('NOT FOUND at ' + (Get-Date -Format o)) | Out-File $log -Append }"</CommandLine>
|
||||||
|
<Description>從 Ventoy USB 的 \ventoy\script\Setup.ps1 複製到 C:\Scripts\(log: firstlogon-copysetup.log)</Description>
|
||||||
<RequiresUserInput>false</RequiresUserInput>
|
<RequiresUserInput>false</RequiresUserInput>
|
||||||
</SynchronousCommand>
|
</SynchronousCommand>
|
||||||
<SynchronousCommand wcm:action="add">
|
<SynchronousCommand wcm:action="add">
|
||||||
<Order>5</Order>
|
<Order>5</Order>
|
||||||
<CommandLine>powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "$log='C:\Windows\Temp\firstlogon-sshd.log'; $i=0; while ($i -lt 30 -and -not (Test-Connection 8.8.8.8 -Count 1 -Quiet -EA 0)) { Start-Sleep 2; $i++ }; ('NetWait=' + ($i*2) + 's') | Out-File $log -Append; try { Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 -EA Stop | Out-Null; Set-Service sshd -StartupType Automatic -EA Stop; Start-Service sshd -EA Stop; ('OK ' + (Get-Date -Format o)) | Out-File $log -Append } catch { ('ERR ' + $_.Exception.Message) | Out-File $log -Append }"</CommandLine>
|
<CommandLine>cmd.exe /c "if exist C:\Scripts\Setup.ps1 (powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\Scripts\Setup.ps1) else (echo SETUP_NOT_FOUND %DATE% %TIME% > C:\Windows\Temp\firstlogon-runsetup.log)"</CommandLine>
|
||||||
<Description>等網路→安裝並啟用 OpenSSH Server(log: firstlogon-sshd.log)</Description>
|
<Description>執行 Setup.ps1(13 項 post-install:SSH/RDP/WinRM/KMS/Chrome/Debloat/Telegram/RemoveAI/UI/IME/重命名帳號…,log: C:\Windows\Temp\setup.log)</Description>
|
||||||
<RequiresUserInput>false</RequiresUserInput>
|
<RequiresUserInput>false</RequiresUserInput>
|
||||||
</SynchronousCommand>
|
</SynchronousCommand>
|
||||||
<SynchronousCommand wcm:action="add">
|
<SynchronousCommand wcm:action="add">
|
||||||
<Order>6</Order>
|
<Order>6</Order>
|
||||||
<CommandLine>powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "$log='C:\Windows\Temp\firstlogon-telegram.log'; [Net.ServicePointManager]::SecurityProtocol=[Net.SecurityProtocolType]::Tls12; $ProgressPreference='SilentlyContinue'; $p=Join-Path $env:TEMP 'tsetup.exe'; $i=0; while ($i -lt 30 -and -not (Test-Connection 8.8.8.8 -Count 1 -Quiet -EA 0)) { Start-Sleep 2; $i++ }; ('NetWait=' + ($i*2) + 's') | Out-File $log -Append; try { ('Start ' + (Get-Date -Format o)) | Out-File $log -Append; Invoke-WebRequest -UseBasicParsing -Uri 'https://telegram.org/dl/desktop/win64' -OutFile $p; ('Downloaded ' + (Get-Item $p).Length + 'B') | Out-File $log -Append; $proc=Start-Process -FilePath $p -ArgumentList '/VERYSILENT','/SUPPRESSMSGBOXES','/NORESTART' -Wait -PassThru; ('Exit ' + $proc.ExitCode) | Out-File $log -Append } catch { ('ERR ' + $_.Exception.Message) | Out-File $log -Append }"</CommandLine>
|
|
||||||
<Description>下載並靜默安裝 Telegram Desktop(log: firstlogon-telegram.log)</Description>
|
|
||||||
<RequiresUserInput>false</RequiresUserInput>
|
|
||||||
</SynchronousCommand>
|
|
||||||
<SynchronousCommand wcm:action="add">
|
|
||||||
<Order>7</Order>
|
|
||||||
<CommandLine>shutdown /r /t 5</CommandLine>
|
<CommandLine>shutdown /r /t 5</CommandLine>
|
||||||
<Description>完成後重開機</Description>
|
<Description>完成後重開機</Description>
|
||||||
</SynchronousCommand>
|
</SynchronousCommand>
|
||||||
|
|||||||
Reference in New Issue
Block a user