使用動態更新更新 Windows 安裝媒體
本文說明如何在部署之前取得動態更新套件並套用至現有的 Windows 映像,並包含可用來自動化此程式的 Windows PowerShell 腳本。
大量授權媒體適用於大量授權服務中心的每個 Windows 版本, (VLSC) 和其他相關通道,例如商務用 Windows Update、Windows Server Update Services (WSUS) 和 Visual Studio 訂閱。 您可以使用動態更新,確保 Windows 裝置在就地升級時擁有最新的功能更新套件,同時保留先前可能已安裝的語言套件和功能隨選 (FOD) 。 動態更新也不需要在就地升級程式中安裝個別的品質更新。
動態更新
每當功能更新的安裝開始 (無論是從連線到 Windows Update) 的媒體或環境,動態更新都是第一個步驟之一。 Windows 安裝程式會連絡Microsoft端點以擷取動態更新套件,然後將這些更新套用至您的操作系統安裝媒體。 更新套件包含下列類型的更新:
- 匯報 Setup.exe 二進位檔或其他安裝程式用於功能更新的檔案
- 匯報 用於 Windows 復原環境的「安全操作系統」 (SafeOS)
- 匯報 至完成功能更新所需的服務堆疊。如需詳細資訊,請參閱維護堆棧更新。
- 最新的累積 (品質) 更新
- 匯報 專供動態更新之製造商發佈的適用驅動程式
動態更新會藉由重新取得語言套件和功能隨選套件來保留它們。
裝置必須能夠連線到因特網,才能取得動態 匯報。 在某些環境中,不是取得動態 匯報 的選項。 在裝置上啟動安裝程式之前,您仍然可以取得動態更新套件並將它套用至映像,以進行媒體型功能更新。
取得動態更新套件
您可以從 Microsoft更新類別目錄取得動態更新套件。 在該網站上,使用右上方的搜尋列來尋找特定版本的動態更新套件。 各種動態更新套件可能不會全部出現在單一搜尋的結果中,因此您可能必須使用不同的關鍵詞進行搜尋,以尋找所有更新。 檢查結果的各個部分,確定您已識別出所需的檔案。 下表顯示要在結果中搜尋或尋找的索引鍵值。
Windows Server 2025 動態更新套件
標題 可以區分每個動態套件。 最新的累積更新內嵌服務堆疊。 只有在指定的累積更新需要時,才會發佈服務堆疊。
更新套件 | Title |
---|---|
安全作業系統動態更新 | 適用於 Microsoft 伺服器作業系統版本 24H2 的 YYYY-MM 安全 OS 動態更新 |
設定動態更新 | Microsoft伺服器作業系統 24H2 版的 YYYY-MM 安裝程式動態更新 |
最新的累積更新 | Microsoft伺服器操作系統 24H2 版的 YYYY-MM 累積更新 |
服務堆疊動態更新 | Microsoft伺服器作業系統 24H2 版的 YYYY-MM 服務堆疊更新 |
Windows Server 版本 23H2 動態更新套件
標題 可以區分每個動態套件。 最新的累積更新內嵌服務堆疊。 只有在指定的累積更新需要時,才會發佈服務堆疊。 Azure Stack HCI 版本 23H2 具有類似的格式。
更新套件 | Title |
---|---|
安全作業系統動態更新 | 適用於 Microsoft 伺服器作業系統版本 23H2 的 YYYY-MM 安全 OS 動態更新 |
設定動態更新 | Microsoft伺服器作業系統 23H2 版的 YYYY-MM 安裝程式動態更新 |
最新的累積更新 | Microsoft伺服器作業系統 23H2 版的 YYYY-MM 累積更新 |
服務堆疊動態更新 | Microsoft伺服器作業系統 23H2 版的 YYYY-MM 服務堆疊更新 |
Azure Stack HCI 22H2 版動態更新套件
需要標題、 產品 和 描述 ,才能區分每個動態套件。 最新的累積更新內嵌服務堆疊。 服務堆疊只有在必要時才會個別發佈,作為指定累積更新的必要條件。
更新套件 | Title | 產品 | 說明 |
---|---|---|---|
安全作業系統動態更新 | 適用於 Microsoft 伺服器作業系統的 YYYY-MM 動態更新,版本 22H2 | Windows 安全作業系統動態更新 | ComponentUpdate |
設定動態更新 | 適用於 Microsoft 伺服器作業系統的 YYYY-MM 動態更新,版本 22H2 | Windows 10 和更新版本動態更新 | SetupUpdate |
最新的累積更新 | Microsoft伺服器操作系統的YYYY-MM累積更新,版本22H2 | ||
服務堆疊動態更新 | 適用於 Microsoft 伺服器作業系統的 YYYY-MM 服務堆疊更新,版本 22H2 |
Windows Server 2022 更新版本動態更新套件
需要標題、 產品 和 描述 ,才能區分每個動態套件。 最新的累積更新內嵌服務堆疊。 服務堆疊只有在必要時才會個別發佈,作為指定累積更新的必要條件。
更新套件 | Title | 產品 | 說明 |
---|---|---|---|
安全作業系統動態更新 | Microsoft伺服器作業系統的 YYYY-MM 動態更新,版本 21H2 | Windows 安全作業系統動態更新 | ComponentUpdate |
設定動態更新 | Microsoft伺服器作業系統的 YYYY-MM 動態更新,版本 21H2 | Windows 10 和更新版本動態更新 | SetupUpdate |
最新的累積更新 | Microsoft伺服器作業系統的 YYYY-MM 累積更新版本 21H2 | ||
服務堆疊動態更新 | 適用於 Microsoft 伺服器作業系統的 YYYY-MM 服務堆疊更新,版本 21H2 |
Windows 11 版本 22H2 和更新版本動態更新套件
標題 可以區分每個動態套件。 最新的累積更新內嵌服務堆疊。 只有在指定的累積更新需要時,才會發佈服務堆疊。 下列標題適用於 Windows 11 版本 22H2。 Windows 11,版本 23H2 和 24H2 具有類似的格式。
更新套件 | Title |
---|---|
安全作業系統動態更新 | 適用於 Windows 11 22H2 版的 YYYY-MM 安全 OS 動態更新 |
設定動態更新 | YYYY-MM 設定 Windows 11 版本 22H2 的動態更新 |
最新的累積更新 | Windows 11 22H2 版的 YYYY-MM 累積更新 |
服務堆疊動態更新 | Windows 11 22H2 版的YYYY-MM服務堆疊更新 |
Windows 11 版本 21H2 動態更新套件
需要標題、 產品 和 描述 ,才能區分每個動態套件。 最新的累積更新內嵌服務堆疊。 服務堆疊只有在必要時才會個別發佈,作為指定累積更新的必要條件。
更新套件 | Title | 產品 | 說明 |
---|---|---|---|
安全作業系統動態更新 | 適用於 Windows 11的YYYY-MM動態更新 | Windows 安全作業系統動態更新 | ComponentUpdate |
設定動態更新 | 適用於 Windows 11的YYYY-MM動態更新 | Windows 10 和更新版本動態更新 | SetupUpdate |
最新的累積更新 | Windows 11的YYYY-MM累積更新 | ||
服務堆疊動態更新 | Windows 11 21H2 版的YYYY-MM服務堆疊更新 |
Windows 10 版本 22H2 動態更新套件
需要標題、 產品 和 描述 ,才能區分每個動態套件。 最新的累積更新內嵌服務堆疊。 服務堆疊只有在必要時才會個別發佈,作為指定累積更新的必要條件。
更新套件 | Title | 產品 | 說明 |
---|---|---|---|
安全作業系統動態更新 | Windows 10 22H2 版的YYYY-MM動態更新 | Windows 安全作業系統動態更新 | ComponentUpdate |
設定動態更新 | Windows 10 22H2 版的YYYY-MM動態更新 | Windows 10 和更新版本動態更新 | SetupUpdate |
最新的累積更新 | Windows 10 22H2 版的 YYYY-MM 累積更新 | ||
服務堆疊動態更新 | Windows 10 22H2 版的 YYYY-MM 服務堆疊更新 |
如果您想要使用其他語言或功能隨選自定義映像,請從 大量授權服務中心下載補充媒體 ISO 檔案。 例如,如果您的裝置將停用動態更新,而且如果使用者需要特定的功能隨選,您可以將這些功能預安裝到映射中。
更新 Windows 安裝媒體
正確更新安裝媒體牽涉到在數個不同的目標上運作的許多動作, (映像檔案) 。 某些動作會在不同的目標上重複。 目標映像檔案包括:
- Windows 預安裝環境 (WinPE) :用來安裝、部署和修復 Windows 作業系統的小型操作系統
- Windows Recovery Environment (WinRE) :修復無法啟動操作系統的常見原因。 WinRE 是以 WinPE 為基礎,而且可以使用其他驅動程式、語言、選擇性套件和其他疑難解答或診斷工具來自定義。
- Windows 操作系統:儲存在 \sources\install.wim 中的一或多個 Windows 版本
- Windows 安裝媒體:Windows 安裝媒體中檔案和資料夾的完整集合。 例如,\sources 資料夾、\boot 資料夾、Setup.exe 等等。
下表顯示將各種工作套用至檔案的正確順序。 例如,完整順序會從將維護堆疊更新新增至 WinRE (1) 開始,最後將開機管理員從 WinPE 新增至新的媒體 (28) 。
工作 | winRE (winre.wim) | 操作系統 (install.wim) | winPE (boot.wim) | 新媒體 |
---|---|---|---|---|
新增服務堆疊動態更新 | 1 | 9 | 17 | |
新增語言套件 | 2 | 10 | 18 | |
新增本地化的選擇性套件 | 3 | 19 | ||
新增字型支援 | 4 | 20 | ||
新增文字到語音轉換 | 5 | 21 | ||
更新 Lang.ini | 22 | |||
視需要新增功能 | 11 | |||
新增安全OS動態更新 | 6 | |||
新增安裝程序動態更新 | 26 | |||
從 WinPE 新增 setup.exe 和 setuphost.exe | 27 | |||
從 WinPE 新增開機管理員 | 28 | |||
新增最新的累積更新 | 12 | 23 | ||
清除映像 | 7 | 13 | 24 | |
新增選擇性元件 | 14 | |||
新增 .NET 和 .NET 累積更新 | 15 | |||
匯出映像 | 8 | 16 | 25 |
注意
從 2021 年 2 月開始,最新的累積更新和服務堆疊更新將會合併並散發於Microsoft更新類別目錄中,作為新的合併累積更新。 針對需要服務堆疊更新以更新安裝媒體的步驟 1、9 和 18,您應該使用合併的累積更新。 如需合併累積更新的詳細資訊,請參閱 服務堆疊更新。
注意
Microsoft會從 Windows 移除 Flash 元件,KB4577586「Adobe Flash Player 的移除更新」。 您也可以在步驟 20 到 21 之間的目錄) 上部署可用KB4577586 (中的更新,隨時移除 Flash。 自 2021 年 7 月起,KB4577586,Windows 10 版本 1607 和 1507 的最新累積更新中將包含「移除 Adobe Flash Player 的更新」。 更新也會包含在 Windows 8.1、Windows Server 2012 和 Windows Embedded 8 Standard 的每月匯總和僅限安全性更新中。 如需詳細資訊,請 參閱 Adobe Flash Player 終止支援更新。
多個 Windows 版本
install.wim) (主作業系統檔案可能包含多個版本的 Windows。 根據索引,部署指定版本可能只需要更新。 或者,可能是所有版本都需要更新。 此外,請確定語言已安裝在功能隨選之前,且最新的累積更新一律會最後套用。
其他語言和功能
您不需要在映像中新增更多語言和功能來完成更新,但除了開始映射之外,還有機會使用更多語言、選擇性元件和功能隨選自定義映像。 當您新增更多語言和功能時,請務必以正確的順序進行這些變更:先套用服務堆棧更新,後面接著新增語言,再新增功能,最後套用最新的累積更新。 提供的範例腳本會安裝第二種語言 (,在此案例中為日文 (ja-JP) ) 。 由於此語言是由 lp.cab 所支援,因此不需要新增語言體驗套件。 日文會同時新增至主要操作系統和復原環境,以允許使用者以日文檢視復原畫面。 這包括新增目前安裝在復原映像中的當地語系化套件版本。
選擇性元件和 .NET 功能可以離線安裝,不過這樣做會建立需要裝置重新啟動的暫止作業。 因此,執行映像清除的呼叫將會失敗。 有兩個選項可避免清除失敗。 其中一個選項是略過映射清除步驟,不過這會導致較大的 install.wim。 另一個選項是在清除之後,但在導出之前,於步驟中安裝 .NET 和選擇性元件。 這是範例腳本中的選項。 如此一來,您就必須從原始的 install.wim (開始,當您在下次維護或更新映射時,不會) 任何暫止動作,例如下一個月) (。
檢查點累積更新
從 Windows 11、版本 24H2 和 Windows Server 2025 開始,最新的累積更新可能具有必須先安裝的必要條件累積更新。 這些稱為檢查點累積更新。 在這些情況下,累積更新檔案層級差異是以先前的累積更新為基礎,而不是 Windows RTM 版本。 優點是較小的更新套件和更快速的安裝。 當您從 Microsoft更新類別目錄取得最新的累積更新時,會從下載按鈕取得檢查點累積更新。 此外,累積更新的 知識庫 文章將會提供其他資訊。
若要在服務 Windows OS 時安裝檢查點 () (步驟 9 & 12) 和 WinPE (步驟 17 & 23) ,請使用目標累積更新來呼叫 Add-WindowsPackage
。 的資料夾 -PackagePath
會視需要用來探索並安裝一或多個檢查點。 只有目標累積更新和檢查點累積更新應該位於 資料夾中 -PackagePath
。 具有修訂 <的累積更新套件 = 將會處理目標累積更新。 如果您不是使用其他語言和/或選擇性功能自定義映像,則可以針對上述步驟 9 & 17,先) 個別呼叫 Add-WindowsPackage
(檢查點累積更新。 步驟 12 和 23 無法使用個別呼叫。
Windows PowerShell 腳本來將動態 匯報 套用至現有的映像
這些範例僅供說明之用,因此缺少錯誤處理。 文稿假設下列套件會儲存在此資料夾結構的本機:
資料夾 | 描述 |
---|---|
C:\mediaRefresh | 包含 PowerShell 腳本的父資料夾 |
C:\mediaRefresh\oldMedia | 包含將重新整理之原始媒體的資料夾。 例如, 包含 Setup.exe 和 \sources資料夾。 |
C:\mediaRefresh\newMedia | 將包含更新媒體的資料夾。 它會從 \oldMedia 複製,然後作為所有更新和清除作業的目標。 |
入門
腳本一開始會宣告全域變數,並建立要用於掛接影像的資料夾。 然後,製作原始媒體的複本,從 \oldMedia 到 \newMedia,在發生腳本錯誤時保留原始媒體,而且必須從已知狀態重新開始。 此外,它提供舊媒體與新媒體的比較,以評估變更。 若要確保新的媒體更新,請確定它們不是唯讀的。
#Requires -RunAsAdministrator
function Get-TS { return "{0:HH:mm:ss}" -f [DateTime]::Now }
Write-Output "$(Get-TS): Starting media refresh"
# Declare language for showcasing adding optional localized components
$LANG = "ja-jp"
$LANG_FONT_CAPABILITY = "jpan"
# Declare media for FOD and LPs
# Note: Starting with Windows 11, version 21H2, the language pack (LANGPACK) ISO has been superseded by the FOD ISO.
# Language packs and the \Windows Preinstallation Environment packages are part of the LOF ISO.
# If you are using this script for Windows 10, modify to mount and use the LANGPACK ISO.
$FOD_ISO_PATH = "C:\mediaRefresh\packages\FOD-PACKAGES_OEM_PT1_amd64fre_MULTI.iso"
# Declare Dynamic Update packages. A dedicated folder is used for the latest cumulative update, and as needed
# checkpoint cumulative updates.
$LCU_PATH = "C:\mediaRefresh\packages\CU\LCU.msu"
$SSU_PATH = "C:\mediaRefresh\packages\Other\SSU_DU.msu"
$SETUP_DU_PATH = "C:\mediaRefresh\packages\Other\Setup_DU.cab"
$SAFE_OS_DU_PATH = "C:\mediaRefresh\packages\Other\SafeOS_DU.cab"
$DOTNET_CU_PATH = "C:\mediaRefresh\packages\Other\DotNet_CU.msu"
# Declare folders for mounted images and temp files
$MEDIA_OLD_PATH = "C:\mediaRefresh\oldMedia"
$MEDIA_NEW_PATH = "C:\mediaRefresh\newMedia"
$WORKING_PATH = "C:\mediaRefresh\temp"
$MAIN_OS_MOUNT = "C:\mediaRefresh\temp\MainOSMount"
$WINRE_MOUNT = "C:\mediaRefresh\temp\WinREMount"
$WINPE_MOUNT = "C:\mediaRefresh\temp\WinPEMount"
# Mount the Features on Demand ISO
Write-Output "$(Get-TS): Mounting FOD ISO"
$FOD_ISO_DRIVE_LETTER = (Mount-DiskImage -ImagePath $FOD_ISO_PATH -ErrorAction stop | Get-Volume).DriveLetter
# Note: Starting with Windows 11, version 21H2, the correct path for main OS language and optional features
# moved to \LanguagesAndOptionalFeatures instead of the root. For Windows 10, use $FOD_PATH = $FOD_ISO_DRIVE_LETTER + ":\"
$FOD_PATH = $FOD_ISO_DRIVE_LETTER + ":\LanguagesAndOptionalFeatures"
# Declare language related cabs
$WINPE_OC_PATH = "$FOD_ISO_DRIVE_LETTER`:\Windows Preinstallation Environment\x64\WinPE_OCs"
$WINPE_OC_LANG_PATH = "$WINPE_OC_PATH\$LANG"
$WINPE_OC_LANG_CABS = Get-ChildItem $WINPE_OC_LANG_PATH -Name
$WINPE_OC_LP_PATH = "$WINPE_OC_LANG_PATH\lp.cab"
$WINPE_FONT_SUPPORT_PATH = "$WINPE_OC_PATH\WinPE-FontSupport-$LANG.cab"
$WINPE_SPEECH_TTS_PATH = "$WINPE_OC_PATH\WinPE-Speech-TTS.cab"
$WINPE_SPEECH_TTS_LANG_PATH = "$WINPE_OC_PATH\WinPE-Speech-TTS-$LANG.cab"
$OS_LP_PATH = "$FOD_PATH\Microsoft-Windows-Client-Language-Pack_x64_$LANG.cab"
# Create folders for mounting images and storing temporary files
New-Item -ItemType directory -Path $WORKING_PATH -ErrorAction Stop | Out-Null
New-Item -ItemType directory -Path $MAIN_OS_MOUNT -ErrorAction stop | Out-Null
New-Item -ItemType directory -Path $WINRE_MOUNT -ErrorAction stop | Out-Null
New-Item -ItemType directory -Path $WINPE_MOUNT -ErrorAction stop | Out-Null
# Keep the original media, make a copy of it for the new, updated media.
Write-Output "$(Get-TS): Copying original media to new media path"
Copy-Item -Path $MEDIA_OLD_PATH"\*" -Destination $MEDIA_NEW_PATH -Force -Recurse -ErrorAction stop | Out-Null
Get-ChildItem -Path $MEDIA_NEW_PATH -Recurse | Where-Object { -not $_.PSIsContainer -and $_.IsReadOnly } | ForEach-Object { $_.IsReadOnly = $false }
更新 WinRE 和每個主要 OS Windows 版本
腳本會更新主要操作系統檔案內的每個 Windows 版本, (install.wim) 。 針對每個版本,會掛接主要OS映像。
針對第一個映像,Winre.wim 會複製到工作資料夾並掛接。 然後,它會套用服務堆疊動態更新,因為其元件用於更新其他元件。 因為腳本是選擇性地新增日文,所以會將語言套件新增至映像,並安裝所有已安裝在 Winre.wim 中之選用套件的日文版本。 然後,它會套用安全OS動態更新套件。 其完成方式是清除和導出影像,以減少影像大小。
接下來,針對掛接的OS映射,腳本會從套用服務堆疊動態更新開始。 然後,它會新增日文語言支援,然後新增日文語言功能。 不同於動態更新套件,它會使用 Add-WindowsCapability
來新增這些功能。 如需這類功能及其相關聯功能名稱的完整清單,請參閱 可用的功能隨選。 現在可以啟用其他選擇性元件或新增其他功能隨選。 如果這類功能具有相關聯的累積更新 (例如 .NET) ,則這是套用這些更新的時間。 接著,腳本會繼續套用最新的累積更新。 最後,腳本會清除並匯出映射。 您可以離線安裝選擇性元件以及 .NET 功能,但這需要重新啟動裝置。 這就是為什麼腳本會在清除后和匯出之前安裝 .NET 和選擇性元件。
此程式會針對主要作業系統檔案內的每個 Windows 版本重複執行。 為了減少大小,會儲存第一個映射中的服務 Winre.wim 檔案,並用來更新每個後續的 Windows 版本。 這會減少 install.wim 的最終大小。
#
# Update each main OS Windows image including the Windows Recovery Environment (WinRE)
#
# Get the list of images contained within the main OS
$WINOS_IMAGES = Get-WindowsImage -ImagePath $MEDIA_NEW_PATH"\sources\install.wim"
Foreach ($IMAGE in $WINOS_IMAGES) {
# first mount the main OS image
Write-Output "$(Get-TS): Mounting main OS, image index $($IMAGE.ImageIndex)"
Mount-WindowsImage -ImagePath $MEDIA_NEW_PATH"\sources\install.wim" -Index $IMAGE.ImageIndex -Path $MAIN_OS_MOUNT -ErrorAction stop| Out-Null
if ($IMAGE.ImageIndex -eq "1") {
#
# update Windows Recovery Environment (WinRE) within this OS image
#
Copy-Item -Path $MAIN_OS_MOUNT"\windows\system32\recovery\winre.wim" -Destination $WORKING_PATH"\winre.wim" -Force -ErrorAction stop | Out-Null
Write-Output "$(Get-TS): Mounting WinRE"
Mount-WindowsImage -ImagePath $WORKING_PATH"\winre.wim" -Index 1 -Path $WINRE_MOUNT -ErrorAction stop | Out-Null
# Add servicing stack update (Step 1 from the table)
# Depending on the Windows release that you are updating, there are 2 different approaches for updating the servicing stack
# The first approach is to use the combined cumulative update. This is for Windows releases that are shipping a combined
# cumulative update that includes the servicing stack updates (i.e. SSU + LCU are combined). Windows 11, version 21H2 and
# Windows 11, version 22H2 are examples. In these cases, the servicing stack update is not published seperately; the combined
# cumulative update should be used for this step. However, in hopefully rare cases, there may breaking change in the combined
# cumulative update format, that requires a standalone servicing stack update to be published, and installed first before the
# combined cumulative update can be installed.
# This is the code to handle the rare case that the SSU is published and required for the combined cumulative update
# Write-Output "$(Get-TS): Adding package $SSU_PATH"
# Add-WindowsPackage -Path $WINRE_MOUNT -PackagePath $SSU_PATH | Out-Null
# Now, attempt the combined cumulative update.
# There is a known issue where the servicing stack update is installed, but the cumulative update will fail. This error should
# be caught and ignored, as the last step will be to apply the Safe OS update and thus the image will be left with the correct
# packages installed.
Write-Output "$(Get-TS): Adding package $LCU_PATH to WinRE"
try
{
Add-WindowsPackage -Path $WINRE_MOUNT -PackagePath $LCU_PATH | Out-Null
}
Catch
{
$theError = $_
Write-Output "$(Get-TS): $theError"
if ($theError.Exception -like "*0x8007007e*") {
Write-Output "$(Get-TS): This failure is a known issue with combined cumulative update, we can ignore."
}
else {
throw
}
}
# The second approach for Step 1 is for Windows releases that have not adopted the combined cumulative update
# but instead continue to have a seperate servicing stack update published. In this case, we'll install the SSU
# update. This second approach is commented out below.
# Write-Output "$(Get-TS): Adding package $SSU_PATH"
# Add-WindowsPackage -Path $WINRE_MOUNT -PackagePath $SSU_PATH | Out-Null
#
# Optional: Add the language to recovery environment
#
# Install lp.cab cab
Write-Output "$(Get-TS): Adding package $WINPE_OC_LP_PATH to WinRE"
Add-WindowsPackage -Path $WINRE_MOUNT -PackagePath $WINPE_OC_LP_PATH -ErrorAction stop | Out-Null
# Install language cabs for each optional package installed
$WINRE_INSTALLED_OC = Get-WindowsPackage -Path $WINRE_MOUNT
Foreach ($PACKAGE in $WINRE_INSTALLED_OC) {
if ( ($PACKAGE.PackageState -eq "Installed") -and ($PACKAGE.PackageName.startsWith("WinPE-")) -and ($PACKAGE.ReleaseType -eq "FeaturePack") ) {
$INDEX = $PACKAGE.PackageName.IndexOf("-Package")
if ($INDEX -ge 0) {
$OC_CAB = $PACKAGE.PackageName.Substring(0, $INDEX) + "_" + $LANG + ".cab"
if ($WINPE_OC_LANG_CABS.Contains($OC_CAB)) {
$OC_CAB_PATH = Join-Path $WINPE_OC_LANG_PATH $OC_CAB
Write-Output "$(Get-TS): Adding package $OC_CAB_PATH to WinRE"
Add-WindowsPackage -Path $WINRE_MOUNT -PackagePath $OC_CAB_PATH -ErrorAction stop | Out-Null
}
}
}
}
# Add font support for the new language
if ( (Test-Path -Path $WINPE_FONT_SUPPORT_PATH) ) {
Write-Output "$(Get-TS): Adding package $WINPE_FONT_SUPPORT_PATH to WinRE"
Add-WindowsPackage -Path $WINRE_MOUNT -PackagePath $WINPE_FONT_SUPPORT_PATH -ErrorAction stop | Out-Null
}
# Add TTS support for the new language
if (Test-Path -Path $WINPE_SPEECH_TTS_PATH) {
if ( (Test-Path -Path $WINPE_SPEECH_TTS_LANG_PATH) ) {
Write-Output "$(Get-TS): Adding package $WINPE_SPEECH_TTS_PATH to WinRE"
Add-WindowsPackage -Path $WINRE_MOUNT -PackagePath $WINPE_SPEECH_TTS_PATH -ErrorAction stop | Out-Null
Write-Output "$(Get-TS): Adding package $WINPE_SPEECH_TTS_LANG_PATH to WinRE"
Add-WindowsPackage -Path $WINRE_MOUNT -PackagePath $WINPE_SPEECH_TTS_LANG_PATH -ErrorAction stop | Out-Null
}
}
# Add Safe OS
Write-Output "$(Get-TS): Adding package $SAFE_OS_DU_PATH to WinRE"
Add-WindowsPackage -Path $WINRE_MOUNT -PackagePath $SAFE_OS_DU_PATH -ErrorAction stop | Out-Null
# Perform image cleanup
Write-Output "$(Get-TS): Performing image cleanup on WinRE"
DISM /image:$WINRE_MOUNT /cleanup-image /StartComponentCleanup /ResetBase /Defer | Out-Null
# Dismount
Dismount-WindowsImage -Path $WINRE_MOUNT -Save -ErrorAction stop | Out-Null
# Export
Write-Output "$(Get-TS): Exporting image to $WORKING_PATH\winre.wim"
Export-WindowsImage -SourceImagePath $WORKING_PATH"\winre.wim" -SourceIndex 1 -DestinationImagePath $WORKING_PATH"\winre2.wim" -ErrorAction stop | Out-Null
}
Copy-Item -Path $WORKING_PATH"\winre2.wim" -Destination $MAIN_OS_MOUNT"\windows\system32\recovery\winre.wim" -Force -ErrorAction stop | Out-Null
#
# update Main OS
#
# Add servicing stack update (Step 18 from the table)
# Depending on the Windows release that you are updating, there are 2 different approaches for updating the servicing stack
# The first approach is to use the combined cumulative update. This is for Windows releases that are shipping a combined cumulative update that
# includes the servicing stack updates (i.e. SSU + LCU are combined). Windows 11, version 21H2 and Windows 11, version 22H2 are examples. In these
# cases, the servicing stack update is not published seperately; the combined cumulative update should be used for this step. However, in hopefully
# rare cases, there may breaking change in the combined cumulative update format, that requires a standalone servicing stack update to be published,
# and installed first before the combined cumulative update can be installed.
# This is the code to handle the rare case that the SSU is published and required for the combined cumulative update
# Write-Output "$(Get-TS): Adding package $SSU_PATH"
# Add-WindowsPackage -Path $MAIN_OS_MOUNT -PackagePath $SSU_PATH | Out-Null
# Now, attempt the combined cumulative update. Unlike WinRE and WinPE, we don't need to check for error 0x8007007e
Write-Output "$(Get-TS): Adding package $LCU_PATH to main OS, index $($IMAGE.ImageIndex)"
Add-WindowsPackage -Path $MAIN_OS_MOUNT -PackagePath $LCU_PATH | Out-Null
# The second approach for Step 18 is for Windows releases that have not adopted the combined cumulative update
# but instead continue to have a seperate servicing stack update published. In this case, we'll install the SSU
# update. This second approach is commented out below.
# Write-Output "$(Get-TS): Adding package $SSU_PATH to main OS, index $($IMAGE.ImageIndex)"
# Add-WindowsPackage -Path $MAIN_OS_MOUNT -PackagePath $SSU_PATH | Out-Null
# Optional: Add language to main OS
Write-Output "$(Get-TS): Adding package $OS_LP_PATH to main OS, index $($IMAGE.ImageIndex)"
Add-WindowsPackage -Path $MAIN_OS_MOUNT -PackagePath $OS_LP_PATH -ErrorAction stop | Out-Null
# Optional: Add a Features on Demand to the image
Write-Output "$(Get-TS): Adding language FOD: Language.Fonts.Jpan~~~und-JPAN~0.0.1.0 to main OS, index $($IMAGE.ImageIndex)"
Add-WindowsCapability -Name "Language.Fonts.$LANG_FONT_CAPABILITY~~~und-$LANG_FONT_CAPABILITY~0.0.1.0" -Path $MAIN_OS_MOUNT -Source $FOD_PATH -ErrorAction stop | Out-Null
Write-Output "$(Get-TS): Adding language FOD: Language.Basic~~~$LANG~0.0.1.0 to main OS, index $($IMAGE.ImageIndex)"
Add-WindowsCapability -Name "Language.Basic~~~$LANG~0.0.1.0" -Path $MAIN_OS_MOUNT -Source $FOD_PATH -ErrorAction stop | Out-Null
Write-Output "$(Get-TS): Adding language FOD: Language.OCR~~~$LANG~0.0.1.0 to main OS, index $($IMAGE.ImageIndex)"
Add-WindowsCapability -Name "Language.OCR~~~$LANG~0.0.1.0" -Path $MAIN_OS_MOUNT -Source $FOD_PATH -ErrorAction stop | Out-Null
Write-Output "$(Get-TS): Adding language FOD: Language.Handwriting~~~$LANG~0.0.1.0 to main OS, index $($IMAGE.ImageIndex)"
Add-WindowsCapability -Name "Language.Handwriting~~~$LANG~0.0.1.0" -Path $MAIN_OS_MOUNT -Source $FOD_PATH -ErrorAction stop | Out-Null
Write-Output "$(Get-TS): Adding language FOD: Language.TextToSpeech~~~$LANG~0.0.1.0 to main OS, index $($IMAGE.ImageIndex)"
Add-WindowsCapability -Name "Language.TextToSpeech~~~$LANG~0.0.1.0" -Path $MAIN_OS_MOUNT -Source $FOD_PATH -ErrorAction stop | Out-Null
Write-Output "$(Get-TS): Adding language FOD: Language.Speech~~~$LANG~0.0.1.0 to main OS, index $($IMAGE.ImageIndex)"
Add-WindowsCapability -Name "Language.Speech~~~$LANG~0.0.1.0" -Path $MAIN_OS_MOUNT -Source $FOD_PATH -ErrorAction stop | Out-Null
# Note: If I wanted to enable additional Features on Demand, I'd add these here.
# Add latest cumulative update
Write-Output "$(Get-TS): Adding package $LCU_PATH to main OS, index $($IMAGE.ImageIndex)"
Add-WindowsPackage -Path $MAIN_OS_MOUNT -PackagePath $LCU_PATH -ErrorAction stop | Out-Null
# Perform image cleanup
Write-Output "$(Get-TS): Performing image cleanup on main OS, index $($IMAGE.ImageIndex)"
DISM /image:$MAIN_OS_MOUNT /cleanup-image /StartComponentCleanup | Out-Null
#
# Note: If I wanted to enable additional Optional Components, I'd add these here.
# In addition, we'll add .NET 3.5 here as well. Both .NET and Optional Components might require
# the image to be booted, and thus if we tried to cleanup after installation, it would fail.
#
Write-Output "$(Get-TS): Adding NetFX3~~~~ to main OS, index $($IMAGE.ImageIndex)"
Add-WindowsCapability -Name "NetFX3~~~~" -Path $MAIN_OS_MOUNT -Source $FOD_PATH -ErrorAction stop | Out-Null
# Add .NET Cumulative Update
Write-Output "$(Get-TS): Adding package $DOTNET_CU_PATH to main OS, index $($IMAGE.ImageIndex)"
Add-WindowsPackage -Path $MAIN_OS_MOUNT -PackagePath $DOTNET_CU_PATH -ErrorAction stop | Out-Null
# Dismount
Dismount-WindowsImage -Path $MAIN_OS_MOUNT -Save -ErrorAction stop | Out-Null
# Export
Write-Output "$(Get-TS): Exporting image to $WORKING_PATH\install2.wim"
Export-WindowsImage -SourceImagePath $MEDIA_NEW_PATH"\sources\install.wim" -SourceIndex $IMAGE.ImageIndex -DestinationImagePath $WORKING_PATH"\install2.wim" -ErrorAction stop | Out-Null
}
Move-Item -Path $WORKING_PATH"\install2.wim" -Destination $MEDIA_NEW_PATH"\sources\install.wim" -Force -ErrorAction stop | Out-Null
更新 WinPE
此腳本類似於更新 WinRE 的腳本,但會改為掛接 Boot.wim、最後套用具有最新累積更新的套件,然後儲存。 它會針對 Boot.wim 內的所有映像重複此動作,通常是兩個映像。 它一開始會套用服務堆疊動態更新。 因為腳本是使用日文自定義此媒體,所以會從語言套件 ISO 上的 WinPE 資料夾安裝語言套件。 此外,它會將字型支援和文字新增至語音 (TTS) 支援。 由於腳本正在新增語言,因此會重建 lang.ini,用來識別映像中安裝的語言。 針對第二個映像,我們將儲存 setup.exe 和 setuphost.exe 以供稍後使用,以確保這些版本符合安裝媒體的 \sources\setup.exe 和 \sources\setuphost.exe 版本。 如果這些二進位檔不相同,Windows 安裝程式會在安裝期間失敗。 我們也會儲存服務開機管理員檔案,以供稍後在腳本中使用。 最後,腳本會清除並匯出 Boot.wim,並將其複製回新媒體。
#
# update Windows Preinstallation Environment (WinPE)
#
# Get the list of images contained within WinPE
$WINPE_IMAGES = Get-WindowsImage -ImagePath $MEDIA_NEW_PATH"\sources\boot.wim"
Foreach ($IMAGE in $WINPE_IMAGES) {
# update WinPE
Write-Output "$(Get-TS): Mounting WinPE, image index $($IMAGE.ImageIndex)"
Mount-WindowsImage -ImagePath $MEDIA_NEW_PATH"\sources\boot.wim" -Index $IMAGE.ImageIndex -Path $WINPE_MOUNT -ErrorAction stop | Out-Null
# Add servicing stack update (Step 9 from the table)
# Depending on the Windows release that you are updating, there are 2 different approaches for updating the servicing stack
# The first approach is to use the combined cumulative update. This is for Windows releases that are shipping a combined
# cumulative update that includes the servicing stack updates (i.e. SSU + LCU are combined). Windows 11, version 21H2 and
# Windows 11, version 22H2 are examples. In these cases, the servicing stack update is not published separately; the combined
# cumulative update should be used for this step. However, in hopefully rare cases, there may breaking change in the combined
# cumulative update format, that requires a standalone servicing stack update to be published, and installed first before the
# combined cumulative update can be installed.
# This is the code to handle the rare case that the SSU is published and required for the combined cumulative update
# Write-Output "$(Get-TS): Adding package $SSU_PATH"
# Add-WindowsPackage -Path $WINPE_MOUNT -PackagePath $SSU_PATH | Out-Null
# Now, attempt the combined cumulative update.
# There is a known issue where the servicing stack update is installed, but the cumulative update will fail.
# This error should be caught and ignored, as the last step will be to apply the cumulative update
# (or in this case the combined cumulative update) and thus the image will be left with the correct packages installed.
try
{
Write-Output "$(Get-TS): Adding package $LCU_PATH to WinPE, image index $($IMAGE.ImageIndex)"
Add-WindowsPackage -Path $WINPE_MOUNT -PackagePath $LCU_PATH | Out-Null
}
Catch
{
$theError = $_
Write-Output "$(Get-TS): $theError"
if ($theError.Exception -like "*0x8007007e*") {
Write-Output "$(Get-TS): This failure is a known issue with combined cumulative update, we can ignore."
}
else {
throw
}
}
# The second approach for Step 9 is for Windows releases that have not adopted the combined cumulative update
# but instead continue to have a separate servicing stack update published. In this case, we'll install the SSU
# update. This second approach is commented out below.
# Write-Output "$(Get-TS): Adding package $SSU_PATH"
# Add-WindowsPackage -Path $WINPE_MOUNT -PackagePath $SSU_PATH | Out-Null
# Install lp.cab cab
Write-Output "$(Get-TS): Adding package $WINPE_OC_LP_PATH to WinPE, image index $($IMAGE.ImageIndex)"
Add-WindowsPackage -Path $WINPE_MOUNT -PackagePath $WINPE_OC_LP_PATH -ErrorAction stop | Out-Null
# Install language cabs for each optional package installed
$WINPE_INSTALLED_OC = Get-WindowsPackage -Path $WINPE_MOUNT
Foreach ($PACKAGE in $WINPE_INSTALLED_OC) {
if ( ($PACKAGE.PackageState -eq "Installed") -and ($PACKAGE.PackageName.startsWith("WinPE-")) -and ($PACKAGE.ReleaseType -eq "FeaturePack") ) {
$INDEX = $PACKAGE.PackageName.IndexOf("-Package")
if ($INDEX -ge 0) {
$OC_CAB = $PACKAGE.PackageName.Substring(0, $INDEX) + "_" + $LANG + ".cab"
if ($WINPE_OC_LANG_CABS.Contains($OC_CAB)) {
$OC_CAB_PATH = Join-Path $WINPE_OC_LANG_PATH $OC_CAB
Write-Output "$(Get-TS): Adding package $OC_CAB_PATH to WinPE, image index $($IMAGE.ImageIndex)"
Add-WindowsPackage -Path $WINPE_MOUNT -PackagePath $OC_CAB_PATH -ErrorAction stop | Out-Null
}
}
}
}
# Add font support for the new language
if ( (Test-Path -Path $WINPE_FONT_SUPPORT_PATH) ) {
Write-Output "$(Get-TS): Adding package $WINPE_FONT_SUPPORT_PATH to WinPE, image index $($IMAGE.ImageIndex)"
Add-WindowsPackage -Path $WINPE_MOUNT -PackagePath $WINPE_FONT_SUPPORT_PATH -ErrorAction stop | Out-Null
}
# Add TTS support for the new language
if (Test-Path -Path $WINPE_SPEECH_TTS_PATH) {
if ( (Test-Path -Path $WINPE_SPEECH_TTS_LANG_PATH) ) {
Write-Output "$(Get-TS): Adding package $WINPE_SPEECH_TTS_PATH to WinPE, image index $($IMAGE.ImageIndex)"
Add-WindowsPackage -Path $WINPE_MOUNT -PackagePath $WINPE_SPEECH_TTS_PATH -ErrorAction stop | Out-Null
Write-Output "$(Get-TS): Adding package $WINPE_SPEECH_TTS_LANG_PATH to WinPE, image index $($IMAGE.ImageIndex)"
Add-WindowsPackage -Path $WINPE_MOUNT -PackagePath $WINPE_SPEECH_TTS_LANG_PATH -ErrorAction stop | Out-Null
}
}
# Generates a new Lang.ini file which is used to define the language packs inside the image
if ( (Test-Path -Path $WINPE_MOUNT"\sources\lang.ini") ) {
Write-Output "$(Get-TS): Updating lang.ini"
DISM /image:$WINPE_MOUNT /Gen-LangINI /distribution:$WINPE_MOUNT | Out-Null
}
# Add latest cumulative update
Write-Output "$(Get-TS): Adding package $LCU_PATH to WinPE, image index $($IMAGE.ImageIndex)"
Add-WindowsPackage -Path $WINPE_MOUNT -PackagePath $LCU_PATH -ErrorAction stop | Out-Null
# Perform image cleanup
Write-Output "$(Get-TS): Performing image cleanup on WinPE, image index $($IMAGE.ImageIndex)"
DISM /image:$WINPE_MOUNT /cleanup-image /StartComponentCleanup /ResetBase /Defer | Out-Null
if ($IMAGE.ImageIndex -eq "2") {
# Save setup.exe for later use. This will address possible binary mismatch with the version in the main OS \sources folder
Copy-Item -Path $WINPE_MOUNT"\sources\setup.exe" -Destination $WORKING_PATH"\setup.exe" -Force -ErrorAction stop | Out-Null
# Save setuphost.exe for later use. This will address possible binary mismatch with the version in the main OS \sources folder
# This is only required starting with Windows 11 version 24H2
$TEMP = Get-WindowsImage -ImagePath $MEDIA_NEW_PATH"\sources\boot.wim" -Index $IMAGE.ImageIndex
if ([System.Version]$TEMP.Version -ge [System.Version]"10.0.26100") {
Copy-Item -Path $WINPE_MOUNT"\sources\setuphost.exe" -Destination $WORKING_PATH"\setuphost.exe" -Force -ErrorAction stop | Out-Null
}
else {
Write-Output "$(Get-TS): Skipping copy of setuphost.exe; image version $($TEMP.Version)"
}
# Save serviced boot manager files later copy to the root media.
Copy-Item -Path $WINPE_MOUNT"\Windows\boot\efi\bootmgfw.efi" -Destination $WORKING_PATH"\bootmgfw.efi" -Force -ErrorAction stop | Out-Null
Copy-Item -Path $WINPE_MOUNT"\Windows\boot\efi\bootmgr.efi" -Destination $WORKING_PATH"\bootmgr.efi" -Force -ErrorAction stop | Out-Null
}
# Dismount
Dismount-WindowsImage -Path $WINPE_MOUNT -Save -ErrorAction stop | Out-Null
#Export WinPE
Write-Output "$(Get-TS): Exporting image to $WORKING_PATH\boot2.wim"
Export-WindowsImage -SourceImagePath $MEDIA_NEW_PATH"\sources\boot.wim" -SourceIndex $IMAGE.ImageIndex -DestinationImagePath $WORKING_PATH"\boot2.wim" -ErrorAction stop | Out-Null
}
Move-Item -Path $WORKING_PATH"\boot2.wim" -Destination $MEDIA_NEW_PATH"\sources\boot.wim" -Force -ErrorAction stop | Out-Null
更新剩餘的媒體檔案
腳本的這個部分會更新安裝程序檔案。 它只會將安裝程序動態更新套件中的個別檔案複製到新媒體。 此步驟會視需要帶入更新的安裝程式檔案,以及最新的相容性資料庫和取代元件指令清單。 此腳本也會使用先前從 WinPE 儲存的版本,最終取代 setup.exe、setuphost.exe 和開機管理員檔案。
#
# update remaining files on media
#
# Add Setup DU by copy the files from the package into the newMedia
Write-Output "$(Get-TS): Adding package $SETUP_DU_PATH"
cmd.exe /c $env:SystemRoot\System32\expand.exe $SETUP_DU_PATH -F:* $MEDIA_NEW_PATH"\sources" | Out-Null
# Copy setup.exe from boot.wim, saved earlier.
Write-Output "$(Get-TS): Copying $WORKING_PATH\setup.exe to $MEDIA_NEW_PATH\sources\setup.exe"
Copy-Item -Path $WORKING_PATH"\setup.exe" -Destination $MEDIA_NEW_PATH"\sources\setup.exe" -Force -ErrorAction stop | Out-Null
# Copy setuphost.exe from boot.wim, saved earlier.
if (Test-Path -Path $WORKING_PATH"\setuphost.exe") {
Write-Output "$(Get-TS): Copying $WORKING_PATH\setuphost.exe to $MEDIA_NEW_PATH\sources\setuphost.exe"
Copy-Item -Path $WORKING_PATH"\setuphost.exe" -Destination $MEDIA_NEW_PATH"\sources\setuphost.exe" -Force -ErrorAction stop | Out-Null
}
# Copy bootmgr files from boot.wim, saved earlier.
$MEDIA_NEW_FILES = Get-ChildItem $MEDIA_NEW_PATH -Force -Recurse -Filter b*.efi
Foreach ($File in $MEDIA_NEW_FILES){
if (($File.Name -ieq "bootmgfw.efi") -or ($File.Name -ieq "bootx64.efi") -or ($File.Name -ieq "bootia32.efi") -or ($File.Name -ieq "bootaa64.efi"))
{
Write-Output "$(Get-TS): Copying $WORKING_PATH\bootmgfw.efi to $($File.FullName)"
Copy-Item -Path $WORKING_PATH"\bootmgfw.efi" -Destination $File.FullName -Force -ErrorAction stop | Out-Null
}
elseif ($File.Name -ieq "bootmgr.efi")
{
Write-Output "$(Get-TS): Copying $WORKING_PATH\bootmgr.efi to $($File.FullName)"
Copy-Item -Path $WORKING_PATH"\bootmgr.efi" -Destination $File.FullName -Force -ErrorAction stop | Out-Null
}
}
完成
在最後一個步驟中,腳本會移除暫存盤的工作資料夾,並卸除我們的語言套件和功能隨選 ISO。
#
# Perform final cleanup
#
# Remove our working folder
Remove-Item -Path $WORKING_PATH -Recurse -Force -ErrorAction stop | Out-Null
# Dismount ISO images
Write-Output "$(Get-TS): Dismounting ISO images"
Dismount-DiskImage -ImagePath $FOD_ISO_PATH -ErrorAction stop | Out-Null
Write-Output "$(Get-TS): Media refresh completed!"