操作系统

Win10 → Win11 全新安裝前的穩健備份與可回滾遷移

hadeschan · 10月21日 · 2025年 本文共12020个字 · 预计阅读41分钟 105次已读

Win10 → Win11 全新安裝前的穩健備份與可回滾遷移(SRE Runbook)

主題/場景簡述:在 Windows 10 重裝為 Windows 11(24H2/25H2)前,使用一鍵 PowerShell 腳本完成瀏覽器(多帳號)、應用清單、驅動、WSL、SSH、Wi-Fi、郵件客戶端與代理/VPN 的可驗證備份;並提供回滾與恢復路徑。
目標版本/對象:適用 Windows 10 22H2 → Windows 11 24H2/25H2;讀者為運維/桌面工程/自動化實施人員,需本機 Administrator 權限。

概述

本文提供一條成功路徑:在清空系統前,把「該帶走的都帶走」,特別是多瀏覽器多帳號環境;過程可複製、可驗證、可回滾,並附完整一鍵腳本與驗證點。

先決條件與維護窗口

  • 權限:<USER> 具本機 Administrators 權限;以「系統管理員身分」開啟 PowerShell。
  • 介質:準備 Windows 11 24H2/25H2 官方 ISO(如走就地升級預檢/替代路徑時用)。
  • 磁碟空間:備份目的磁碟(如 D:\Win10_Backup 或外接硬碟)剩餘 ≥ 50 GB(視個人資料量調整)。
  • 網路:建議有可用有線網路,Wi-Fi 憑證將導出為 XML。
  • 殺軟:臨時關閉第三方防毒/管控,以免鎖檔影響拷貝與日誌。
  • BitLocker:記錄恢復金鑰;清裝前可暫停保護器(見下文策略)。
  • 時間預算:備份 10–60 分鐘(視資料量)、重裝與恢復 30–120 分鐘。

風險與回滾

  • 觸發時機:發現備份缺漏、安裝失敗、或升級相容性報錯(如 -1047527152 / 0xC1900110-1047526896 / 0xC1900208)。
  • 回滾策略:保留 D:\Win10_Backup(或外接碟)完整目錄;必要時可用同版 ISO 就地修復安裝或重裝回 Win10,再用「收尾/恢復」章節把驅動、軟體、瀏覽器與配置導回。
  • 檢查點/快照:若在虛擬化環境,於執行腳本前建立 VM 快照;實機可另行製作整碟影像(工具自選)。

TL;DR 執行清單

  1. 以管理員開 PowerShell;建立備份根目錄 D:\Win10_Backup
  2. 執行「一鍵穩健備份腳本」(見附錄);全程產生日誌 backup_log_*.txt
  3. 於各瀏覽器手動匯出:密碼 CSV、書籤 HTML、重要擴充規則(如 uBlock 設定)。
  4. (可選)ISO 相容性預檢:setup.exe /Compat ScanOnly,確認可升級(非本文主線)。
  5. 建立 RDP 兜底(啟用 TermService + 防火牆 + 開機任務)。
  6. BitLocker 保護器:manage-bde -protectors -disable C: -RebootCount 1
  7. 執行清裝;完成後登入 <USER>
  8. 離線導入驅動(pnputil /add-driver)與 winget import 安裝軟體。
  9. 登入各瀏覽器開啟同步 → 導入書籤/密碼 CSV(用後刪除 CSV)。
  10. 按「安裝後驗證清單」逐項核對並清理備份敏感資料。

分階段步驟

一、預檢

目的:確認 OS/權限/磁碟空間與日誌路徑,避免中途失敗。

命令(PowerShell)

# 以系統管理員身分執行
$backup = "D:\Win10_Backup"
$admin  = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole("Administrators")
Write-Host "Admin = $admin"
Get-ComputerInfo | Select-Object OsName, OsVersion, OsBuildNumber
Get-Volume | Where-Object DriveLetter -eq 'D' | Select-Object DriveLetter, SizeRemaining

期望輸出/日誌位置Admin = True;顯示 OS 版本與 D: 剩餘空間。日誌稍後由腳本的 Start-Transcript 生成於 D:\Win10_Backup\backup_log_*.txt

驗證與繼續條件:具管理員權限且備份盤剩餘 ≥ 50 GB。

二、相容性掃描(可選,ISO 就地升級預檢)

目的:若考慮以 ISO 就地升級(非本文主線),先做 Compat ScanOnly,提前暴露阻塞。

