EC2 Image BuilderでWindows Server OSを日本語化するコンポーネントを作ってみた – Ver.2

2023.04.08

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

しばたです。

以前の記事でWindows Server 2019専用でUIを日本語化するコンポーネントを作成しました。

今回はWindows Server 2022版の作成を検討しコンポーネントの仕組みを見直してみました。

Windows Server 2022の問題

前の記事を公開した直後は単純に「Windows Server 2022用に言語パックのURLを変えるだけで大丈夫だろう。」と考えていたのですが、どうもWindows Serverの言語パックは原則非公開でWindows Server 2019だけが例外的な扱いの様でした。
(通常はGUIの設定画面からダウンロード、オフライン環境ではボリュームライセンスサービスセンター等の契約者向けサイトからダウンロードする前提の模様)

このためWindow Server 2022においても言語パック単体の公開はしておらず、オンラインでは評価版ISOファイルを代替として使える様でした。

この ISO は Windows Server 2022 上でのみ利用可能であり、これまで別々に提供されていたオンデマンド機能 (FOD) と言語パックの ISO をひとつにまとめたものです。これを FOD と言語パックのリポジトリとして使用することができます。

ただ、この評価版ISOファイルは約6GBあり、流石に都度ダウンロードするには重すぎると判断しました。

そこで今回は事前にS3バケットに言語パックのCABファイル(Microsoft-Windows-Server-Language-Pack_x64_ja-jp.cab)をアップロードしておき、S3からCABファイルをダウンロードする形に方式を変えることにしました。
併せてWindows Server 2019でも使える様に処理の共通化も図りました。

作ったコンポーネント (Ver.2.1)

以下が更新したコンポーネント定義です。

記事公開後に少し内容を見直し、Ver.2.1としています。

今回はWindows Server 2022およびWindows Server 2019を対象としています。
動作確認はしてませんがWindows Server 2016でも動くと思います。

今回は利用者環境のS3バケットから言語パックのCABファイルを直接ダウンロードする仕組みに変えため、コンポーネントのパラメーターで「S3バケット名」「CABファイルのパス」を指定する形になっています。

コンポーネント定義 Ver.2.1

name: windows-server-setup-japanese-ui
description: Install Japanese Language pack and Update UI configurations.
schemaVersion: 1.0

parameters:
  - S3BucketName:
      type: string
      default: 'your-bucket-name'
      description: Set your S3 bucket name.
  - S3BucketPath:
      type: string
      default: 'LanguagePack/WindowsServer2022/Microsoft-Windows-Server-Language-Pack_x64_ja-jp.cab'
      description: Set your S3 key path for language packe .cab file.
