Packer Multi‑Platform Builds: Consistent Cloud Images
Stop managing separate image templates for AWS and Azure. Learn how to use Packer’s multi‑platform builds to keep your images consistent across clouds.
30 May 2026, 06:15 UTC

The Problem: The “Works on AWS, Fails on Azure” Image
Maintaining separate image templates for different cloud providers is a recipe for configuration drift. When you manage one HCL file for AWS AMIs and another for Azure Managed Images, a security patch or a middleware update added to one is often forgotten in the other. This leads to inconsistent environments where the application behaves differently depending on the underlying cloud provider.
The solution is to decouple the provisioning logic (what goes inside the OS) from the builder configuration (how the cloud provider creates the VM). By using Packer’s multi‑platform build capability, you can define a single source of truth for your software stack and deploy it across multiple clouds simultaneously.
Decoupling Builders from Provisioners
In Packer, a builder is the component responsible for creating a machine image for a specific platform. A provisioner is the tool that installs software or configures the OS. The power of multi‑platform builds lies in the fact that provisioners are decoupled from builders.
When you define multiple builders within a single build block, Packer orchestrates the creation of temporary instances across all specified providers. Once those instances are reachable via SSH or WinRM, Packer applies the same set of provisioners to every instance. This ensures that the exact same version of Nginx, Java, or your custom agent is installed regardless of whether the target is an Amazon Machine Image (AMI) or an Azure VHD.
Managing Platform Differences with Variables
While the software stack remains the same, cloud providers require different metadata. AWS needs a region and instance_type; Azure needs a location and vm_size. To keep the configuration clean, use HCL variables to abstract these platform‑specific identifiers.
By defining these in a .pkrvars.hcl file, you can maintain a generic template that remains portable across different accounts or regions without modifying the core build logic.
Example: Unified Build for AWS and Azure
The following configuration demonstrates how to target both AWS and Azure using a shared shell script for installation. This assumes you have the amazon-linux-2 and ubuntu-22.04 images as sources, and that your shell script handles the package manager differences.
# Run this on your local machine with appropriate cloud credentials exported.
# Required: packer init . (to install aws and azure-arm plugins)
variable "app_version" {
type = string
default = "1.2.0"
}
source "amazon-ebs" "aws-ubuntu" {
ami_name = "app-server-{{timestamp}}"
instance_type = "t3.micro"
region = "us-east-1"
source_ami = "ami-xxxxxxxxxxxx" # Ubuntu 22.04 AMI
ssh_username = "ubuntu"
}
source "azure-arm" "azure-ubuntu" {
vm_size = "Standard_B1s"
location = "East US"
os_type = "Linux"
image_publisher = "Canonical"
image_offer = "0001-com-ubuntu-server-jammy"
image_sku = "22_04-lts"
}
build {
sources = [
"source.amazon-ebs.aws-ubuntu",
"source.azure-arm.azure-ubuntu"
]
provisioner "shell" {
inline = [
"echo 'Installing App Version ${var.app_version}'",
"sudo apt-get update",
"sudo apt-get install -y nginx"
]
}
}
Execution and Verification
To execute this build, run the following command from your terminal:
packer build -debug .
Permissions: You must have ec2:RunInstances and ec2:CreateImage permissions for AWS, and Contributor or a custom image‑creation role for the Azure Resource Group.
Verification: Check the output logs for the Provisioning phase. You should see two concurrent SSH connections being established—one to the AWS instance and one to the Azure instance—followed by the same shell commands being executed on both.
The “OS Trap”: A Critical Limitation
The biggest risk in multi‑platform builds is assuming the base OS is identical across providers. While you can use Ubuntu on both AWS and Azure, the default cloud‑init configurations, network interface naming (e.g., eth0 vs ens3), and pre‑installed utilities often differ.
If your provisioner relies on a specific path or a package manager (like yum for Amazon Linux vs apt for Ubuntu), the build will fail on one of the platforms. To solve this, your provisioner scripts must include conditional logic to detect the OS:
if [ -f /etc/debian_version ]; then
sudo apt-get install -y nginx
elif [ -f /etc/redhat-release ]; then
sudo yum install -y nginx
fi
Actionable Summary
To implement multi‑platform builds effectively:
- Standardize the Base OS: Use the same Linux distribution (e.g., Ubuntu 22.04) across all providers to minimize provisioner complexity.
- Validate First: Run
packer validate .to catch HCL syntax errors before triggering expensive cloud instances. - Use Variable Files: Keep environment‑specific IDs in
.pkrvars.hclto ensure the main template remains a reusable blueprint. - Audit the Result: Manually spin up one instance from each resulting image to verify that the software is functioning identically across clouds.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.