命令(CMD,替換光碟盤符 <ISO_DRIVE>

<ISO_DRIVE>:\setup.exe /Compat ScanOnly /DynamicUpdate Disable /CopyLogs D:\Win11_Logs /EULA accept

期望輸出/日誌位置:返回 0;日誌於 C:\$WINDOWS.~BT\Sources\Panther\D:\Win11_Logs

常見報錯

  • -1047526896 / 0xC1900208:不相容應用;卸載或更新後重試。
  • -1047527152 / 0xC1900110:升級被取消;檢查 setuperr.logsetupact.log

驗證與繼續條件:無阻塞即通過;本文後續走「清裝 + 恢復」。

三、RDP 兜底

目的:確保遠端可回收系統(啟動遠端桌面服務與防火牆規則,並設置開機任務)。

命令(PowerShell)

# 啟用 RDP 服務與防火牆
Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Terminal Server" -Name "fDenyTSConnections" -Value 0
Enable-NetFirewallRule -DisplayGroup "Remote Desktop"
Set-Service -Name TermService -StartupType Automatic
Start-Service TermService

# 兜底開機任務(SYSTEM + AtStartup)
$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-NoProfile -WindowStyle Hidden -Command `"Start-Service TermService; Enable-NetFirewallRule -DisplayGroup 'Remote Desktop'`""
$trigger = New-ScheduledTaskTrigger -AtStartup
Register-ScheduledTask -TaskName "RDP_Failsafe_Enable" -Action $action -Trigger $trigger -RunLevel Highest -User "SYSTEM"

期望輸出/日誌位置:任務建立成功;事件檢視器 Microsoft-Windows-TaskScheduler/Operational 有 106/129 事件。

驗證與繼續條件qwinsta 可見 RDP-Tcp;Test-NetConnection -ComputerName <HOST> -Port 3389 成功。

四、BitLocker 保護器策略

目的:避免重啟過程因保護器導致等待人工 PIN/恢復。

命令(CMD)

manage-bde -protectors -disable C: -RebootCount 1

期望輸出/日誌位置:顯示「保護器已停用」;事件 BitLocker-DrivePreparationTool 正常。

驗證與繼續條件manage-bde -status C: 顯示 Protection Status: Protection Off

五、靜默執行:一鍵穩健備份

目的:以 robocopy 為核心穩定複製(支援大量/長路徑/重試),並導出應用清單、驅動、WSL、SSH、Wi-Fi、郵件客戶端、主流瀏覽器與 VPN/代理配置。

命令(PowerShell,一次性執行)

# === 基本設定與日誌 ===
$ErrorActionPreference = 'Continue'
$backup = "D:\Win10_Backup"
$log    = Join-Path $backup ("backup_log_{0:yyyyMMdd_HHmmss}.txt" -f (Get-Date))
function New-Dir($p){ if(-not (Test-Path $p)){ New-Item -ItemType Directory -Path $p -Force | Out-Null } }
New-Dir $backup
Start-Transcript -Path $log -Append | Out-Null

try {
  $drive = (Get-Item $backup).PSDrive
  if($drive.Free -lt 10GB){ Write-Warning "備份目的地可用空間不足 10GB,請確認。" }
} catch { Write-Warning "無法檢測可用空間:$($_.Exception.Message)" }

function Copy-FolderRobust {
  param([string]$Source, [string]$Dest)
  if(Test-Path $Source){
    New-Dir $Dest
    robocopy $Source $Dest /MIR /R:2 /W:1 /COPY:DAT /DCOPY:DAT /XJ /SL /NFL /NDL /NP /NJH /NJS | Out-Null
  } else {
    Write-Host "略過:$Source 不存在"
  }
}

# === App 清單與驅動 ===
try { winget export -o (Join-Path $backup "apps.json") --include-versions } catch { Write-Warning "winget export 失敗:$($_.Exception.Message)" }
New-Dir (Join-Path $backup "Drivers")
cmd /c "pnputil /export-driver * ""$backup\Drivers""" | Out-Null

# === SSH / Wi-Fi ===
Copy-FolderRobust "$env:USERPROFILE\.ssh" (Join-Path $backup "ssh")
New-Dir (Join-Path $backup "WiFi")
cmd /c "netsh wlan export profile key=clear folder=""$backup\WiFi""" | Out-Null

# === 瀏覽器(Chromium 家族) ===
$chromiumMap = @{
  "Chrome"    = "$env:LOCALAPPDATA\Google\Chrome\User Data"
  "Edge"      = "$env:LOCALAPPDATA\Microsoft\Edge\User Data"
  "Brave"     = "$env:LOCALAPPDATA\BraveSoftware\Brave-Browser\User Data"
  "Vivaldi"   = "$env:LOCALAPPDATA\Vivaldi\User Data"
  "Opera"     = "$env:APPDATA\Opera Software\Opera Stable"
  "OperaGX"   = "$env:APPDATA\Opera Software\Opera GX Stable"
}
foreach($name in $chromiumMap.Keys){
  Copy-FolderRobust $chromiumMap[$name] (Join-Path $backup "Browsers\$name")
}

# === Firefox(Roaming) ===
if(Test-Path "$env:APPDATA\Mozilla\Firefox"){
  Copy-FolderRobust "$env:APPDATA\Mozilla\Firefox\Profiles" (Join-Path $backup "Browsers\Firefox\Profiles")
  Copy-Item "$env:APPDATA\Mozilla\Firefox\profiles.ini" (Join-Path $backup "Browsers\Firefox") -Force
}

# === Outlook / Thunderbird / WeChat ===
Copy-FolderRobust "$env:USERPROFILE\Documents\Outlook Files" (Join-Path $backup "Outlook")
Copy-FolderRobust "$env:APPDATA\Microsoft\Signatures"        (Join-Path $backup "Outlook\Signatures")
Copy-FolderRobust "$env:APPDATA\Thunderbird"                 (Join-Path $backup "Thunderbird")
Copy-FolderRobust "$env:USERPROFILE\Documents\WeChat Files"  (Join-Path $backup "WeChat\Documents")
Copy-FolderRobust "$env:APPDATA\Tencent\WeChat Files"        (Join-Path $backup "WeChat\AppData")

# === VPN / 代理 ===
Copy-FolderRobust "$env:ProgramFiles\WireGuard\Data\Configurations" (Join-Path $backup "WireGuard")
Copy-FolderRobust "$env:ProgramFiles\OpenVPN\config"                (Join-Path $backup "OpenVPN")
Copy-FolderRobust "$env:USERPROFILE\OpenVPN\config"                 (Join-Path $backup "OpenVPN_User")
Copy-FolderRobust "$env:APPDATA\v2rayN"                             (Join-Path $backup "v2rayN\AppData")
Copy-FolderRobust "$env:LOCALAPPDATA\v2rayN"                        (Join-Path $backup "v2rayN\Local")

# === 使用者常用資料夾 ===
$folders = @("Desktop","Documents","Pictures","Videos","Downloads")
foreach($f in $folders){
  $src = Join-Path $env:USERPROFILE $f
  $dst = Join-Path (Join-Path $backup "User") $f
  Copy-FolderRobust $src $dst
}

# === 可選:WSL 匯出(若已安裝) ===
try {
  $wslList = & wsl.exe --list --quiet 2>$null
  if($LASTEXITCODE -eq 0 -and $wslList){
    $wslDir = Join-Path $backup "WSL"
    New-Dir $wslDir
    $wslList | ForEach-Object {
      $name = $_.Trim()
      if($name){
        $tar = Join-Path $wslDir ($name -replace '[^\w\.-]','_') + ".tar"
        & wsl.exe --export $name $tar 2>$null
      }
    }
  }
} catch {
  Write-Warning "WSL 匯出略過:$($_.Exception.Message)"
}

# === 統計摘要 ===
try {
  $size = (Get-ChildItem $backup -Recurse -Force | Measure-Object Length -Sum).Sum
  "{0:yyyy-MM-dd HH:mm:ss} 備份完成,總大小:{1:N2} GB" -f (Get-Date), ($size/1GB) | Write-Host
} catch {}

Stop-Transcript | Out-Null

期望輸出/日誌位置:控制台顯示「備份完成,總大小:X.XX GB」;詳細日誌位於 D:\Win10_Backup\backup_log_*.txt

驗證與繼續條件:存在以下關鍵產物:

  • D:\Win10_Backup\apps.json(winget 軟體清單)
  • D:\Win10_Backup\Drivers\*.inf(驅動導出)
  • D:\Win10_Backup\Browsers\<Browser>\*(瀏覽器資料)
  • D:\Win10_Backup\WiFi\*.xml(Wi-Fi 憑證)
  • D:\Win10_Backup\ssh\*(SSH 金鑰/設定)
  • D:\Win10_Backup\WSL\*.tar(如有)

六、重啟與清裝

目的:執行 Windows 11 全新安裝(本節不展開)。

驗證與繼續條件:安裝完成可登入 <USER>winver 顯示 Windows 11 版本號(見下節核對)。

七、收尾(恢復與清理)

目的:離線導入驅動、批量安裝應用、恢復 Wi-Fi 與瀏覽器環境,並重啟 BitLocker 保護。

命令(CMD/PowerShell)

:: 1) 離線導入驅動(CMD)
pnputil /add-driver D:\Win10_Backup\Drivers\* /subdirs /install
# 2) 安裝 winget 軟體(PowerShell)
winget import -i D:\Win10_Backup\apps.json

