Update media-dynamic-update.md

Due to changes in how optional components are implemented, we are moving the installation of these before the final LCU install. This means cleanup may fail with a warning. Further, other changes include:

1)	Moving some script comments into the main article, to improve readability. Most of this is related to the old approach where SSU was a separate update.
2)	Adding Optional Components (or Legacy Features) to the script. This help ensure they are showcased before the LCU install.
3)	Tweaked the main table of steps, to highlight SSU is coming from LCU, and the sequence change with main OS cleanup.
This commit is contained in:
Steve DiAcetis (MSFT)
2025-01-27 11:12:40 -08:00
parent dbf0f8b83b
commit 3a5da118ae

View File

@ -13,7 +13,7 @@ appliesto:
-<a href=https://learn.microsoft.com/windows/release-health/supported-versions-windows-client target=_blank>Windows 11</a> -<a href=https://learn.microsoft.com/windows/release-health/supported-versions-windows-client target=_blank>Windows 11</a>
-<a href=https://learn.microsoft.com/windows/release-health/supported-versions-windows-client target=_blank>Windows 10</a> -<a href=https://learn.microsoft.com/windows/release-health/supported-versions-windows-client target=_blank>Windows 10</a>
-<a href=https://learn.microsoft.com/windows/release-health/windows-server-release-info target=_blank>Windows Server</a> -<a href=https://learn.microsoft.com/windows/release-health/windows-server-release-info target=_blank>Windows Server</a>
ms.date: 11/11/2024 ms.date: 1/28/2024
--- ---
# Update Windows installation media with Dynamic Update # Update Windows installation media with Dynamic Update
@ -124,27 +124,27 @@ Properly updating the installation media involves many actions operating on seve
This table shows the correct sequence for applying the various tasks to the files. For example, the full sequence starts with adding the servicing stack update to WinRE (1) and concludes with adding boot manager from WinPE to the new media (28). This table shows the correct sequence for applying the various tasks to the files. For example, the full sequence starts with adding the servicing stack update to WinRE (1) and concludes with adding boot manager from WinPE to the new media (28).
|Task |WinRE (winre.wim) |Operating system (install.wim) | WinPE (boot.wim) | New media | |Task |WinRE (winre.wim) |Operating system (install.wim) | WinPE (boot.wim) | New media |
|-------------------------------------------|-------------------|--------------------------------|------------------|-----------| |--------------------------------------------------------|-------------------|--------------------------------|------------------|-----------|
|Add servicing stack Dynamic Update | 1 | 9 | 17 | | |Add servicing stack update via latest cumulative update | 1 | 9 | 17 | |
|Add language pack | 2 | 10 | 18 | | |Add language pack | 2 | 10 | 18 | |
|Add localized optional packages | 3 | | 19 | | |Add localized optional packages | 3 | | 19 | |
|Add font support | 4 | | 20 | | |Add font support | 4 | | 20 | |
|Add text-to-speech | 5 | | 21 | | |Add text-to-speech | 5 | | 21 | |
|Update Lang.ini | | | 22 | | |Update Lang.ini | | | 22 | |
|Add Features on Demand | | 11 | | | |Add Features on Demand | | 11 | | |
|Add Safe OS Dynamic Update | 6 | | | | |Add Optional Components | | 12 | | |
|Add Setup Dynamic Update | | | | 26 | |Add Safe OS Dynamic Update | 6 | | | |
|Add setup.exe and setuphost.exe from WinPE | | | | 27 | |Add Setup Dynamic Update | | | | 26 |
|Add boot manager from WinPE | | | | 28 | |Add setup.exe and setuphost.exe from WinPE | | | | 27 |
|Add latest cumulative update | | 12 | 23 | | |Add boot manager from WinPE | | | | 28 |
|Clean up the image | 7 | 13 | 24 | | |Add latest cumulative update | | 13 | 23 | |
|Add Optional Components | | 14 | | | |Clean up the image | 7 | 14 | 24 | |
|Add .NET and .NET cumulative updates | | 15 | | | |Add .NET and .NET cumulative updates | | 15 | | |
|Export image | 8 | 16 | 25 | | |Export image | 8 | 16 | 25 | |
> [!NOTE] > [!NOTE]
> Starting in February 2021, the latest cumulative update and servicing stack update will be combined and distributed in the Microsoft Update Catalog as a new combined cumulative update. For Steps 1, 9, and 18 that require the servicing stack update for updating the installation media, you should use the combined cumulative update. For more information on the combined cumulative update, see [Servicing stack updates](./servicing-stack-updates.md). > Starting in February 2021, the latest cumulative update and servicing stack update is combined and distributed in the Microsoft Update Catalog as a new combined cumulative update. For Steps 1, 9, and 17 that require the servicing stack update for updating the installation media, you should use the combined cumulative update. For more information on the combined cumulative update, see [Servicing stack updates](./servicing-stack-updates.md).
> [!NOTE] > [!NOTE]
> Microsoft will remove the Flash component from Windows through KB4577586, "Update for Removal of Adobe Flash Player". You can also remove Flash anytime by deploying the update in KB4577586 (available on the Catalog) between steps 20 and 21. As of July 2021, KB4577586, "Update for Removal of Adobe Flash Player" will be included in the latest cumulative update for Windows 10, versions 1607 and 1507. The update will also be included in the Monthly Rollup and the Security Only Update for Windows 8.1, Windows Server 2012, and Windows Embedded 8 Standard. For more information, see [Update on Adobe Flash Player End of Support](https://blogs.windows.com/msedgedev/2020/09/04/update-adobe-flash-end-support/). > Microsoft will remove the Flash component from Windows through KB4577586, "Update for Removal of Adobe Flash Player". You can also remove Flash anytime by deploying the update in KB4577586 (available on the Catalog) between steps 20 and 21. As of July 2021, KB4577586, "Update for Removal of Adobe Flash Player" will be included in the latest cumulative update for Windows 10, versions 1607 and 1507. The update will also be included in the Monthly Rollup and the Security Only Update for Windows 8.1, Windows Server 2012, and Windows Embedded 8 Standard. For more information, see [Update on Adobe Flash Player End of Support](https://blogs.windows.com/msedgedev/2020/09/04/update-adobe-flash-end-support/).
@ -178,7 +178,8 @@ These examples are for illustration only, and therefore lack error handling. The
### Get started ### Get started
The script starts by declaring global variables and creating folders to use for mounting images. Then, make a copy of the original media, from \oldMedia to \newMedia, keeping the original media in case there's a script error and it's necessary to start over from a known state. Also, it provides a comparison of old versus new media to evaluate changes. To ensure that the new media updates, make sure they aren't read-only. The script starts by declaring global variables and creating folders to use for mounting images. Then, make a copy of the original media, from \oldMedia to \newMedia, keeping the original media in case there's a script error and it's necessary to start over from a known state. Also, it provides a comparison of old versus new media to evaluate changes. To ensure that the new media updates, make sure they aren't read-only. The script also showcases adding additional languages, Features on Demand, and Optional Components. These are not required, but added to highlight when in the sequence they should be addeed. 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. Further, the path for main OS language and optional features moved to \LanguagesAndOptionalFeatures instead of the root. If you are using this script for Windows 10, modify to mount and use the LANGPACK ISO.
```powershell ```powershell
#Requires -RunAsAdministrator #Requires -RunAsAdministrator
@ -187,40 +188,38 @@ function Get-TS { return "{0:HH:mm:ss}" -f [DateTime]::Now }
Write-Output "$(Get-TS): Starting media refresh" 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 # Declare Dynamic Update packages. A dedicated folder is used for the latest cumulative update, and as needed
# checkpoint cumulative updates. # checkpoint cumulative updates.
$LCU_PATH = "C:\mediaRefresh\packages\CU\LCU.msu" $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" $SETUP_DU_PATH = "C:\mediaRefresh\packages\Other\Setup_DU.cab"
$SAFE_OS_DU_PATH = "C:\mediaRefresh\packages\Other\SafeOS_DU.cab" $SAFE_OS_DU_PATH = "C:\mediaRefresh\packages\Other\SafeOS_DU.cab"
$DOTNET_CU_PATH = "C:\mediaRefresh\packages\Other\DotNet_CU.msu" $DOTNET_CU_PATH = "C:\mediaRefresh\packages\Other\DotNet_CU.msu"
# Declare folders for mounted images and temp files # Declare media for FOD and LPs
$MEDIA_OLD_PATH = "C:\mediaRefresh\oldMedia" $FOD_ISO_PATH = "C:\mediaRefresh\packages\CLIENT_LOF_PACKAGES_OEM.iso"
$MEDIA_NEW_PATH = "C:\mediaRefresh\newMedia"
$WORKING_PATH = "C:\mediaRefresh\temp" # Array of Features On Demand for main OS
$MAIN_OS_MOUNT = "C:\mediaRefresh\temp\MainOSMount" # This is optional to showcase where these are added
$WINRE_MOUNT = "C:\mediaRefresh\temp\WinREMount" $FOD = @(
$WINPE_MOUNT = "C:\mediaRefresh\temp\WinPEMount" 'XPS.Viewer~~~~0.0.1.0'
)
# Array of Legacy Features for main OS
# This is optional to showcase where these are added
$OC = @(
'MediaPlayback'
'WindowsMediaPlayer'
)
# Mount the Features on Demand ISO # Mount the Features on Demand ISO
Write-Output "$(Get-TS): Mounting FOD ISO" Write-Output "$(Get-TS): Mounting FOD ISO"
$FOD_ISO_DRIVE_LETTER = (Mount-DiskImage -ImagePath $FOD_ISO_PATH -ErrorAction stop | Get-Volume).DriveLetter $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" $FOD_PATH = $FOD_ISO_DRIVE_LETTER + ":\LanguagesAndOptionalFeatures"
# Declare language for showcasing adding optional localized components
$LANG = "ja-jp"
$LANG_FONT_CAPABILITY = "jpan"
# Declare language related cabs # Declare language related cabs
$WINPE_OC_PATH = "$FOD_ISO_DRIVE_LETTER`:\Windows Preinstallation Environment\x64\WinPE_OCs" $WINPE_OC_PATH = "$FOD_ISO_DRIVE_LETTER`:\Windows Preinstallation Environment\x64\WinPE_OCs"
$WINPE_OC_LANG_PATH = "$WINPE_OC_PATH\$LANG" $WINPE_OC_LANG_PATH = "$WINPE_OC_PATH\$LANG"
@ -231,6 +230,14 @@ $WINPE_SPEECH_TTS_PATH = "$WINPE_OC_PATH\WinPE-Speech-TTS.cab"
$WINPE_SPEECH_TTS_LANG_PATH = "$WINPE_OC_PATH\WinPE-Speech-TTS-$LANG.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" $OS_LP_PATH = "$FOD_PATH\Microsoft-Windows-Client-Language-Pack_x64_$LANG.cab"
# Declare folders for mounted images and temp files
$MEDIA_OLD_PATH = "C:\mediaRefresh\oldMedia\Ge\client_professional_en-us"
$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"
# Create folders for mounting images and storing temporary files # 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 $WORKING_PATH -ErrorAction Stop | Out-Null
New-Item -ItemType directory -Path $MAIN_OS_MOUNT -ErrorAction stop | Out-Null New-Item -ItemType directory -Path $MAIN_OS_MOUNT -ErrorAction stop | Out-Null
@ -241,15 +248,16 @@ New-Item -ItemType directory -Path $WINPE_MOUNT -ErrorAction stop | Out-Null
Write-Output "$(Get-TS): Copying original media to new media path" 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 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 } Get-ChildItem -Path $MEDIA_NEW_PATH -Recurse | Where-Object { -not $_.PSIsContainer -and $_.IsReadOnly } | ForEach-Object { $_.IsReadOnly = $false }
``` ```
### Update WinRE and each main OS Windows edition ### Update WinRE and each main OS Windows edition
The script will update each edition of Windows within the main operating system file (install.wim). For each edition, the main OS image is mounted. The script will update each edition of Windows within the main operating system file (install.wim). For each edition, the main OS image is mounted.
For the first image, Winre.wim is copied to the working folder, and mounted. It then applies servicing stack Dynamic Update, since its components are used for updating other components. Since the script is optionally adding Japanese, it adds the language pack to the image, and installs the Japanese versions of all optional packages already installed in Winre.wim. Then, it applies the Safe OS Dynamic Update package. It finishes by cleaning and exporting the image to reduce the image size. For the first image, Winre.wim is copied to the working folder, and mounted. It then applies servicing stack servicing stack via the latest cumulative update, since its components are used for updating other components. Depending on the Windows release that you are updating, there are two 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 change, that requires a standalone servicing stack update to be published, and installed first before the combined cumulative update can be installed. Since the script is optionally adding Japanese, it adds the language pack to the image, and installs the Japanese versions of all optional packages already installed in Winre.wim. Then, it applies the Safe OS Dynamic Update package. It finishes by cleaning and exporting the image to reduce the image size.
Next, for the mounted OS image, the script starts by applying the servicing stack Dynamic Update. Then, it adds Japanese language support and then the Japanese language features. Unlike the Dynamic Update packages, it uses `Add-WindowsCapability` to add these features. For a full list of such features, and their associated capability name, see [Available Features on Demand](/windows-hardware/manufacture/desktop/features-on-demand-non-language-fod). Now is the time to enable other Optional Components or add other Features on Demand. If such a feature has an associated cumulative update (for example, .NET), this is the time to apply those. The script then proceeds with applying the latest cumulative update. Finally, the script cleans and exports the image. You can install Optional Components, along with the .NET feature, offline, but that requires the device to be restarted. This is why the script installs .NET and Optional Components after cleanup and before export. Next, for the mounted OS image, the script starts by applying the servicing stack via the latest cumulative update. Then, it adds Japanese language support and then the Japanese language features. Unlike the Dynamic Update packages, it uses `Add-WindowsCapability` to add these features. For a full list of such features, and their associated capability name, see [Available Features on Demand](/windows-hardware/manufacture/desktop/features-on-demand-non-language-fod). Now is the time to enable other Optional Components or add other Features on Demand. If such a feature has an associated cumulative update (for example, .NET), this is the time to apply those. The script then attempts to clean the image, then a final step to apply the latest cumulative update. It is important to apply the latest cumulative update last, to ensure Features on Demand, Optional Components and Languages are updated from their initial release state. The .NET feature is an exception, that is added along with its cumulative update next. Finally, the script exports the image.
This process is repeated for each edition of Windows within the main operating system file. To reduce size, the serviced Winre.wim file from the first image is saved, and used to update each subsequent Windows edition. This reduces the final size of install.wim. This process is repeated for each edition of Windows within the main operating system file. To reduce size, the serviced Winre.wim file from the first image is saved, and used to update each subsequent Windows edition. This reduces the final size of install.wim.
@ -262,13 +270,15 @@ This process is repeated for each edition of Windows within the main operating s
# Get the list of images contained within the main OS # Get the list of images contained within the main OS
$WINOS_IMAGES = Get-WindowsImage -ImagePath $MEDIA_NEW_PATH"\sources\install.wim" $WINOS_IMAGES = Get-WindowsImage -ImagePath $MEDIA_NEW_PATH"\sources\install.wim"
Foreach ($IMAGE in $WINOS_IMAGES) { Foreach ($IMAGE in $WINOS_IMAGES)
{
# first mount the main OS image # first mount the main OS image
Write-Output "$(Get-TS): Mounting main OS, image index $($IMAGE.ImageIndex)" 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 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") { if ($IMAGE.ImageIndex -eq "1")
{
# #
# update Windows Recovery Environment (WinRE) within this OS image # update Windows Recovery Environment (WinRE) within this OS image
@ -278,29 +288,9 @@ Foreach ($IMAGE in $WINOS_IMAGES) {
Mount-WindowsImage -ImagePath $WORKING_PATH"\winre.wim" -Index 1 -Path $WINRE_MOUNT -ErrorAction stop | Out-Null 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) # 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" Write-Output "$(Get-TS): Adding package $LCU_PATH to WinRE"
try try
{ {
Add-WindowsPackage -Path $WINRE_MOUNT -PackagePath $LCU_PATH | Out-Null Add-WindowsPackage -Path $WINRE_MOUNT -PackagePath $LCU_PATH | Out-Null
} }
Catch Catch
@ -308,38 +298,36 @@ Foreach ($IMAGE in $WINOS_IMAGES) {
$theError = $_ $theError = $_
Write-Output "$(Get-TS): $theError" Write-Output "$(Get-TS): $theError"
if ($theError.Exception -like "*0x8007007e*") { if ($theError.Exception -like "*0x8007007e*")
Write-Output "$(Get-TS): This failure is a known issue with combined cumulative update, we can ignore." {
Write-Warning "$(Get-TS): Failed with error 0x8007007e. This failure is a known issue with combined cumulative update, we can ignore."
} }
else { else
{
throw 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 # Optional: Add the language to recovery environment
# #
# Install lp.cab cab # Install lp.cab cab
Write-Output "$(Get-TS): Adding package $WINPE_OC_LP_PATH to WinRE" 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 Add-WindowsPackage -Path $WINRE_MOUNT -PackagePath $WINPE_OC_LP_PATH -ErrorAction stop | Out-Null
# Install language cabs for each optional package installed # Install language cabs for each optional package installed
$WINRE_INSTALLED_OC = Get-WindowsPackage -Path $WINRE_MOUNT $WINRE_INSTALLED_OC = Get-WindowsPackage -Path $WINRE_MOUNT
Foreach ($PACKAGE in $WINRE_INSTALLED_OC) { Foreach ($PACKAGE in $WINRE_INSTALLED_OC)
{
if ( ($PACKAGE.PackageState -eq "Installed") -and ($PACKAGE.PackageName.startsWith("WinPE-")) -and ($PACKAGE.ReleaseType -eq "FeaturePack") ) { if ( ($PACKAGE.PackageState -eq "Installed") -and ($PACKAGE.PackageName.startsWith("WinPE-")) -and ($PACKAGE.ReleaseType -eq "FeaturePack") )
{
$INDEX = $PACKAGE.PackageName.IndexOf("-Package") $INDEX = $PACKAGE.PackageName.IndexOf("-Package")
if ($INDEX -ge 0) { if ($INDEX -ge 0)
{
$OC_CAB = $PACKAGE.PackageName.Substring(0, $INDEX) + "_" + $LANG + ".cab" $OC_CAB = $PACKAGE.PackageName.Substring(0, $INDEX) + "_" + $LANG + ".cab"
if ($WINPE_OC_LANG_CABS.Contains($OC_CAB)) { if ($WINPE_OC_LANG_CABS.Contains($OC_CAB))
{
$OC_CAB_PATH = Join-Path $WINPE_OC_LANG_PATH $OC_CAB $OC_CAB_PATH = Join-Path $WINPE_OC_LANG_PATH $OC_CAB
Write-Output "$(Get-TS): Adding package $OC_CAB_PATH to WinRE" 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-WindowsPackage -Path $WINRE_MOUNT -PackagePath $OC_CAB_PATH -ErrorAction stop | Out-Null
@ -349,15 +337,17 @@ Foreach ($IMAGE in $WINOS_IMAGES) {
} }
# Add font support for the new language # Add font support for the new language
if ( (Test-Path -Path $WINPE_FONT_SUPPORT_PATH) ) { if ( (Test-Path -Path $WINPE_FONT_SUPPORT_PATH) )
{
Write-Output "$(Get-TS): Adding package $WINPE_FONT_SUPPORT_PATH to WinRE" 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-WindowsPackage -Path $WINRE_MOUNT -PackagePath $WINPE_FONT_SUPPORT_PATH -ErrorAction stop | Out-Null
} }
# Add TTS support for the new language # Add TTS support for the new language
if (Test-Path -Path $WINPE_SPEECH_TTS_PATH) { if (Test-Path -Path $WINPE_SPEECH_TTS_PATH)
if ( (Test-Path -Path $WINPE_SPEECH_TTS_LANG_PATH) ) { {
if ( (Test-Path -Path $WINPE_SPEECH_TTS_LANG_PATH) )
{
Write-Output "$(Get-TS): Adding package $WINPE_SPEECH_TTS_PATH to WinRE" 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 Add-WindowsPackage -Path $WINRE_MOUNT -PackagePath $WINPE_SPEECH_TTS_PATH -ErrorAction stop | Out-Null
@ -373,6 +363,10 @@ Foreach ($IMAGE in $WINOS_IMAGES) {
# Perform image cleanup # Perform image cleanup
Write-Output "$(Get-TS): Performing image cleanup on WinRE" Write-Output "$(Get-TS): Performing image cleanup on WinRE"
DISM /image:$WINRE_MOUNT /cleanup-image /StartComponentCleanup /ResetBase /Defer | Out-Null DISM /image:$WINRE_MOUNT /cleanup-image /StartComponentCleanup /ResetBase /Defer | Out-Null
if ($LastExitCode -ne 0)
{
throw "Error: Failed to perform image cleanup on WinRE. Exit code: $LastExitCode"
}
# Dismount # Dismount
Dismount-WindowsImage -Path $WINRE_MOUNT -Save -ErrorAction stop | Out-Null Dismount-WindowsImage -Path $WINRE_MOUNT -Save -ErrorAction stop | Out-Null
@ -389,35 +383,15 @@ Foreach ($IMAGE in $WINOS_IMAGES) {
# update Main OS # update Main OS
# #
# Add servicing stack update (Step 18 from the table) # Add servicing stack update (Step 17 from the table). Unlike WinRE and WinPE, we don't need to check for error 0x8007007e
# 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)" 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 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)" # Optional: Add language to main OS and corresponding language experience Features on Demand
# 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)" 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 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)" 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 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
@ -436,22 +410,47 @@ Foreach ($IMAGE in $WINOS_IMAGES) {
Write-Output "$(Get-TS): Adding language FOD: Language.Speech~~~$LANG~0.0.1.0 to main OS, index $($IMAGE.ImageIndex)" 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 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. # Optional: Add additional Features On Demand
For ( $index = 0; $index -lt $FOD.count; $index++)#
{
Write-Output "$(Get-TS): Adding $($FOD[$index]) to main OS, index $($IMAGE.ImageIndex)"
Add-WindowsCapability -Name $($FOD[$index]) -Path $MAIN_OS_MOUNT -Source $FOD_PATH -ErrorAction stop | Out-Null
}
# Optional: Add Legacy Features
For ( $index = 0; $index -lt $OC.count; $index++)
{
Write-Output "$(Get-TS): Adding $($OC[$index]) to main OS, index $($IMAGE.ImageIndex)"
DISM /Image:$MAIN_OS_MOUNT /Enable-Feature /FeatureName:$($OC[$index]) /All | Out-Null
if ($LastExitCode -ne 0)
{
throw "Error: Failed to add $($OC[$index]) to main OS, index $($IMAGE.ImageIndex). Exit code: $LastExitCode"
}
}
# Add latest cumulative update # Add latest cumulative update
Write-Output "$(Get-TS): Adding package $LCU_PATH to main OS, index $($IMAGE.ImageIndex)" 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 Add-WindowsPackage -Path $MAIN_OS_MOUNT -PackagePath $LCU_PATH -ErrorAction stop | Out-Null
# Perform image cleanup # Perform image cleanup. Some Optional Components might require the image to be booted, and thus
# image cleanup may fail. We'll catch and handle as a warning.
Write-Output "$(Get-TS): Performing image cleanup on main OS, index $($IMAGE.ImageIndex)" Write-Output "$(Get-TS): Performing image cleanup on main OS, index $($IMAGE.ImageIndex)"
DISM /image:$MAIN_OS_MOUNT /cleanup-image /StartComponentCleanup | Out-Null DISM /image:$MAIN_OS_MOUNT /cleanup-image /StartComponentCleanup | Out-Null
if ($LastExitCode -ne 0)
{
if ($LastExitCode -eq -2146498554)
{
# We hit 0x800F0806 CBS_E_PENDING. We will ignore this with a warning
# This is likely due to legacy components being added that require online operations.
Write-Warning "$(Get-TS): Failed to perform image cleanup on main OS, index $($IMAGE.ImageIndex). Exit code: $LastExitCode. The operation cannot be performed until pending servicing operations are completed. The image must be booted to complete the pending servicing operation."
}
else
{
throw "Error: Failed to perform image cleanup on main OS, index $($IMAGE.ImageIndex). Exit code: $LastExitCode"
}
}
# # Finally, we'll add .NET 3.5 and the .NET cumulative update
# 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)" 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-WindowsCapability -Name "NetFX3~~~~" -Path $MAIN_OS_MOUNT -Source $FOD_PATH -ErrorAction stop | Out-Null
@ -465,7 +464,6 @@ Foreach ($IMAGE in $WINOS_IMAGES) {
# Export # Export
Write-Output "$(Get-TS): Exporting image to $WORKING_PATH\install2.wim" 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 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 Move-Item -Path $WORKING_PATH"\install2.wim" -Destination $MEDIA_NEW_PATH"\sources\install.wim" -Force -ErrorAction stop | Out-Null
@ -484,31 +482,14 @@ This script is similar to the one that updates WinRE, but instead it mounts Boot
# Get the list of images contained within WinPE # Get the list of images contained within WinPE
$WINPE_IMAGES = Get-WindowsImage -ImagePath $MEDIA_NEW_PATH"\sources\boot.wim" $WINPE_IMAGES = Get-WindowsImage -ImagePath $MEDIA_NEW_PATH"\sources\boot.wim"
Foreach ($IMAGE in $WINPE_IMAGES) { Foreach ($IMAGE in $WINPE_IMAGES)
{
# update WinPE # update WinPE
Write-Output "$(Get-TS): Mounting WinPE, image index $($IMAGE.ImageIndex)" 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 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) # 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 try
{ {
Write-Output "$(Get-TS): Adding package $LCU_PATH to WinPE, image index $($IMAGE.ImageIndex)" Write-Output "$(Get-TS): Adding package $LCU_PATH to WinPE, image index $($IMAGE.ImageIndex)"
@ -518,38 +499,34 @@ Foreach ($IMAGE in $WINPE_IMAGES) {
{ {
$theError = $_ $theError = $_
Write-Output "$(Get-TS): $theError" Write-Output "$(Get-TS): $theError"
if ($theError.Exception -like "*0x8007007e*")
if ($theError.Exception -like "*0x8007007e*") { {
Write-Output "$(Get-TS): This failure is a known issue with combined cumulative update, we can ignore." Write-Warning "$(Get-TS): Failed with error 0x8007007e. This failure is a known issue with combined cumulative update, we can ignore."
} }
else { else
{
throw 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 # Install lp.cab cab
Write-Output "$(Get-TS): Adding package $WINPE_OC_LP_PATH to WinPE, image index $($IMAGE.ImageIndex)" 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 Add-WindowsPackage -Path $WINPE_MOUNT -PackagePath $WINPE_OC_LP_PATH -ErrorAction stop | Out-Null
# Install language cabs for each optional package installed # Install language cabs for each optional package installed
$WINPE_INSTALLED_OC = Get-WindowsPackage -Path $WINPE_MOUNT $WINPE_INSTALLED_OC = Get-WindowsPackage -Path $WINPE_MOUNT
Foreach ($PACKAGE in $WINPE_INSTALLED_OC) { Foreach ($PACKAGE in $WINPE_INSTALLED_OC)
{
if ( ($PACKAGE.PackageState -eq "Installed") -and ($PACKAGE.PackageName.startsWith("WinPE-")) -and ($PACKAGE.ReleaseType -eq "FeaturePack") ) { if ( ($PACKAGE.PackageState -eq "Installed") -and ($PACKAGE.PackageName.startsWith("WinPE-")) -and ($PACKAGE.ReleaseType -eq "FeaturePack") )
{
$INDEX = $PACKAGE.PackageName.IndexOf("-Package") $INDEX = $PACKAGE.PackageName.IndexOf("-Package")
if ($INDEX -ge 0) { if ($INDEX -ge 0)
{
$OC_CAB = $PACKAGE.PackageName.Substring(0, $INDEX) + "_" + $LANG + ".cab" $OC_CAB = $PACKAGE.PackageName.Substring(0, $INDEX) + "_" + $LANG + ".cab"
if ($WINPE_OC_LANG_CABS.Contains($OC_CAB)) { if ($WINPE_OC_LANG_CABS.Contains($OC_CAB))
{
$OC_CAB_PATH = Join-Path $WINPE_OC_LANG_PATH $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)" 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-WindowsPackage -Path $WINPE_MOUNT -PackagePath $OC_CAB_PATH -ErrorAction stop | Out-Null
} }
@ -558,15 +535,17 @@ Foreach ($IMAGE in $WINPE_IMAGES) {
} }
# Add font support for the new language # Add font support for the new language
if ( (Test-Path -Path $WINPE_FONT_SUPPORT_PATH) ) { if ( (Test-Path -Path $WINPE_FONT_SUPPORT_PATH) )
{
Write-Output "$(Get-TS): Adding package $WINPE_FONT_SUPPORT_PATH to WinPE, image index $($IMAGE.ImageIndex)" 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-WindowsPackage -Path $WINPE_MOUNT -PackagePath $WINPE_FONT_SUPPORT_PATH -ErrorAction stop | Out-Null
} }
# Add TTS support for the new language # Add TTS support for the new language
if (Test-Path -Path $WINPE_SPEECH_TTS_PATH) { if (Test-Path -Path $WINPE_SPEECH_TTS_PATH)
if ( (Test-Path -Path $WINPE_SPEECH_TTS_LANG_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)" 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 Add-WindowsPackage -Path $WINPE_MOUNT -PackagePath $WINPE_SPEECH_TTS_PATH -ErrorAction stop | Out-Null
@ -576,9 +555,14 @@ Foreach ($IMAGE in $WINPE_IMAGES) {
} }
# Generates a new Lang.ini file which is used to define the language packs inside the image # 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") ) { if ( (Test-Path -Path $WINPE_MOUNT"\sources\lang.ini") )
{
Write-Output "$(Get-TS): Updating lang.ini" Write-Output "$(Get-TS): Updating lang.ini"
DISM /image:$WINPE_MOUNT /Gen-LangINI /distribution:$WINPE_MOUNT | Out-Null DISM /image:$WINPE_MOUNT /Gen-LangINI /distribution:$WINPE_MOUNT | Out-Null
if ($LastExitCode -ne 0)
{
throw "Error: Failed to update lang.ini. Exit code: $LastExitCode"
}
} }
# Add latest cumulative update # Add latest cumulative update
@ -588,28 +572,31 @@ Foreach ($IMAGE in $WINPE_IMAGES) {
# Perform image cleanup # Perform image cleanup
Write-Output "$(Get-TS): Performing image cleanup on WinPE, image index $($IMAGE.ImageIndex)" Write-Output "$(Get-TS): Performing image cleanup on WinPE, image index $($IMAGE.ImageIndex)"
DISM /image:$WINPE_MOUNT /cleanup-image /StartComponentCleanup /ResetBase /Defer | Out-Null DISM /image:$WINPE_MOUNT /cleanup-image /StartComponentCleanup /ResetBase /Defer | Out-Null
if ($LastExitCode -ne 0)
{
throw "Error: Failed to perform image cleanup on WinPE, image index $($IMAGE.ImageIndex). Exit code: $LastExitCode"
}
if ($IMAGE.ImageIndex -eq "2") { 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 # 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 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 # 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 # This is only required starting with Windows 11 version 24H2
$TEMP = Get-WindowsImage -ImagePath $MEDIA_NEW_PATH"\sources\boot.wim" -Index $IMAGE.ImageIndex $TEMP = Get-WindowsImage -ImagePath $MEDIA_NEW_PATH"\sources\boot.wim" -Index $IMAGE.ImageIndex
if ([System.Version]$TEMP.Version -ge [System.Version]"10.0.26100") { 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 Copy-Item -Path $WINPE_MOUNT"\sources\setuphost.exe" -Destination $WORKING_PATH"\setuphost.exe" -Force -ErrorAction stop | Out-Null
} }
else { else
{
Write-Output "$(Get-TS): Skipping copy of setuphost.exe; image version $($TEMP.Version)" Write-Output "$(Get-TS): Skipping copy of setuphost.exe; image version $($TEMP.Version)"
} }
# Save serviced boot manager files later copy to the root media. # 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\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 Copy-Item -Path $WINPE_MOUNT"\Windows\boot\efi\bootmgr.efi" -Destination $WORKING_PATH"\bootmgr.efi" -Force -ErrorAction stop | Out-Null
} }
# Dismount # Dismount
@ -618,10 +605,10 @@ Foreach ($IMAGE in $WINPE_IMAGES) {
#Export WinPE #Export WinPE
Write-Output "$(Get-TS): Exporting image to $WORKING_PATH\boot2.wim" 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 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 Move-Item -Path $WORKING_PATH"\boot2.wim" -Destination $MEDIA_NEW_PATH"\sources\boot.wim" -Force -ErrorAction stop | Out-Null
``` ```
### Update remaining media files ### Update remaining media files
@ -636,14 +623,18 @@ This part of the script updates the Setup files. It simply copies the individual
# Add Setup DU by copy the files from the package into the newMedia # Add Setup DU by copy the files from the package into the newMedia
Write-Output "$(Get-TS): Adding package $SETUP_DU_PATH" 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 cmd.exe /c $env:SystemRoot\System32\expand.exe $SETUP_DU_PATH -F:* $MEDIA_NEW_PATH"\sources" | Out-Null
if ($LastExitCode -ne 0)
{
throw "Error: Failed to expand $SETUP_DU_PATH. Exit code: $LastExitCode"
}
# Copy setup.exe from boot.wim, saved earlier. # Copy setup.exe from boot.wim, saved earlier.
Write-Output "$(Get-TS): Copying $WORKING_PATH\setup.exe to $MEDIA_NEW_PATH\sources\setup.exe" 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-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. # Copy setuphost.exe from boot.wim, saved earlier.
if (Test-Path -Path $WORKING_PATH"\setuphost.exe") { if (Test-Path -Path $WORKING_PATH"\setuphost.exe")
{
Write-Output "$(Get-TS): Copying $WORKING_PATH\setuphost.exe to $MEDIA_NEW_PATH\sources\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-Item -Path $WORKING_PATH"\setuphost.exe" -Destination $MEDIA_NEW_PATH"\sources\setuphost.exe" -Force -ErrorAction stop | Out-Null
} }
@ -651,28 +642,20 @@ if (Test-Path -Path $WORKING_PATH"\setuphost.exe") {
# Copy bootmgr files from boot.wim, saved earlier. # Copy bootmgr files from boot.wim, saved earlier.
$MEDIA_NEW_FILES = Get-ChildItem $MEDIA_NEW_PATH -Force -Recurse -Filter b*.efi $MEDIA_NEW_FILES = Get-ChildItem $MEDIA_NEW_PATH -Force -Recurse -Filter b*.efi
Foreach ($File in $MEDIA_NEW_FILES){ 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")) 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)" 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 Copy-Item -Path $WORKING_PATH"\bootmgfw.efi" -Destination $File.FullName -Force -ErrorAction stop | Out-Null
} }
elseif ($File.Name -ieq "bootmgr.efi") elseif ($File.Name -ieq "bootmgr.efi")
{ {
Write-Output "$(Get-TS): Copying $WORKING_PATH\bootmgr.efi to $($File.FullName)" 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 Copy-Item -Path $WORKING_PATH"\bootmgr.efi" -Destination $File.FullName -Force -ErrorAction stop | Out-Null
} }
} }
```
### Finish up
As a last step, the script removes the working folder of temporary files, and unmounts our language pack and Features on Demand ISOs.
```powershell
# #
# Perform final cleanup # Perform final cleanup
# #
@ -685,4 +668,13 @@ Write-Output "$(Get-TS): Dismounting ISO images"
Dismount-DiskImage -ImagePath $FOD_ISO_PATH -ErrorAction stop | Out-Null Dismount-DiskImage -ImagePath $FOD_ISO_PATH -ErrorAction stop | Out-Null
Write-Output "$(Get-TS): Media refresh completed!" Write-Output "$(Get-TS): Media refresh completed!"
```
### Finish up
As a last step, the script removes the working folder of temporary files, and unmounts our language pack and Features on Demand ISOs.
```powershell
TODO
``` ```