Files
windows-unattend/Setup.ps1
Timmy b1e5b6912a unattend.xml 直接以 User(首字大寫)建帳號;Setup.ps1 拿掉 rename-LocalUser
把 Order 3 的 net user 命令直接寫成 net user User,省掉「先建小寫 user 再
Rename-LocalUser 改成大寫 User」這個迂迴。Setup.ps1 的 rename-hide-accounts
段同時改名為 hide-admin-account,內容只剩設 SpecialAccounts\\UserList\\Admin=0
這一行——隱藏 Admin 仍然由 Setup.ps1 負責。

文件全部同步:DEPLOY/QUICKSTART/SUMMARY/README/CLAUDE 把「先建小寫再改名」的
bulletproof 解釋與相關段名都拿掉。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 13:59:15 +08:00

334 lines
15 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 'hide-admin-account' {
# 帳號 User 在 unattend.xml Order 3 已直接以大寫建立,這裡只負責把 Admin 從登入畫面藏掉
$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