# 3) 導入 Wi-Fi 設定(PowerShell,可逐一)
Get-ChildItem D:\Win10_Backup\WiFi\*.xml | ForEach-Object {
  netsh wlan add profile filename="$($_.FullName)"
}

# 4) Firefox 檔回置(必要時)
Copy-Item "D:\Win10_Backup\Browsers\Firefox\profiles.ini" "$env:APPDATA\Mozilla\Firefox" -Force
robocopy "D:\Win10_Backup\Browsers\Firefox\Profiles" "$env:APPDATA\Mozilla\Firefox\Profiles" /MIR

# 5) RDP 驗證(如需)
Test-NetConnection -ComputerName <HOST> -Port 3389

# 6) 重新啟用 BitLocker(PowerShell 或 CMD)
manage-bde -protectors -enable C:

期望輸出/日誌位置pnputil 成功導入;winget import 逐項安裝完成;BitLocker Protection: On

驗證與繼續條件:瀏覽器登入同步正常;關鍵應用可啟動;Wi-Fi 能連線;RDP 能連通。

安裝後驗證清單

  • 版本號winver 顯示 Windows 11 24H2/25H2;或 PowerShell (Get-ComputerInfo).OsVersion
  • 服務/RDPGet-Service TermService = Running;Test-NetConnection <HOST> -Port 3389 成功。
  • BitLockermanage-bde -status C: Protection: On;保護器列出正常。
  • 事件/日誌SetupMicrosoft-Windows-BitLocker/BitLocker Management 無錯;備份日誌留存於 D:\Win10_Backup\backup_log_*.txt
  • 留存產物apps.jsonDriversWiFiWSLBrowsers 等目錄齊備;密碼 CSV 已導入並刪除

