다음을 통해 공유


동적 업데이트를 사용하여 Windows 설치 미디어 업데이트

이 문서에서는 배포 전에 기존 Windows 이미지에 동적 업데이트 패키지를 획득하고 적용하는 방법을 설명하고 이 프로세스를 자동화하는 데 사용할 수 있는 Windows PowerShell 스크립트를 포함합니다.

볼륨 라이선스 미디어는 VLSC(볼륨 라이선싱 서비스 센터) 및 비즈니스용 Windows 업데이트, WSUS(Windows Server Update Services) 및 Visual Studio 구독 같은 기타 관련 채널에서 Windows의 각 릴리스에 사용할 수 있습니다. 동적 업데이트를 사용하여 이전에 설치되었을 수 있는 언어 팩 및 FOD(주문형 기능)를 유지하면서 Windows 디바이스에 현재 위치 업그레이드의 일부로 최신 기능 업데이트 패키지가 있는지 확인할 수 있습니다. 또한 동적 업데이트는 현재 위치 업그레이드 프로세스의 일부로 별도의 품질 업데이트를 설치할 필요가 없습니다.

동적 업데이트

미디어 또는 Windows 업데이트 연결된 환경에서 기능 업데이트 설치가 시작될 때마다 동적 업데이트가 첫 번째 단계 중 하나입니다. Windows 설치 프로그램은 Microsoft 엔드포인트에 연결하여 동적 업데이트 패키지를 가져온 다음 해당 업데이트를 운영 체제 설치 미디어에 적용합니다. 업데이트 패키지에는 다음과 같은 종류의 업데이트가 포함됩니다.

  • 설치 프로그램에서 기능 업데이트에 사용하는 이진 파일 또는 기타 파일을 Setup.exe 업데이트
  • Windows 복구 환경에 사용되는 "안전한 운영 체제"(SafeOS)에 대한 업데이트
  • 기능 업데이트를 완료하는 데 필요한 서비스 스택에 업데이트 자세한 내용은 서비스 스택 업데이트를 참조하세요.
  • 최신 누적(품질) 업데이트
  • 특히 동적 업데이트를 위한 제조업체에서 이미 게시한 해당 드라이버에 대한 업데이트

동적 업데이트는 언어 팩 및 주문형 기능 패키지를 다시 액세스하여 유지합니다.

디바이스는 동적 업데이트 얻기 위해 인터넷에 연결할 수 있어야 합니다. 일부 환경에서는 동적 업데이트 가져오는 옵션이 아닙니다. 디바이스에서 설치 프로그램을 시작하기 전에 동적 업데이트 패키지를 획득하고 이미지에 적용하여 미디어 기반 기능 업데이트를 계속 수행할 수 있습니다.

동적 업데이트 패키지 가져오기

Microsoft 업데이트 카탈로그에서 동적 업데이트 패키지를 가져올 수 있습니다. 해당 사이트에서 오른쪽 위에 있는 검색 창을 사용하여 특정 릴리스에 대한 동적 업데이트 패키지를 찾습니다. 다양한 동적 업데이트 패키지가 단일 검색 결과에 모두 없는 것일 수 있으므로 모든 업데이트를 찾으려면 다른 키워드로 검색해야 할 수 있습니다. 결과의 다양한 부분을 확인하여 필요한 파일을 식별했는지 확인합니다. 다음 표에서는 결과에서 검색하거나 찾을 키 값을 보여 줍니다.

Windows 11 버전 22H2 동적 업데이트 패키지

타이틀은 각 동적 패키지를 구분할 수 있습니다. 누적 업데이트에는 서비스 스택이 포함되어 있습니다. 서비스 스택은 지정된 누적 업데이트에 필요한 경우에만 게시됩니다.

패키지 업데이트 제목
안전한 OS 동적 업데이트 Windows 11 버전 22H2용 YYYY-MM 안전 OS 동적 업데이트
동적 업데이트 설정 Windows 11 버전 22H2용 YYYY-MM 설치 동적 업데이트
최신 누적 업데이트 Windows 11 버전 22H2용 YYYY-MM 누적 업데이트
서비스 스택 동적 업데이트 Windows 11 버전 22H2용 YYYY-MM 서비스 스택 업데이트