phases:
  - name: build
    steps:
      - name: DownloadLanguagePackStep
        action: S3Download
        inputs:
        - source: s3://{{ S3BucketName }}/{{ S3BucketPath }}
          destination: C:\Windows\TEMP\Microsoft-Windows-Server-Language-Pack_x64_ja-jp.cab
          overwrite: true
      - name: InstallLanguagePackStep
        action: ExecutePowerShell
        inputs:
          commands:
            - |
              # Set configurations
              $jpCabPath = 'C:\Windows\TEMP\Microsoft-Windows-Server-Language-Pack_x64_ja-jp.cab'

              # Check if file exists
              if (-not (Test-Path -LiteralPath $jpCabPath)) {
                  Write-Error ('Failed to download {0}.' -f $jpCabPath)
                  return 
              }
              
              # Install language pack  
              try {
                  Write-Output 'Insall Language pack cab file...'
                  Write-Output ('  {0}' -f $jpCabPath)
                  Add-WindowsPackage -Online -PackagePath $jpCabPath -LogPath (Join-Path -Path $env:TEMP 'dism.log')
              } catch {
                  Write-Error $_
                  return
              } finally {
                  # Remove .cab
                  if (Test-Path -LiteralPath $jpCabPath) {
                      Write-Output 'Remove Language pack cab file...'
                      Remove-Item -LiteralPath $jpCabPath
                  }
              }

              # Set UI Language Settings
              Write-Output 'Update language list...'
              Set-WinUserLanguageList -LanguageList ja-JP, en-US -Force

              # Set System local
              Write-Output 'Update system locale...'
              Set-WinSystemLocale -SystemLocale ja-JP
      - name: RebootAfterLanguagePackInstalled
        action: Reboot
        inputs:
          delaySeconds: 10
      - name: UpdateUIConfigurationsStep
        action: ExecutePowerShell
        inputs:
          commands:
            - |
              # Update UI Language Settings
              Write-Output 'Update UI language...'
              Set-WinUILanguageOverride -Language ja-JP
              Write-Output 'Update Input method...'
              Set-WinDefaultInputMethodOverride -InputTip '0411:00000411'

              # Set Location settings
              Write-Output 'Set Location...'
              Set-WinHomeLocation -GeoId 122
              Set-WinCultureFromLanguageListOptOut -OptOut $False

              # Set Timezone
              Write-Output 'Set Timezone...'
              Set-TimeZone -Id 'Tokyo Standard Time'
      - name: UpdateSysprepCconfigsStep
        action: ExecutePowerShell
        inputs:
          commands:
            - |
              $xmlFiles = @()
              # EC2 Launch v2
              if (Test-Path -LiteralPath 'C:\ProgramData\Amazon\EC2Launch\sysprep\unattend.xml') {
                  $xmlFiles += 'C:\ProgramData\Amazon\EC2Launch\sysprep\unattend.xml'
              }
              # EC2 Launch v1
              if (Test-Path -LiteralPath 'C:\ProgramData\Amazon\EC2-Windows\Launch\Sysprep\Unattend.xml') {
                  $xmlFiles += 'C:\ProgramData\Amazon\EC2-Windows\Launch\Sysprep\Unattend.xml'
              }
              foreach ($x in $xmlFiles) {
                  Write-Output ('Update {0}...' -f $x)
                  # Backup Unattend.xml 
                  Copy-Item -LiteralPath $x -Destination "${x}.orig" -Force
                  # Update Unattend.xml
                  $xmlDoc = [ xml](Get-Content -LiteralPath $x -Raw)
                  # Update Timezone
                  $xmlDoc.unattend.settings.component | Where-Object { $_.name -eq 'Microsoft-Windows-Shell-Setup' } | ForEach-Object {
                      $_.TimeZone = 'Tokyo Standard Time'
                  }
                  # Update Launguage, Locale
                  $xmlDoc.unattend.settings.component | Where-Object { $_.name -eq 'Microsoft-Windows-International-Core' } | ForEach-Object {
                      $_.InputLocale = 'ja-JP'
                      $_.SystemLocale = 'ja-JP'
                      $_.UILanguage = 'ja-JP'
                      $_.UserLocale = 'ja-JP'
                  }
                  $xmlDoc.Save($x)
              }
      - name: ApplyWindowsUpdateStep
        action: UpdateOS

コードの解説

前回からの変更点を中心にコードの解説をしておきます。

1. 言語パックのダウンロード (DownloadLanguagePackStep)

記事公開後にAWS組み込みのアクションS3Downloadがあることに気が付き処理を見直しています。
このアクションを使いS3バケットに配備した言語パック(CABファイル)をC:\Windows\TEMP\にダウンロードする様にしています。

S3バケット名とCABファイルのパスはパラメーターで指定可能にしてあり、デフォルト値は

  • S3BucketName = your-bucket-name
  • S3BucketPath = LanguagePack/WindowsServer2022/Microsoft-Windows-Server-Language-Pack_x64_ja-jp.cab

としています。
S3アクセスをするためImage Builderで使うIAMロールに対象S3バケット・オブジェクトへの読み取り権限を与えておく必要があります。

2. 言語パックのインストール (InstallLanguagePackStep)

言語パックのインストール処理は前回と変わりありません。
ダウンロード処理をAWS組み込みアクションにしたことで処理がすっきりしました。

3. 再起動 (RebootAfterLanguagePackInstalled)

追加した日本語にUIを変更する(具体的にはSet-WinUILanguageOverrideコマンドの実行)には一度再起動する必要があるためImage BuilderのRebootアクションを使ってOSを再起動します。
併せてこの再起動でシステムロケールの変更も反映させています。

ここは前回と変わりありません。

4. 言語設定の変更 (UpdateUIConfigurationsStep)