故障排除(與本成功路徑高度相關)

  • winget import 報錯(找不到套件/源):更新源 winget source update;或單獨安裝缺包。常見錯碼:-2147467259 / 0x80004005
  • pnputil 導入失敗:確認 INF 完整與簽章;重試指令附 /subdirs /install。常見錯碼:-2147467259 / 0x80004005
  • Wi-Fi 導入提示介面關閉:先開啟無線 netsh interface set interface name="Wi-Fi" admin=ENABLED,再導入。
  • WSL 匯出/還原失敗:關閉分佈 wsl --shutdown 後重試;常見錯碼:-2147024891 / 0x80070005(權限)。
  • 瀏覽器登入狀態消失:屬於預期(DPAPI 綁定舊系統);請以帳號同步 + CSV/HTML 雙保險導回。
  • ISO 預檢報錯
    • -1047526896 / 0xC1900208:卸載不相容軟體或更新驅動/BIOS。
    • -1047527152 / 0xC1900110:升級被取消;檢查 setuperr.log/setupact.log 並清理暫存。
    • -2147024865 / 0x8007001F:裝置 I/O/音訊驅動衝突;拔除非必要 USB,更新驅動後重試。

附錄

完整一鍵腳本(可直接貼上執行)

PowerShell:Win10 → Win11 清裝前穩健備份腳本(含日誌/重試/瀏覽器覆蓋)
# 以系統管理員身分執行;可調整目的地磁碟
$ErrorActionPreference = 'Continue'
$backup = "D:\Win10_Backup"
$log    = Join-Path $backup ("backup_log_{0:yyyyMMdd_HHmmss}.txt" -f (Get-Date))
function New-Dir($p){ if(-not (Test-Path $p)){ New-Item -ItemType Directory -Path $p -Force | Out-Null } }
New-Dir $backup
Start-Transcript -Path $log -Append | Out-Null

function Copy-FolderRobust {
  param([string]$Source, [string]$Dest)
  if(Test-Path $Source){
    New-Dir $Dest
    robocopy $Source $Dest /MIR /R:2 /W:1 /COPY:DAT /DCOPY:DAT /XJ /SL /NFL /NDL /NP /NJH /NJS | Out-Null
  } else {
    Write-Host "略過:$Source 不存在"
  }
}

