diff --git a/Setup.ps1 b/Setup.ps1
index 75f76d9..7d53456 100644
--- a/Setup.ps1
+++ b/Setup.ps1
@@ -5,8 +5,10 @@
#
# Log: C:\Windows\Temp\setup.log(每段都寫 START/DONE/ERR)
#
-# 順序:先做不需網路的本機設定(防火牆 / 註冊機碼 / Default profile),
-# 再等網路,最後跑下載安裝(OpenSSH FoD / KMS / winget / RemoveAI / Win11Debloat / Telegram)。
+# 順序:開頭先暫停 Windows Update(避免跟本腳本搶頻寬/CPU),
+# 再做不需網路的本機設定(防火牆 / 註冊機碼 / Default profile),
+# 接著等網路,跑下載安裝(OpenSSH FoD / KMS / winget / RemoveAI / Win11Debloat / Telegram),
+# 結尾解除 WU 暫停並主動觸發掃描,這樣更新會排在所有 post-install 之後才開始下載。
$ErrorActionPreference = 'Continue'
$ProgressPreference = 'SilentlyContinue'
@@ -30,60 +32,30 @@ $UwpRemoveList = @(
'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 }
- ]
+# Win11Debloat 的設定(與 toolkit/config/win11debloat-config.json 同步)
+# 改用 hashtable + ConvertTo-Json,避免 here-string 在某些 PowerShell 環境 parse 失敗
+$Win11DebloatTweakNames = @(
+ 'RemoveApps','RemoveGamingApps','RemoveCommApps','RemoveW11Outlook',
+ 'DisableTelemetry','DisableSearchHistory','DisableBing','DisableStoreSearchSuggestions',
+ 'DisableSearchHighlights','DisableLockscreenTips','DisableSuggestions','DisableDesktopSpotlight',
+ 'DisableFindMyDevice','DisableEdgeAds','DisableBraveBloat','DisableSettings365Ads',
+ 'DisableSettingsHome','DisableCopilot','DisableRecall','DisableClickToDo',
+ 'DisableAISvcAutoStart','DisablePaintAI','DisableNotepadAI','DisableEdgeAI',
+ 'PreventUpdateAutoReboot','DisableModernStandbyNetworking','DisableDVR','DisableGameBarIntegration',
+ 'ShowHiddenFolders','ShowKnownFileExt','EnableDarkMode','TaskbarAlignLeft',
+ 'HideSearchTb','HideTaskview','HideChat','DisableWidgets','DisableStartRecommended',
+ 'Hide3dObjects','ExplorerToThisPC','HideGallery','DisableDragTray'
+)
+$Win11DebloatConfig = [ordered]@{
+ Version = '1.0'
+ Tweaks = @($Win11DebloatTweakNames | ForEach-Object { [ordered]@{ Name = $_; Value = $true } })
+ Deployment = @(
+ [ordered]@{ Name = 'CreateRestorePoint'; Value = $true },
+ [ordered]@{ Name = 'RestartExplorer'; Value = $false },
+ [ordered]@{ Name = 'AppRemovalScopeIndex'; Value = 0 }
+ )
}
-'@
+$Win11DebloatConfigJson = $Win11DebloatConfig | ConvertTo-Json -Depth 5
# ---- helpers ----
@@ -91,6 +63,7 @@ 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
+ Write-Host $line
}
function Section {
@@ -126,6 +99,25 @@ function Set-RegValue {
# ---- Phase 1: 本機設定(不需網路)----
+Section 'pause-windows-update' {
+ # 設定官方暫停旗標:暫停 24 小時(Setup.ps1 結尾會主動清掉並觸發掃描)
+ $now = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
+ $expiry = (Get-Date).ToUniversalTime().AddHours(24).ToString("yyyy-MM-ddTHH:mm:ssZ")
+ $ux = 'HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings'
+ Set-RegValue $ux 'PauseUpdatesStartTime' $now 'String'
+ Set-RegValue $ux 'PauseUpdatesExpiryTime' $expiry 'String'
+ Set-RegValue $ux 'PauseFeatureUpdatesStartTime' $now 'String'
+ Set-RegValue $ux 'PauseFeatureUpdatesEndTime' $expiry 'String'
+ Set-RegValue $ux 'PauseQualityUpdatesStartTime' $now 'String'
+ Set-RegValue $ux 'PauseQualityUpdatesEndTime' $expiry 'String'
+ Log "WU paused until $expiry (UTC)"
+
+ # 停掉已啟動的 WU 相關服務,避免在 Setup.ps1 期間搶頻寬/CPU
+ foreach ($svc in @('UsoSvc','wuauserv','BITS')) {
+ Stop-Service -Name $svc -Force -ErrorAction SilentlyContinue
+ }
+}
+
Section 'firewall-ssh-22' {
Remove-NetFirewallRule -DisplayName 'SSH TCP 22 (Allow Inbound)' -ErrorAction SilentlyContinue
New-NetFirewallRule -DisplayName 'SSH TCP 22 (Allow Inbound)' `
@@ -324,6 +316,39 @@ Section 'install-telegram' {
Log "Telegram installer exit code: $($proc.ExitCode)"
}
+Section 'resume-and-trigger-windows-update' {
+ # 清掉暫停旗標(pause-windows-update 設的那幾個值)
+ $ux = 'HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings'
+ foreach ($n in @('PauseUpdatesStartTime','PauseUpdatesExpiryTime',
+ 'PauseFeatureUpdatesStartTime','PauseFeatureUpdatesEndTime',
+ 'PauseQualityUpdatesStartTime','PauseQualityUpdatesEndTime')) {
+ Remove-ItemProperty -Path $ux -Name $n -ErrorAction SilentlyContinue
+ }
+
+ # 確保服務可啟動
+ foreach ($svc in @('BITS','wuauserv','UsoSvc')) {
+ Start-Service -Name $svc -ErrorAction SilentlyContinue
+ }
+
+ # 通知 Update Orchestrator 重新讀設定,然後立即掃描/下載/安裝
+ $uso = "$env:SystemRoot\System32\UsoClient.exe"
+ if (Test-Path $uso) {
+ & $uso RefreshSettings | Out-Null
+ & $uso StartScan | Out-Null
+ & $uso StartDownload | Out-Null
+ & $uso StartInstall | Out-Null
+ Log 'WU resumed; UsoClient scan/download/install triggered'
+ } else {
+ # 後備:呼叫 COM 觸發掃描
+ try {
+ (New-Object -ComObject Microsoft.Update.AutoUpdate).DetectNow()
+ Log 'WU resumed; AutoUpdate.DetectNow triggered (COM fallback)'
+ } catch {
+ Log "WU resumed but trigger failed: $($_.Exception.Message)"
+ }
+ }
+}
+
# 結尾:刪掉殘留的明碼回應檔
Section 'cleanup-unattend-files' {
Remove-Item 'C:\Windows\Panther\unattend.xml' -Force -ErrorAction SilentlyContinue
diff --git a/unattend.xml b/unattend.xml
index 791b488..46afe58 100644
--- a/unattend.xml
+++ b/unattend.xml
@@ -169,8 +169,8 @@
3
- cmd.exe /c "net user User 1234 /add & net user User /logonpasswordchg:yes"
- 建立本機帳號 User(密碼 1234,首次登入強制改密)
+ cmd.exe /c "net user User /add & net user User /logonpasswordchg:yes"
+ 建立本機帳號 User(空密碼,首次登入強制設定新密碼)
false
@@ -181,8 +181,8 @@
5
- 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)"
- 執行 Setup.ps1(13 項 post-install:SSH/RDP/WinRM/KMS/Chrome/Debloat/Telegram/RemoveAI/UI/IME/重命名帳號…,log: C:\Windows\Temp\setup.log)
+ cmd.exe /c "(echo == firstlogon-runsetup %DATE% %TIME% == & if exist C:\Scripts\Setup.ps1 (powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\Scripts\Setup.ps1) else (echo SETUP_NOT_FOUND)) > C:\Windows\Temp\firstlogon-runsetup.log 2>&1"
+ 執行 Setup.ps1(所有 stdout/stderr 都收到 firstlogon-runsetup.log,含 parse error);正常 Setup.ps1 自身寫到 setup.log
false