かつて山市良と呼ばれたおじさんのブログ

セイテクエンジニアのブログ  かつて山市良と呼ばれたおじさんのブログ  vol.31 VMテンプレートのオフラインパッチスクリプト|続・ラボ環境 on Azure(3)

 

 

vol.31 VMテンプレートのオフラインパッチスクリプト|続・ラボ環境 on Azure(3)

2024年07月29日配信
執筆者:山内 和朗

 前回(vol.30)、Sysprepで一般化したHyper-V仮想マシン(VM)の仮想ハードディスク(VHDまたはVHDX、以下VHD(x))にもオフラインパッチが可能であることを紹介しました。今回はVHD(x)に対するオフラインパッチの一連の作業をPowerShellスクリプトで半自動化することに挑戦します。

 

VMテンプレートのオフラインパッチを効率化したい

 

 このブログの以下の記事では、Azure上に作成したHyper-Vのラボ環境で、Hyper-V VMのデプロイ(インポート)に繰り返し利用できるWindows Server 2022のVMテンプレートを作成しました。

vol.18 ラボ環境 on Azureを作る(6) - VMテンプレートを作成する

 この例のようにOSごとにVMテンプレート化しておけば、すばやくVMを必要な数だけ準備することができます。しかし残念なことに、Sysprepで一般化したイメージを含むVMテンプレートは、毎月毎月古くなっていくため、VMテンプレートを作成した次の月からは、VMをデプロイ後に最新の更新プログラムをインストールする必要があります。

 VMテンプレートのイメージを最新に維持するために、毎月VMテンプレートを再作成(最新の更新プログラムをインストールして再びSysprepを実行し、エクスポート)するのは大変です。VHD(x)に対するオフラインパッチを利用すれば、VMテンプレートのイメージを起動することなく、Windowsや.NET Frameworkの品質更新プログラム(累積的な更新プログラム)でイメージを最新の状態に更新することができます。

 

効率化のための半自動化スクリプト(注: 実験中)

 

 特定の場所にダウンロードしておいた、ゲストOS用の更新プログラムのMSUパッケージを、VMテンプレートのVHD(x)に自動的に適用するPowerShellスクリプトを考えてみました。この「offlinepatch.ps1」に、ダウンロードしたMSUパッケージを保存したパス、更新対象のVHD(x)のパス、オフラインパッチ中にVHD(x)のイメージをマウントするマウント先を指定して実行することで、VHD(x)を更新しようという算段です。

 

[offlinepatch.ps1]

Param($PackageDir,$TargetVhd,$mountdir)
if ($PSBoundParameters.Count -ne 3)  { Write-host "Error: -PackageDir <path> -TargetVhd <path> -MountDir <path>" ;exit }
If ( -not (Test-Path $packagedir)) { Write-host "Error: -PackageDir <path> does not exist." ;exit }
If ( -not (Test-Path $targetvhd)) { Write-host "Error: -TargetVhd <path> does not exist." ;exit }
If ( -not (Test-Path $mountdir)) { Write-host "Error: -MountDir <path> does not exist." ;exit }

$packages = (Get-ChildItem -Path $packagedir| where {$_.extension -eq ".msu"} | Sort Name | %{$_.FullName})

$success = $true
if ($packages.Count -gt 0){
  DISM /Mount-Image /Imagefile:$targetvhd /index:1 /mountdir:$mountdir
  #if (Test-Path -Path $mountdir"\Windows") {
  if ($LASTEXITCODE -eq 0) {
    foreach ($package in $packages){
      DISM /Add-Package /Image:$mountdir /PackagePath:$package
      if (!($LASTEXITCODE -eq 0)) {
        $success = $false
        Write-Host "Failed: "$package
      }
    }
  } else {
    "Failed: DISM /Mount-Image"
    "Failed: VHD(x) was not updated."
    exit
  }
  if ($success) {
    DISM /Unmount-Image /mountdir:$mountdir /Commit
    if (!($LASTEXITCODE -eq 0)) {
      DISM /Unmount-Image /mountdir:$mountdir /Discard
      "Failed: DISM /Commit, VHD(x) was not updated. "
      exit
    } 
  } else {
    DISM /Unmount-Image /mountdir:$mountdir /Discard
    "Failed: DISM /Add-Package, VHD(x) was not updated."
    exit
  }
}
exit

 

 このスクリプトをPowerShellで次のように実行すると、VHD(x)をマウント先にマウントし、パッケージのパスに存在する1以上のMSUパッケージを順番に適用して、その後、マウントを解除します(画面1、画面2)。

PS C:\> .\offlinepatch.ps1 -PackageDir "<パッケージのパス>" -TargetVhd "<VHD(x)のパス" -MountDir "<マウント先パス>"


画面1
画面1 「C:¥Tools¥Packages」フォルダーにMSUパッケージを保存してから、「offlinepatch.ps1」を実行

画面2
画面2 複数のMSUパッケージの適用後、マウントが解除されて、VHD(x)が更新された

 まだまだ実験中のスクリプトですが、エラーが無い限り、期待通りに動作するようです。画面の例では、ファイル名やフォルダー名に空白を含む場合にちゃんと動くかどうか自信がなかったので、VMテンプレートの「Virtual Hard Disks」フォルダーに移動してからスクリプトを実行しましたが、その後の検証で空白を含んでいても問題なく動くことを確認しました。

 このスクリプトはあくまでもサンプルです。いかなる責任も負いません。エラー処理についてはパラメーターの数とフォルダーパスの簡単なチェックしか行っていませんし、DISMコマンドで発生するエラーに対する処理のテストはまだ不十分です。一応、Windowsのバージョンと更新プログラムの対象バージョンが一致せずに、DISMによる適用がエラーで失敗する場合の処理はテストしました(画面3、画面4)。私のラボ環境のためのスクリプトですから、現状はこのまま使用し、必要に応じて調整していこうと思っています。肝心なのは、テストしきれていないエラーが発生しないように、適切なMSUファイルをダウンロードしておくことです(と来月の自分への伝言)。

 

画面3

画面3 Windows 10バージョン22H2のVHDXに、Windows Server 2022向けの更新プログラム(KB5037782)を適用させようとしたところ。DISMのエラーを適切に処理し、VHDXは更新されなかった

 

画面4

画面4 同じVHDXに対して、正しい更新プログラム(Windows 10バージョン22H2向けKB5039211、OSビルド19045.4529)のパッケージと入れ替えて実行すると、更新に成功

 

blog_subscribe

blog_comment

最新記事