Young Leaves

Azure Windows VM のイメージを取得しAzure Compute Gallery に格納する

Azure Windows VM のイメージを他のVM でも流用したいときがあります。今回はAzure Windows VM のイメージを取得し、Azure Compute Gallery に格納後、イメージからAzure VM を作成するまでを説明します。

若葉 香月
28 December, 2024

実施環境

Azure VM OS

Windows Server 2025 Datacenter Azure Edition

Azure CLI

2.67.0

Bicep CLI

0.32.4

前提条件

  • Azure リソース作成用のサブスクリプションがあること
  • Azure CLI の実行環境があること

Azure Compute Gallery とは

Azure Compute Gallery はAzure VM のイメージやアプリケーションを管理するサービスです。Compute Gallery を利用することで、サブスクリプションやテナント、パブリックへのAzure VM イメージ、アプリケーションの共有、管理が容易となります。

Azure Compute Gallery でVM イメージを管理するには、VM のイメージ定義の作成が必要です。VM 定義内でイメージのソース、OS 種別、リリースノート、最小、最大メモリを定義しバージョン管理します。バージョンごとのイメージ管理を行えるため、イメージからVM を作成する場合は特定のバージョンのVM を作成できます。VM イメージの管理ではレプリカで利用可能なVM 数を指定することで、スケーリング時のオーバーロードの削減もできます。アプリケーションを管理する場合、VM とアプリケーションを分離して管理できるため、アプリケーション更新時のVM イメージ更新の工数を削減できます。

Azure Compute Gallery はRBAC 共有、RBAC + 直接共有ギャラリー (プレビュー)、RBAC + コミュニティギャラリーの公開範囲があります。各公開範囲の違いは以下のとおりとなります。

ユーザー

グループ

サービスプリンシパル

特定のサブスクリプション (または) テナント内のすべてのユーザー

Azure のすべてのユーザーと公に

RBAC

×

×

RBAC + 直接共有ギャラリー

×

×

RBAC + コミュニティギャラリー

×

Azure Compute Gallery はグローバルにレプリケーションを行い、可用性ゾーンもサポートしています。これらの機能を利用することで、VM イメージ展開の可用性を向上できます。

Azure Compute Gallery 自体の料金は無料ですが、イメージのレプリカを格納するストレージコストと他リージョンへの初回レプリケートに課金が発生します。詳細な料金は以下URL を参照してください。

Azure VM のイメージについて

Azure VM のOS イメージには一般化されたイメージと特殊化されたイメージの2種類があります。一般化されたイメージはOS からユーザー・シンの固有情報を削除し、複数のサーバー (VM) で利用できるイメージです。特殊化されたイメージはユーザー・マシンの固有情報を残し、特定のサーバー(VM) でのみ利用できるイメージです。

Azure VM で一般化されたイメージを準備する場合、Windows OS ではSysprep ツール、Linux OS ではMicrosoft Azure Linux Agent を利用します。これらの作業はVM イメージのキャプチャ時に実施されないため、ユーザー側での手動対応が必要です。また、一部のサードパーティー製ソフトウェアではSysprep などで固有情報を削除しないものもあるため、これらのソフトウェアを利用する場合、VM 作成後にソフトウェアを導入するなどの工夫する必要があります (ウィルス対策ソフトや資産管理系のソフト、エージェントの類、など)。

Azure VM のイメージキャプチャはAzure Portal、Azure CLI、Azure PowerShell、各種IaC ツールで実施できます。Azure VM からイメージキャプチャ後、Azure VM のStatus がgeneralized となりVM の再起動ができなくなります。そのため、想定外の事象に対応する場合は事前にスナップショットの取得やバックアップの作成を行うようにしてください。

今回の構成

今回はIIS を導入したAzure Windows VM を一般化し、Azure Compute Gallery へ格納します。VM イメージの格納後、Compute Gallery のVM イメージを利用しVM を作成します。Azure リソースの作成はBicep (Azure Verified Modules) で行い、VM イメージの一般化、VM イメージ格納を手動で実施します。