再起動後はUI言語の変更(Set-WinUILanguageOverride)をはじめ各種設定変更コマンドを実行しており、ざっくり

  • UI言語を日本語に変更
  • 「地域」のを日本に変更
  • タイムゾーンをJSTに変更

をやっています。
ここも前回同様であり、Windows Server 2019、Windows Server 2022で同じ処理が使えます。

このステップでOSの設定変更は完了です。

5. Sysprep設定の変更 (UpdateSysprepCconfigsStep)

Image BuilderでAMIを作成する際はSysprepによる一般化が行われるためUnattend.xmlファイルを変更してやる必要があるのも前回と同じです。

ただし、Unattend.xmlファイルの配置場所はOS毎(正確にはEC2 Launchのバージョン毎)によって変わるため更新対象ファイルのチェックを追加しています。

6. Windows Updateの実施 (ApplyWindowsUpdateStep)

言語パックをインストールした後はWindows Updateをした方が良いのでAWS組み込みアクションUpdateOSを追加しました。

この作業により言語パックファイル作成時点以降の更新に対する言語リソースの適用などを行います。

試してみる

ここから実際にこのコンポーネントを試していきます。
今回も私の検証AWSアカウントの東京リージョンを使用しています。

0. S3バケットの準備

まずは事前準備として任意のS3バケットにCABファイルをアップロードしておきます。

今回は私の個人用バケットのLanguagePack/WindowsServer2022/Microsoft-Windows-Server-Language-Pack_x64_ja-jp.cabにアップロードしています。

ちなみに元データはWindows Server 2022評価版ISOファイルのLanguagesAndOptionalFeaturesフォルダに存在しています。

ISOファイルのダウンロードおよびS3のアップロード手順については割愛します。

1. コンポーネントの作成

続けてマネジメントコンソールから新規にコンポーネントを作成します。

タイプは「ビルド」でwindows-server-setup-japanese-uiという名前にしています。
コンポーネントバージョンは何でも構わないのですが、前回からのバージョンアップの意味を込めて2.0.0としています。
そして、互換性のあるOSバージョンはWindows Server 2019とWindows Server 2022にしています。

コンポーネント定義は前掲のコードをそのまま転記します。

その他設定は必要があれば行ってください。

エラー無く作成できればOKです。

2. レシピの作成

あとは通常の流れ通りレシピを作りパイプラインを実行していく形となります。

今回は前回作ったmy-japanese-windows-imageレシピをVer.2.0.0に更新する形にしました。
主要な点だけ解説すると、対象OSを「Windows Server 2022」に更新し、

ビルドコンポーネントをwindows-server-setup-japanese-uiに切り替えて、S3BucketNameS3BucketPathの2パラメーターを事前準備で用意した環境に合わせた値にしてやります。

作成結果はこんな感じです。

3. パイプラインの作成

パイプラインも前回作ったbuild-my-japanese-windows-imageを更新する形にしました。
レシピをmy-japanese-windows-image Ver.2.0.0に更新します。

インフラストラクチャ設定およびディストリビューション設定は新規に作成するか環境に応じたものにしてください。
今回のコンポーネントを利用するのに必要な前提条件は

  • ビルド時に要インターネットアクセス (S3へのアクセスのため)
    • VPC Endpointで代用可能
  • 当該S3バケットおよびオブジェクトに対する読み取り権限
    • IAMロールに権限を追加してください

となるため、S3バケットへのアクセス権が必要となります。
今回は横着してIAMロールにAmazonS3ReadOnlyAccessをアタッチしていますが、本番環境ではちゃんと対象バケットを制限する様にしてください。

(事前に用意しているImage Builder用IAMロール。構築手順は割愛)

4. パイプラインの実行

これで準備ができたので、最後にパイプラインを実行して作成されるイメージがちゃんと日本語化されているか確認していきます。

実行結果はこんな感じでami-09c06b1953b961f3cというAMIが作成されました。

このAMIから確認用のEC2インスタンスを起動してやり、日本語化されていることを確認します。

最後に

以上となります。

S3へのアクセス権が必要になったものの対応OSも増え処理としてはだいぶ汎用的なものになったと思います。
需要がどれだけあるかは怪しいですが誰かの役に立てば幸いです。