You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在单个Packer Azure-Arm模板中同时构建VHD和托管镜像?

问题:同一Packer模板同时支持构建VHD和Managed Image

我正在学习Packer,目前通过注释代码的不同部分可分别生成Managed Image和VHD,但同时配置两者输出参数时会触发报错:

Error: 2 error(s) occurred:

* Either a VHD or a managed image can be built, but not both. Please specify either capture_container_name and capture_name_prefix or managed_image_resource_group_name and managed_image_name.
* Specify either a VHD (storage_account and resource_group_name) or Managed Image (managed_image_resource_group_name and managed_image_name) output

  on windows_server2019.pkr.hcl line 10:
  (source code not available)


==> Wait completed after 0 seconds

==> Builds finished but no artifacts were created.

我的模板代码如下:

packer {
  required_plugins {
    windows-update = {
      version = "0.14.1"
      source  = "github.com/rgl/windows-update"
    }
  }
}

source "azure-arm" "server_2019" {
  use_azure_cli_auth                               = true
  azure_tags                                       = local.tags
  build_resource_group_name                        = var.resource_group_name
  build_key_vault_name                             = "EXAMPLE-RGP-${var.environment}-KVT"
  os_type                                          = local.azure_sku.os_type
  image_publisher                                  = local.azure_sku.image_publisher
  image_offer                                      = local.azure_sku.image_offer
  image_sku                                        = local.azure_sku.image_sku
  vm_size                                          = local.azure_sku.vm_size
  shared_gallery_image_version_exclude_from_latest = false
  virtual_network_resource_group_name              = local.network.virtual_network_resource_group_name
  virtual_network_name                             = local.network.virtual_network_name
  virtual_network_subnet_name                      = local.network.virtual_network_subnet_name
  private_virtual_network_with_public_ip           = false
  communicator                                     = "winrm"
  winrm_use_ssl                                    = true
  winrm_insecure                                   = true
  winrm_timeout                                    = "3m"
  winrm_username                                   = "winrm"
  winrm_password                                   = var.winrm_password

  # VHD (Both VHD and Managed image can't be done at the same time, comment one of them out)
  resource_group_name    = var.resource_group_name
  storage_account        = lower("examplestorage${var.environment}blobevgimg")
  capture_container_name = "evergreen-images"
  capture_name_prefix    = "packer_server2019"

  # Managed Image (Both VHD and Managed image can't be done at the same time, comment one of them out)
  managed_image_name                 = "packer_server2019"
  managed_image_resource_group_name  = var.resource_group_name
  managed_image_storage_account_type = "Standard_LRS"
  shared_image_gallery_destination {
    resource_group       = var.resource_group_name
    gallery_name         = upper("GALLERY${var.environment}EVERGREENIMAGES")
    image_name           = "Evergreen-Images-Windows"
    image_version        = formatdate("YYYY.MM.DD", timestamp())
    replication_regions  = ["australiaeast", "australiasoutheast"]
    storage_account_type = "Standard_ZRS"
  }
}

build {
  sources = [
    "sources.azure-arm.server_2019",
  ]

  provisioner "file" {
    source      = "../scripts/ConfigureRemotingForAnsible.ps1"
    destination = "C:\\temp\\ConfigureRemotingForAnsible.ps1"
  }

  provisioner "windows-update" {
    search_criteria = "IsInstalled=0"
    filters = [
      "exclude:$_.Title -like '*Preview*'",
      "include:$true",
    ]
    update_limit = 25
  }
}

我知道可通过拆分模板文件并单独调用CLI实现,但想了解是否有方法能在同一个模板文件中同时支持构建VHD和Managed Image?


解决方案

可以通过变量+条件配置的方式在同一个模板中实现两种输出类型的切换,无需拆分文件,以下是两种可行方案:

方案一:单Source块+条件参数赋值

通过定义变量控制输出类型,在Source块中对VHD和Managed Image的参数进行条件赋值,不生效的参数设为null(Packer会自动忽略null参数)。

修改后的模板示例:

packer {
  required_plugins {
    windows-update = {
      version = "0.14.1"
      source  = "github.com/rgl/windows-update"
    }
  }
}

# 新增变量:指定输出类型,可选vhd或managed_image
variable "output_type" {
  type        = string
  description = "指定构建输出类型,可选值: vhd, managed_image"
  default     = "managed_image"
}

source "azure-arm" "server_2019" {
  use_azure_cli_auth                               = true
  azure_tags                                       = local.tags
  build_resource_group_name                        = var.resource_group_name
  build_key_vault_name                             = "EXAMPLE-RGP-${var.environment}-KVT"
  os_type                                          = local.azure_sku.os_type
  image_publisher                                  = local.azure_sku.image_publisher
  image_offer                                      = local.azure_sku.image_offer
  image_sku                                        = local.azure_sku.image_sku
  vm_size                                          = local.azure_sku.vm_size
  shared_gallery_image_version_exclude_from_latest = false
  virtual_network_resource_group_name              = local.network.virtual_network_resource_group_name
  virtual_network_name                             = local.network.virtual_network_name
  virtual_network_subnet_name                      = local.network.virtual_network_subnet_name
  private_virtual_network_with_public_ip           = false
  communicator                                     = "winrm"
  winrm_use_ssl                                    = true
  winrm_insecure                                   = true
  winrm_timeout                                    = "3m"
  winrm_username                                   = "winrm"
  winrm_password                                   = var.winrm_password

  # VHD 配置:仅当output_type为vhd时生效
  resource_group_name    = var.output_type == "vhd" ? var.resource_group_name : null
  storage_account        = var.output_type == "vhd" ? lower("examplestorage${var.environment}blobevgimg") : null
  capture_container_name = var.output_type == "vhd" ? "evergreen-images" : null
  capture_name_prefix    = var.output_type == "vhd" ? "packer_server2019" : null

  # Managed Image 配置:仅当output_type为managed_image时生效
  managed_image_name                 = var.output_type == "managed_image" ? "packer_server2019" : null
  managed_image_resource_group_name  = var.output_type == "managed_image" ? var.resource_group_name : null
  managed_image_storage_account_type = var.output_type == "managed_image" ? "Standard_LRS" : null
  
  # 共享镜像库配置:仅当构建Managed Image时生效
  dynamic "shared_image_gallery_destination" {
    for_each = var.output_type == "managed_image" ? [true] : []
    content {
      resource_group       = var.resource_group_name
      gallery_name         = upper("GALLERY${var.environment}EVERGREENIMAGES")
      image_name           = "Evergreen-Images-Windows"
      image_version        = formatdate("YYYY.MM.DD", timestamp())
      replication_regions  = ["australiaeast", "australiasoutheast"]
      storage_account_type = "Standard_ZRS"
    }
  }
}

build {
  sources = [
    "sources.azure-arm.server_2019",
  ]

  provisioner "file" {
    source      = "../scripts/ConfigureRemotingForAnsible.ps1"
    destination = "C:\\temp\\ConfigureRemotingForAnsible.ps1"
  }

  provisioner "windows-update" {
    search_criteria = "IsInstalled=0"
    filters = [
      "exclude:$_.Title -like '*Preview*'",
      "include:$true",
    ]
    update_limit = 25
  }
}

运行方式:

  • 构建VHD:
    packer build -var "output_type=vhd" windows_server2019.pkr.hcl
    
  • 构建Managed Image:
    packer build -var "output_type=managed_image" windows_server2019.pkr.hcl
    

方案二:多Source块+变量选择构建目标

定义两个独立的Source块,分别对应VHD和Managed Image的配置,通过变量控制Build块中要运行的Source。

修改后的模板示例:

packer {
  required_plugins {
    windows-update = {
      version = "0.14.1"
      source  = "github.com/rgl/windows-update"
    }
  }
}

# 新增变量:选择构建目标
variable "build_target" {
  type        = string
  description = "选择构建目标,可选值: vhd, managed_image"
  default     = "managed_image"
}