リソースプロバイダーの登録

今回はAzure Verified Modules でAzure Windows VM を作成するため、EncryptionAtHost 機能を有効化します。既に有効化している場合はこの手順をスキップしてください。

# Microsoft.Compute/EncryptionAtHostを登録する
az feature register --namespace Microsoft.Compute --name EncryptionAtHost

EncryptionAtHostの状態を確認する
StateがRegisteredであること
az feature list -o table --query "[?contains(name, 'Microsoft.Compute/EncryptionAtHost')].{Name:name,State:properties.state}"
リソースプロバイダーを登録する
az provider register --namespace Microsoft.Compute

もしリソースプロバイダーを利用したくない場合はBicep テンプレートファイル作成時に「securityType: ''」と「encryptionAtHost: false」を指定し、ホスト暗号化を無効化してください。

Azure Windows VM、Azure Comute Gallery の作成

初めにリソースグループを作成し、VM イメージ作成用のAzure VM とAzure Compute Gallery を作成します。Bicep テンプレートファイルとパラメーターファイルのフォルダ構成は以下のとおりです。

.├── main.bicep└── parameters    └── main.bicepparam

Bicep パラメーターファイルは以下のとおりです。今回は検証のためAzure VM のパスワードをハードコードしていますが、実務などで利用する場合はSSH キーやEntra ID 認証を利用するようにしてください。

using '../main.bicep'
// リソースグループのパラメーターparam resourceGroupParam = {  name: 'rg-vmimage'}
// コンピューティングギャラリー、イメージ定義のパラメーターparam computeGalleryParam = {  name: 'galvmimage'  identifier: {    offer: 'WindowsServer'    publisher: 'MicrosoftWindowsServer'    sku: '2025-datacenter-g2'  }  isAcceleratedNetworkSupported: true  isHibernateSupported: true  memory: {    max: 16    min: 4  }  imagename: 'az-winsrv-iis'  osState: 'Generalized'  osType: 'Windows'  vCPUs: {    max: 8    min: 2  }}
// ネットワークセキュリティグループのパラメーターparam networkSecurityGroupParam = {  name: 'nsg-vmimage'  securityRules: [    {      name: 'AllowHTTPAccess'      properties: {        access: 'Allow'        description: 'Test HTTP Access'        destinationAddressPrefix: ''        destinationPortRange: '80'        direction: 'Inbound'        priority: 100        protocol: ''        sourceAddressPrefix: ''        sourcePortRange: ''      }    }    {      name: 'AllowRDPAccess'      properties: {        access: 'Allow'        description: 'Test RDP Access'        destinationAddressPrefix: ''        destinationPortRange: '3389'        direction: 'Inbound'        priority: 200        protocol: ''        sourceAddressPrefix: ''        sourcePortRange: ''      }    }  ]}
// 仮想ネットワークのパラメーターparam virtualNetworkParam = {  name: 'vnet-vmimage'  addressPrefixes: [    '10.0.0.0/16'  ]  subnets: [    {      name: 'sub-vm'      addressPrefix: '10.0.0.0/24'    }  ]}
// 仮想マシンのパラメーターparam virtualMachineParam = {  vmname: 'vm-vmimage'  adminUser: 'azureuser'  imageReference: {    publisher: 'MicrosoftWindowsServer'    offer: 'WindowsServer'    sku: '2025-datacenter-azure-edition'    version: 'latest'  }  nicConfigurations: [    {      nicname: 'nic-vmimage'      ipConfigurations: [        {          name: 'ipconfig01'          pipConfiguration: {            pipname: 'pip-vmimage'            skuName: 'Standard'            publicIpNameSuffix: ''            zones: []          }        }      ]      nicSuffix: ''    }  ]  osDisk: {    name: 'osdisk-vmimage'    caching: 'ReadWrite'    diskSizeGB: 128    managedDisk: {      storageAccountType: 'Standard_LRS'    }  }  osType: 'Windows'  vmSize: 'Standard_D2s_v3'  zone: 0}
param adminPassword = 'rQAZxs4w@Age'

