はじめに
Azure にある公式の Windows Server 2019 Datacenter の VM イメージは、記事執筆時点で .NET Framework 4.8 が入っていなかったので、 .NET Framework 4.8 入りの VM イメージをビルドした。Packer で。
Azure リソース グループを作成
ビルドした VM イメージの置き場となるリソースグループを作成しておく。PowerShell ではなく Azure CLI で行った。
az group create -n rg-example -l japaneast
Azure 資格情報の作成
Packer で Azure VM イメージを作成する際に必要な、Azure の資格情報を取得。
az ad sp create-for-rbac --query "{ client_id: appId, client_secret: password, tenant_id: tenant }"
サブスクリプションの ID も。
az account show --query "{ subscription_id: id }"
Packer テンプレートを定義
Packer は HCL に対応しているので、JSON ではなく HCL でテンプレートを記述した。ファイル名は windows.pkr.hcl。
variable "client_id" { type = string } variable "client_secret" { type = string } variable "tenant_id" { type = string } variable "subscription_id" { type = string } variable "managed_image_resource_group_name" { type = string default = "rg-example" } variable "managed_image_name" { type = string default = "vmi-example" } variable "build_resource_group_name" { type = string default = "rg-example" } variable "vm_size" { type = string default = "Standard_D2_v2" } source "azure-arm" "windowsserver2019" { client_id = var.client_id client_secret = var.client_secret tenant_id = var.tenant_id subscription_id = var.subscription_id managed_image_resource_group_name = var.managed_image_resource_group_name managed_image_name = var.managed_image_name os_type = "Windows" image_publisher = "MicrosoftWindowsServer" image_offer = "WindowsServer" image_sku = "2019-Datacenter" communicator = "winrm" winrm_use_ssl = true winrm_insecure = true winrm_timeout = "10m" winrm_username = "packer" azure_tags = { dept = "Engineering" task = "Image deployment" } build_resource_group_name = var.build_resource_group_name vm_size = var.vm_size } build { sources = [ "sources.azure-arm.windowsserver2019" ] provisioner "powershell" { inline = [ "while ((Get-Service RdAgent).Status -ne 'Running') { Start-Sleep -s 5 }", "while ((Get-Service WindowsAzureGuestAgent).Status -ne 'Running') { Start-Sleep -s 5 }", ] } # .NET Framework 4.8 をインストール provisioner "windows-shell" { inline = [ "curl -fSLo dotnet-framework-installer.exe https://download.visualstudio.microsoft.com/download/pr/7afca223-55d2-470a-8edc-6a1739ae3252/abd170b4b0ec15ad0222a809b761a036/ndp48-x86-x64-allos-enu.exe", ".\\dotnet-framework-installer.exe /q", "del .\\dotnet-framework-installer.exe", ] } provisioner "windows-restart" {} provisioner "powershell" { inline = [ "& $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit", "while($true) { $imageState = Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State | Select ImageState; if($imageState.ImageState -ne 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { Write-Output $imageState.ImageState; Start-Sleep -s 10 } else { break } }", ] } }
Packer イメージをビルド
取得しておいた Azure の資格情報を指定して packer build
実行。
packer build \ -var "client_id=<Azure 資格情報の client_id>" \ -var "client_secret=<Azure 資格情報の client_secret>" \ -var "tenant_id=<Azure 資格情報の tenant_id>" \ -var "subscription_id=<Azure 資格情報の subscription_id>" \ .\windows.pkr.hcl
ビルドは結構時間がかかる。10分以上、30分未満くらい。コーヒーでも飲みながら待つといい。
おわりに
今回は powershell provisioner を使って、 .NET Framework 4.8 のインストーラーを VM 内にダウンロードし、インストールした。
記事には書かなかったが、ローカルにあらかじめ用意したインストーラーを、file provisioner を使ってアップロードする方法も試したが、WinRM でのアップロードが激遅でまったく実用的ではなかった。
大きいファイルを VM イメージ内に含めたい場合は、Azure ファイル共有を介した方がいいだろうな。