Step-by-Step Guide: Building Custom Azure Images with Packer
Prerequisites
- The version used in this example: Packer v1.11.0
Assumptions
- You have created a resource group for the resource to be created in
- You have created an Azure Credential (App Registration) used to create the image
Install Packer
If you haven't already, install Packer on your machine.
- If using Chocolately use the command: choco install packer
Set up Azure credentials
Packer needs to authenticate with Azure. You can do this by setting the following environment variables:
export ARM_CLIENT_ID="your_azure_client_id"
export ARM_CLIENT_SECRET="your_azure_client_secret"
export ARM_TENANT_ID="your_azure_tenant_id"
export ARM_SUBSCRIPTION_ID="your_azure_subscription_id"
Replace the placeholders with your actual Azure credentials. Alternatively, you can define this in the variables file in the steps below.
Create Packer template
Create a file called azure-packer-template.pkr.hcl and copy the below code block
packer {
required_plugins {
azure = {
source = "github.com/hashicorp/azure"
version = "~> 2"
}
}
}
variable "client_id" {}
variable "client_secret" {}
variable "tenant_id" {}
variable "subscription_id" {}
variable "resource_group" {}
variable "image_name" {}
variable "location" {}
locals {
virtual_network_name = "syd-prd-net-vnetphip-001"
virtual_network_subnet_name = "syd-prd-net-subnetappphip-001"
virtual_network_resource_group_name = "syd-prd-cldfoud-netrgphip-001"
resource_group_name = "syd-prd-cldfoud-apprgphip-001"
}
source "azure-arm" "example" {
client_id = var.client_id
client_secret = var.client_secret
tenant_id = var.tenant_id
subscription_id = var.subscription_id
# build_resource_group_name = local.resource_group_name
managed_image_resource_group_name = local.resource_group_name
managed_image_name = var.image_name
os_type = "Windows"
image_publisher = "MicrosoftWindowsServer"
image_offer = "WindowsServer"
image_sku = "2022-datacenter-azure-edition"
location = var.location
vm_size = "Standard_DS2_v2"
communicator = "winrm"
winrm_username = "Packer_admin!2"
winrm_password = "p@cker551^%22"
winrm_timeout = "2h"
winrm_insecure = true
winrm_use_ssl = true
# virtual_network_name = local.virtual_network_name
# virtual_network_subnet_name = local.virtual_network_subnet_name
# virtual_network_resource_group_name = local.virtual_network_resource_group_name
}
The above values are explained below
The source
block in a Packer template defines the base configuration for the virtual machine that Packer will create. The "azure-arm"
part specifies that the source should be an Azure Resource Manager image and the "example"
part is just a name that you can choose.
client_id
,client_secret
,tenant_id
,subscription_id
: These fields are used for Azure authentication. You should replace the placeholders with your actual Azure credentials.os_type
: This field specifies the type of operating system. It can be either"Windows"
or"Linux"
.image_publisher
,image_offer
,image_sku
: These fields specify the base image that Packer will use to create the VM. You can find the values for these fields in the Azure Marketplace.location
: This field specifies the Azure location where the VM will be created.vm_size
: This field specifies the size of the VM.communicator
,winrm_username
,winrm_password
,winrm_timeout
,winrm_insecure
,winrm_use_ssl
: These fields are used to configure the WinRM communicator, which Packer uses to connect to the VM.
Optional:
virtual_network_name
: This is the name of the virtual network in Azure where you want to create the VM.virtual_network_subnet_name
: This is the name of the subnet within the specified virtual network where you want to create the VM.virtual_network_resource_group_name
: This is the name of the resource group that contains the virtual network.
I am using winrm to connect to the machine in this case. The last 3 lines in the source are optional. This is intended for working within a private network. By default, if you don't specify the network, then it will create a public IP associated with the packer image along with a temporary resource group and virtual network.
Important: You will also need to ensure you create an NSG rule to allow winrm port, otherwise it will timeout.
Create a variables file
Create a file named variables.hcl
in the same directory as your Packer template with the following content:
client_id = "your_azure_client_id"
client_secret = "your_azure_client_secret"
tenant_id = "your_azure_tenant_id"
subscription_id = "your_azure_subscription_id"
resource_group = "your_resource_group"
image_name = "your_image_name"
location = "your_azure_location"
Replace the placeholders with your actual Azure credentials, resource group, image name, and location.
Validate the Packer template
Run the following command to validate your Packer template:
packer validate -var-file .\packer.hcl .\azure-packer-template.pkr.hcl

Build the Packer image
If the validation is successful, run the following command to build the Packer image:
packer build -var-file .\packer.hcl .\azure-packer-template.pkr.hcl
This command will start the image-building process. Packer will create a temporary resource group and virtual machine in Azure, perform all the provisioning steps defined in your template, capture an image of the virtual machine, and then delete the temporary resources.
If all goes well, it will look like the below


Validate in the Portal to confirm

GitHub
Full code found here
Reference Links