Bicep テンプレートファイルは以下のとおりです。

targetScope = 'subscription'
@description('リソースグループのパラメーター')param resourceGroupParam object
@description('コンピューティングギャラリー、イメージ定義のパラメーター')param computeGalleryParam object
@description('ネットワークセキュリティグループのパラメーター')param networkSecurityGroupParam object
@description('仮想ネットワークのパラメーター')param virtualNetworkParam object
@description('仮想マシンのパラメーター')param virtualMachineParam object
@secure()@description('仮想マシンの管理者ユーザーパスワード')param adminPassword string
// リソースグループの作成module rg 'br/public:avm/res/resources/resource-group:0.4.0' = {  scope: subscription()  name: resourceGroupParam.name  params: {    name: resourceGroupParam.name  }}
// Azure コンピューティングギャラリー、イメージ定義の作成module gallery 'br/public:avm/res/compute/gallery:0.8.1' = {  scope: resourceGroup(resourceGroupParam.name)  name: computeGalleryParam.name  dependsOn: [    rg  ]  params: {    name: computeGalleryParam.name    images: [      {        hyperVGeneration: 'V2'        identifier: computeGalleryParam.identifier        isAcceleratedNetworkSupported: computeGalleryParam.isAcceleratedNetworkSupported        isHibernateSupported: computeGalleryParam.isHibernateSupported        memory: computeGalleryParam.memory        name: computeGalleryParam.imagename        osState: computeGalleryParam.osState        osType: computeGalleryParam.osType        vCPUs: computeGalleryParam.vCPUs      }    ]  }}
// ネットワークセキュリティグループ(NSG)の作成// IIS用のHTTPとSysprep用のRDPを開放するmodule nsg 'br/public:avm/res/network/network-security-group:0.5.0' = {  scope: resourceGroup(resourceGroupParam.name)  name: networkSecurityGroupParam.name  dependsOn: [    rg  ]  params: {    name: networkSecurityGroupParam.name    securityRules: networkSecurityGroupParam.securityRules  }}
// 仮想ネットワーク、サブネットの作成module vnet 'br/public:avm/res/network/virtual-network:0.5.2' = {  scope: resourceGroup(resourceGroupParam.name)  name: virtualNetworkParam.name  dependsOn: [    rg  ]  params: {    name: virtualNetworkParam.name    addressPrefixes: virtualNetworkParam.addressPrefixes    subnets: [      {        name: virtualNetworkParam.subnets[0].name        addressPrefix: virtualNetworkParam.subnets[0].addressPrefix        networkSecurityGroupResourceId: nsg.outputs.resourceId      }    ]  }}
// イメージ作成用VMの作成// VM作成時にNICも作成するmodule vm 'br/public:avm/res/compute/virtual-machine:0.11.0' = {  scope: resourceGroup(resourceGroupParam.name)  name: virtualMachineParam.vmname  dependsOn: [    rg  ]  params: {    name: virtualMachineParam.vmname    adminUsername: virtualMachineParam.adminUser    adminPassword: adminPassword    extensionAadJoinConfig: {      enabled: false    }    imageReference: virtualMachineParam.imageReference    nicConfigurations: [      {        name: virtualMachineParam.nicConfigurations[0].nicname        ipConfigurations: [          {            name: virtualMachineParam.nicConfigurations[0].ipConfigurations[0].name            pipConfiguration: {              name: virtualMachineParam.nicConfigurations[0].ipConfigurations[0].pipConfiguration.pipname              publicIpNameSuffix: virtualMachineParam.nicConfigurations[0].ipConfigurations[0].pipConfiguration.publicIpNameSuffix              zones: virtualMachineParam.nicConfigurations[0].ipConfigurations[0].pipConfiguration.zones            }            subnetResourceId: vnet.outputs.subnetResourceIds[0]          }        ]        nicSuffix: ''      }    ]    osDisk: virtualMachineParam.osDisk    osType: virtualMachineParam.osType    vmSize: virtualMachineParam.vmSize    zone: virtualMachineParam.zone  }}