# 用于VHD构建的Source块
source "azure-arm" "server_2019_vhd" {
  use_azure_cli_auth                               = true
  azure_tags                                       = local.tags
  build_resource_group_name                        = var.resource_group_name
  build_key_vault_name                             = "EXAMPLE-RGP-${var.environment}-KVT"
  os_type                                          = local.azure_sku.os_type
  image_publisher                                  = local.azure_sku.image_publisher
  image_offer                                      = local.azure_sku.image_offer
  image_sku                                        = local.azure_sku.image_sku
  vm_size                                          = local.azure_sku.vm_size
  shared_gallery_image_version_exclude_from_latest = false
  virtual_network_resource_group_name              = local.network.virtual_network_resource_group_name
  virtual_network_name                             = local.network.virtual_network_name
  virtual_network_subnet_name                      = local.network.virtual_network_subnet_name
  private_virtual_network_with_public_ip           = false
  communicator                                     = "winrm"
  winrm_use_ssl                                    = true
  winrm_insecure                                   = true
  winrm_timeout                                    = "3m"
  winrm_username                                   = "winrm"
  winrm_password                                   = var.winrm_password

  # VHD专属配置
  resource_group_name    = var.resource_group_name
  storage_account        = lower("examplestorage${var.environment}blobevgimg")
  capture_container_name = "evergreen-images"
  capture_name_prefix    = "packer_server2019"
}

# 用于Managed Image构建的Source块
source "azure-arm" "server_2019_managed_image" {
  use_azure_cli_auth                               = true
  azure_tags                                       = local.tags
  build_resource_group_name                        = var.resource_group_name
  build_key_vault_name                             = "EXAMPLE-RGP-${var.environment}-KVT"
  os_type                                          = local.azure_sku.os_type
  image_publisher                                  = local.azure_sku.image_publisher
  image_offer                                      = local.azure_sku.image_offer
  image_sku                                        = local.azure_sku.image_sku
  vm_size                                          = local.azure_sku.vm_size
  shared_gallery_image_version_exclude_from_latest = false
  virtual_network_resource_group_name              = local.network.virtual_network_resource_group_name
  virtual_network_name                             = local.network.virtual_network_name
  virtual_network_subnet_name                      = local.network.virtual_network_subnet_name
  private_virtual_network_with_public_ip           = false
  communicator                                     = "winrm"
  winrm_use_ssl                                    = true
  winrm_insecure                                   = true
  winrm_timeout                                    = "3m"
  winrm_username                                   = "winrm"
  winrm_password                                   = var.winrm_password

  # Managed Image专属配置
  managed_image_name                 = "packer_server2019"
  managed_image_resource_group_name  = var.resource_group_name
  managed_image_storage_account_type = "Standard_LRS"
  shared_image_gallery_destination {
    resource_group       = var.resource_group_name
    gallery_name         = upper("GALLERY${var.environment}EVERGREENIMAGES")
    image_name           = "Evergreen-Images-Windows"
    image_version        = formatdate("YYYY.MM.DD", timestamp())
    replication_regions  = ["australiaeast", "australiasoutheast"]
    storage_account_type = "Standard_ZRS"
  }
}

build {
  # 根据变量选择要运行的Source
  sources = var.build_target == "vhd" ? ["sources.azure-arm.server_2019_vhd"] : ["sources.azure-arm.server_2019_managed_image"]

  provisioner "file" {
    source      = "../scripts/ConfigureRemotingForAnsible.ps1"
    destination = "C:\\temp\\ConfigureRemotingForAnsible.ps1"
  }

  provisioner "windows-update" {
    search_criteria = "IsInstalled=0"
    filters = [
      "exclude:$_.Title -like '*Preview*'",
      "include:$true",
    ]
    update_limit = 25
  }
}

运行方式:

  • 构建VHD:
    packer build -var "build_target=vhd" windows_server2019.pkr.hcl
    
  • 构建Managed Image:
    packer build -var "build_target=managed_image" windows_server2019.pkr.hcl
    

内容的提问来源于stack exchange,提问作者philthy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 21:01:51