Young Leaves

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

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

若葉 香月
28 December, 2024

実施環境

OS/ツール バージョン
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\Sysprep
sysprep /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 があることを確認します。

image

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

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

# リソースをクリーンアップする
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 を指定して作成する

参考資料