Bicep テンプレートファイル、パラメーターファイルの準備後、Azure リソースを作成します。

# BicepテンプレートファイルでデプロイするAzureリソースを確認するaz deployment sub what-if -l japaneast -f main.bicep -p parameters/main.bicepparam
Azureリソースをデプロイする
az deployment sub create -l japaneast -f main.bicep -p parameters/main.bicepparam

Azure VM にIIS をインストール

Azure リソース作成後、VM にIIS をインストールします。Azure CLI からVM コマンドを実行しインストールします。

# IISをインストールするaz vm run-command invoke --command-id RunPowerShellScript --name vm-vmimage --resource-group rg-vmimage --scripts "Install-WindowsFeature -Name Web-Server"

Azure Windows VM の一般化、VM イメージの取得

IIS のインストール後、Azure Windows VM にRDP 接続し、Sysprep を使いVM を一般化します。Sysprep 後に一度でもVM を起動してしまうと初期設定画面が始まるため、Sysprep 後にAzure VM を起動しないよう注意してください。

REM Windows Serverを一般化するcd C:\Windows\System32\Sysprepsysprep /oobe /generalize /shutdown

OS イメージの一般化後、Azure VM のステータスをGeneralized に変更します。

# Azure VMの割り当てを解除するaz vm deallocate --resource-group rg-vmimage --name vm-vmimage
Azure VMのステータスを確認する
VM deallocated と表示されること
az vm get-instance-view --resource-group rg-vmimage --name vm-vmimage --query "instanceView.statuses[?starts_with(code, 'PowerState/')].displayStatus" --output tsv
Azure VMのステータスを一般化する
az vm generalize --resource-group rg-vmimage --name vm-vmimage

Azure VM の一般化後、仮想マシンのイメージからCompute Gallery の新しいイメージバージョンを作成します。

# Azure VMから新しいイメージバージョンを作成するaz sig image-version create   --resource-group rg-vmimage   --gallery-name galvmimage   --gallery-image-definition az-winsrv-iis   --gallery-image-version 1.0.1   --virtual-machine $(az vm show --resource-group rg-vmimage --name vm-vmimage --query "id" --output tsv)

Compute Gallery のイメージからAzure VM を作成する

Compute Gallery へVM イメージ格納後、格納したイメージを使いAzure VM を作成します。今回はAzure CLI を利用しAzure VM を作成します。Compute Gallery のイメージを利用する場合、Azure CLI の --image オプションで指定したバージョンのリソースID を指定します (動作確認用のためパスワードはそのままにしています)。

Compute Gallery のイメージ定義を利用するにはCompute Gallery のイメージ定義に対し読み取り権限が必要となりますため、事前に付与しておいてください。

# Compute GalleryのVMイメージからAzure VMを作成するaz vm create   --resource-group rg-vmimage   --name vm-fromgal   --image $(az sig image-version show --resource-group rg-vmimage --gallery-image-definition az-winsrv-iis --gallery-image-version 1.0.1 --gallery-name galvmimage --query "id" --output tsv)   --vnet-name vnet-vmimage   --subnet sub-vm   --admin-username azureuser   --admin-password rQAZxs4w@Age

VM 作成後、RDP でVM にログインしサーバーマネージャーにIIS があることを確認します。

リソースのクリーンアップ

動作確認後、各種リソースをクリーンアップします。

# リソースをクリーンアップするaz group delete --resource-group rg-vmimage

まとめ

  • Azure Compute Gallery はAzure VM のイメージ、アプリケーションの管理を行うサービス
  • Azure VM のVM イメージには一般化されたイメージと特殊化されたイメージがある
  • Azure VM から一般化されたVM イメージを作成する場合、Sysprep 後にAzure VM のステータスをGeneralized に変更する
  • Compute Gallery のイメージを利用するには、VM イメージ定義のID を指定して作成する

参考資料