Windows 11, 버전 21H2 동적 업데이트 패키지

제목, 제품설명 은 각 동적 패키지를 구분하는 데 필요합니다. 최신 누적 업데이트에는 서비스 스택이 포함되어 있습니다. 서비스 스택은 지정된 누적 업데이트의 필수 구성 요소로 필요한 경우에만 별도로 게시됩니다.

패키지 업데이트 제목 제품 설명
안전한 OS 동적 업데이트 Windows 11 대한 YYYY-MM 동적 업데이트 Windows 안전 OS 동적 업데이트 ComponentUpdate
동적 업데이트 설정 Windows 11 대한 YYYY-MM 동적 업데이트 Windows 10 이상 동적 업데이트 SetupUpdate
최신 누적 업데이트 Windows 11 대한 YYYY-MM 누적 업데이트
서비스 스택 동적 업데이트 Windows 11 버전 21H2용 YYYY-MM 서비스 스택 업데이트

Windows 10 버전 22H2 동적 업데이트 패키지

제목, 제품설명 은 각 동적 패키지를 구분하는 데 필요합니다. 최신 누적 업데이트에는 서비스 스택이 포함되어 있습니다. 서비스 스택은 지정된 누적 업데이트의 필수 구성 요소로 필요한 경우에만 별도로 게시됩니다.

패키지 업데이트 제목 제품 설명
안전한 OS 동적 업데이트 Windows 10 버전 22H2용 YYYY-MM 동적 업데이트 Windows 안전 OS 동적 업데이트 ComponentUpdate
동적 업데이트 설정 Windows 10 버전 22H2용 YYYY-MM 동적 업데이트 Windows 10 이상 동적 업데이트 SetupUpdate
최신 누적 업데이트 Windows 10 버전 22H2용 YYYY-MM 누적 업데이트
서비스 스택 동적 업데이트 Windows 10 버전 22H2용 YYYY-MM 서비스 스택 업데이트

추가 언어 또는 주문형 기능으로 이미지를 사용자 지정하려면 볼륨 라이선싱 서비스 센터에서 추가 미디어 ISO 파일을 다운로드합니다. 예를 들어 디바이스에 대해 동적 업데이트를 사용하지 않도록 설정하며 사용자에게 특정 주문형 기능이 필요한 경우 이미지에 미리 설치할 수 있습니다.

Windows 설치 미디어 업데이트

설치 미디어를 올바르게 업데이트하려면 여러 다른 대상(이미지 파일)에서 작동하는 많은 작업이 포함됩니다. 일부 작업은 다른 대상에서 반복됩니다. 대상 이미지 파일에는 다음이 포함됩니다.

  • WinPE(Windows 사전 설치 환경): Windows 운영 체제 설치, 배포 및 복구에 사용되는 소규모 운영 체제
  • WinRE(Windows 복구 환경): 부팅할 수 없는 운영 체제의 일반적인 원인을 복구합니다. 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 추가 27
WinPE에서 부팅 관리자 추가 28
최신 누적 업데이트 추가 12 23
이미지 정리 7 13 24
선택적 구성 요소 추가 14
.NET 및 .NET 누적 업데이트 추가 15
이미지 내보내기 8 16 25

참고

2021년 2월부터 최신 누적 업데이트 및 서비스 스택 업데이트가 결합되어 Microsoft 업데이트 카탈로그에 새로운 결합 누적 업데이트로 배포됩니다. 설치 미디어를 업데이트하기 위해 서비스 스택 업데이트가 필요한 1, 9 및 18단계의 경우 결합된 누적 업데이트를 사용해야 합니다. 결합된 누적 업데이트에 대한 자세한 내용은 서비스 스택 업데이트를 참조하세요.

참고

Microsoft는 "Adobe Flash Player 제거 업데이트"KB4577586 통해 Windows에서 Flash 구성 요소를 제거합니다. 20단계와 21단계 사이에 업데이트를 KB4577586(카탈로그에서 사용 가능)로 배포하여 언제든지 Flash를 제거할 수도 있습니다. 2021년 7월부터 KB4577586 "Adobe Flash Player 제거 업데이트"가 Windows 10, 버전 1607 및 1507에 대한 최신 누적 업데이트에 포함됩니다. 업데이트는 Windows 8.1, Windows Server 2012 및 Windows Embedded 8 Standard에 대한 월별 롤업 및 보안 전용 업데이트에도 포함됩니다. 자세한 내용은 Adobe Flash Player 지원 종료 업데이트를 참조하세요.