try { winget export -o (Join-Path $backup "apps.json") --include-versions } catch {}
New-Dir (Join-Path $backup "Drivers")
cmd /c "pnputil /export-driver * ""$backup\Drivers""" | Out-Null
Copy-FolderRobust "$env:USERPROFILE\.ssh" (Join-Path $backup "ssh")
New-Dir (Join-Path $backup "WiFi")
cmd /c "netsh wlan export profile key=clear folder=""$backup\WiFi""" | Out-Null

$chromiumMap = @{
  "Chrome"    = "$env:LOCALAPPDATA\Google\Chrome\User Data"
  "Edge"      = "$env:LOCALAPPDATA\Microsoft\Edge\User Data"
  "Brave"     = "$env:LOCALAPPDATA\BraveSoftware\Brave-Browser\User Data"
  "Vivaldi"   = "$env:LOCALAPPDATA\Vivaldi\User Data"
  "Opera"     = "$env:APPDATA\Opera Software\Opera Stable"
  "OperaGX"   = "$env:APPDATA\Opera Software\Opera GX Stable"
}
foreach($name in $chromiumMap.Keys){ Copy-FolderRobust $chromiumMap[$name] (Join-Path $backup "Browsers\$name") }

if(Test-Path "$env:APPDATA\Mozilla\Firefox"){
  Copy-FolderRobust "$env:APPDATA\Mozilla\Firefox\Profiles" (Join-Path $backup "Browsers\Firefox\Profiles")
  Copy-Item "$env:APPDATA\Mozilla\Firefox\profiles.ini" (Join-Path $backup "Browsers\Firefox") -Force
}

Copy-FolderRobust "$env:USERPROFILE\Documents\Outlook Files" (Join-Path $backup "Outlook")
Copy-FolderRobust "$env:APPDATA\Microsoft\Signatures"        (Join-Path $backup "Outlook\Signatures")
Copy-FolderRobust "$env:APPDATA\Thunderbird"                 (Join-Path $backup "Thunderbird")
Copy-FolderRobust "$env:USERPROFILE\Documents\WeChat Files"  (Join-Path $backup "WeChat\Documents")
Copy-FolderRobust "$env:APPDATA\Tencent\WeChat Files"        (Join-Path $backup "WeChat\AppData")
Copy-FolderRobust "$env:ProgramFiles\WireGuard\Data\Configurations" (Join-Path $backup "WireGuard")
Copy-FolderRobust "$env:ProgramFiles\OpenVPN\config"                (Join-Path $backup "OpenVPN")
Copy-FolderRobust "$env:USERPROFILE\OpenVPN\config"                 (Join-Path $backup "OpenVPN_User")
Copy-FolderRobust "$env:APPDATA\v2rayN"                             (Join-Path $backup "v2rayN\AppData")
Copy-FolderRobust "$env:LOCALAPPDATA\v2rayN"                        (Join-Path $backup "v2rayN\Local")
$folders = @("Desktop","Documents","Pictures","Videos","Downloads")
foreach($f in $folders){ Copy-FolderRobust (Join-Path $env:USERPROFILE $f) (Join-Path (Join-Path $backup "User") $f) }

try {
  $wslList = & wsl.exe --list --quiet 2>$null
  if($LASTEXITCODE -eq 0 -and $wslList){
    $wslDir = Join-Path $backup "WSL"; New-Dir $wslDir
    $wslList | ForEach-Object {
      $name = $_.Trim(); if($name){
        $tar = Join-Path $wslDir ($name -replace '[^\w\.-]','_') + ".tar"
        & wsl.exe --export $name $tar 2>$null
      }
    }
  }
} catch {}

try { $size = (Get-ChildItem $backup -Recurse -Force | Measure-Object Length -Sum).Sum; 
  "{0:yyyy-MM-dd HH:mm:ss} 備份完成,總大小:{1:N2} GB" -f (Get-Date), ($size/1GB) | Write-Host } catch {}
Stop-Transcript | Out-Null

(可選)就地升級的靜默參數範式

批次命令:Compat ScanOnly 與靜默升級(非本文主線,僅供參考)
:: 相容性預檢(僅掃描,不升級)
<ISO_DRIVE>:\setup.exe /Compat ScanOnly /DynamicUpdate Disable /CopyLogs D:\Win11_Logs /EULA accept

:: 靜默升級(必要時)
<ISO_DRIVE>:\setup.exe /Auto Upgrade /DynamicUpdate Disable /Quiet /NoReboot /CopyLogs D:\Win11_Logs /EULA accept

錯碼舉例-1047526896 / 0xC1900208-2147024891 / 0x80070005-2147024865 / 0x8007001F;請以 setuperr.log/setupact.log 快速定位。






0 条回应

必须 注册 为本站用户, 登录 后才可以发表评论!