여러 Windows 버전

기본 운영 체제 파일(install.wim)에는 여러 버전의 Windows가 포함될 수 있습니다. 인덱스 기반의 지정된 버전에 대한 업데이트만 배포해야 할 수 있습니다. 또는 모든 버전에 업데이트가 필요할 수 있습니다. 또한 주문형 기능 전에 언어가 설치되고 최신 누적 업데이트가 항상 마지막으로 적용되었는지 확인합니다.

추가 언어 및 기능

업데이트를 수행하기 위해 이미지에 더 많은 언어와 기능을 추가할 필요는 없지만 시작 이미지에 있는 것 이상으로 더 많은 언어, 선택적 구성 요소 및 주문형 기능으로 이미지를 사용자 지정할 수 있습니다. 더 많은 언어 및 기능을 추가할 때는 먼저 서비스 스택 업데이트 적용, 언어 추가, 기능 추가, 마지막으로 최신 누적 업데이트 등 올바른 순서로 변경해야 합니다. 제공된 샘플 스크립트는 제2 언어(이 경우 일본어(ja-JP))를 설치합니다. 이 언어는 lp.cab 지원되므로 언어 환경 팩을 추가할 필요가 없습니다. 일본어 는 사용자가 일본어로 복구 화면을 볼 수 있도록 기본 운영 체제와 복구 환경 모두에 추가됩니다. 여기에는 현재 복구 이미지에 설치된 패키지의 지역화된 버전 추가가 포함됩니다.

.NET 기능과 함께 선택적 구성 요소는 오프라인으로 설치할 수 있지만 이렇게 하면 디바이스를 다시 시작해야 하는 보류 중인 작업이 만들어집니다. 따라서 이미지 정리를 수행하기 위한 호출이 실패합니다. 정리 실패를 방지하는 두 가지 옵션이 있습니다. 한 가지 옵션은 이미지 정리 단계를 건너뛰는 것이지만, 이로 인해 install.wim이 커집니다. 또 다른 옵션은 정리 후 내보내기 전에 단계에서 .NET 및 선택적 구성 요소를 설치하는 것입니다. 샘플 스크립트의 옵션입니다. 이렇게 하면 다음에 이미지를 유지 관리하거나 업데이트할 때(예: 다음 달) 원래 install.wim(보류 중인 작업 없음)으로 시작해야 합니다.

기존 이미지에 동적 업데이트 적용하는 스크립트 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
$LCU_PATH        = "C:\mediaRefresh\packages\LCU.msu"
$SSU_PATH        = "C:\mediaRefresh\packages\SSU_DU.msu"
$SETUP_DU_PATH   = "C:\mediaRefresh\packages\Setup_DU.cab"
$SAFE_OS_DU_PATH = "C:\mediaRefresh\packages\SafeOS_DU.cab"
$DOTNET_CU_PATH  = "C:\mediaRefresh\packages\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 버전 업데이트

스크립트는 기본 운영 체제 파일(install.wim) 내의 각 Windows 버전을 업데이트합니다. 각 버전에 대해 기본 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 WinPE
$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.

        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"
        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"
                        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"
            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"
                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"
                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"
        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"
    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"
    # 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"
    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"
    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"
    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"
    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"
    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"
    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"
    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"
    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"
    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~~~~"
    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"
    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 저장하여 이 버전이 설치 미디어의 \sources\setup.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
    {
        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"
    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"
                    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"
        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"
            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"
            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"
    Add-WindowsPackage -Path $WINPE_MOUNT -PackagePath $LCU_PATH -ErrorAction stop | Out-Null  

    # Perform image cleanup
    Write-Output "$(Get-TS): Performing image cleanup on WinPE"
    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 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 및 부팅 관리자 파일을 최종 대체합니다.

#
# 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 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!"