Multi-Cloud Images and HCP Packer¶
One of Packer's selling points is producing equivalent images across multiple clouds from a single codebase. HCP Packer is HashiCorp's managed registry that tracks, promotes, and audits those images.
One Template, Many Clouds¶
A single build block can reference multiple sources across different providers. All run in parallel.
packer {
required_plugins {
amazon = {
source = "github.com/hashicorp/amazon"
version = "~> 1.3"
}
azure = {
source = "github.com/hashicorp/azure"
version = "~> 2.0"
}
googlecompute = {
source = "github.com/hashicorp/googlecompute"
version = "~> 1.1"
}
}
}
source "amazon-ebs" "ubuntu" {
region = "us-east-1"
instance_type = "t3.micro"
ami_name = "ubuntu-${local.timestamp}"
# ...
}
source "azure-arm" "ubuntu" {
managed_image_name = "ubuntu-${local.timestamp}"
location = "East US"
vm_size = "Standard_DS2_v2"
# ...
}
source "googlecompute" "ubuntu" {
project_id = var.gcp_project
zone = "us-central1-a"
image_name = "ubuntu-${local.timestamp}"
# ...
}
build {
name = "ubuntu-multi-cloud"
sources = [
"source.amazon-ebs.ubuntu",
"source.azure-arm.ubuntu",
"source.googlecompute.ubuntu",
]
provisioner "shell" {
inline = ["sudo apt-get update && sudo apt-get install -y nginx"]
}
}
One packer build produces three images. The same provisioner runs on each. Fast and DRY.
Challenges in Multi-Cloud Builds¶
- Different usernames per AMI: Ubuntu on AWS is
ubuntu; on Azure may be any set user; on GCP may bepackeror any user. - Different package availability: cloud-specific agents (cloud-init, SSM, Azure Linux Agent) vary.
- Different default disk layouts: first boot behavior differs.
- Different networking: VPCs, VNets, VPC Networks all behave slightly differently.
- Quotas and regions: each cloud has its own capacity limits.
Keep provisioners cloud-agnostic where possible. Use conditionals or only / except filters for cloud-specific steps:
provisioner "shell" {
only = ["amazon-ebs.ubuntu"]
inline = ["sudo yum install -y amazon-ssm-agent"]
}
Multi-Region Within One Cloud¶
For a single cloud, multi-region is often handled by builder-specific arguments (e.g., ami_regions for AWS) rather than separate sources. This produces a single AMI lineage replicated across regions, with the same ID semantics.
For GCP, images are global by default. For Azure, images can be global via Shared Image Galleries.
HCP Packer Overview¶
HCP Packer is HashiCorp Cloud Platform's managed image registry. It tracks:
- Buckets: logical containers for image lineages (e.g., "ubuntu-base")
- Iterations: individual builds; each tagged with metadata
- Artifacts: the actual cloud-side images within an iteration (per region, per platform)
- Channels: named pointers to iterations (e.g., "production" -> iteration 42)
Think git analogy: bucket is a repo, iterations are commits, channels are branches or tags.
Authenticating to HCP Packer¶
Create an HCP service principal, grab its client ID and secret, set env vars:
export HCP_CLIENT_ID=xxx
export HCP_CLIENT_SECRET=yyy
Optionally export HCP_PROJECT_ID=zzz if multiple projects.
The hcp-packer-registry Post-Processor¶
post-processor "hcp-packer-registry" {
bucket_name = "ubuntu-base"
description = "Ubuntu 22.04 LTS base image"
bucket_labels = {
"os" = "ubuntu-22.04"
"maintained_by" = "platform-team"
}
build_labels = {
"build-timestamp" = local.timestamp
"build-commit" = var.git_commit
}
}
Behavior:
- Creates the bucket if it doesn't exist (on first run)
- Creates a new iteration for this build
- Records artifacts per source (AMI IDs, Azure image IDs, GCP images)
- Records provenance metadata (who built, when, from what commit)
Channels¶
Channels are pointers to iterations. They are how downstream consumers resolve "the current production Ubuntu image" without hardcoding iteration IDs.
Typical channels:
latest: automatically follows the most recent successful iteration (opt-in)development: manually set by dev teamstaging: set after QAproduction: set after approval
Promotion is a single API call or UI click: change the channel's pointer to a new iteration.
Consuming HCP Packer in Terraform¶
data "hcp_packer_artifact" "ubuntu" {
bucket_name = "ubuntu-base"
channel_name = "production"
platform = "aws"
region = "us-east-1"
}
resource "aws_instance" "web" {
ami = data.hcp_packer_artifact.ubuntu.external_identifier
instance_type = "t3.micro"
}
At plan time, Terraform queries HCP Packer for the artifact pointed to by the production channel. Promote a new iteration and the next Terraform plan picks up the new AMI.
Iteration Statuses¶
- Scheduled: created, build not started
- Running: build in progress
- Cancelled: manually cancelled
- Failed: build failed
- Done: build succeeded
- Revoked: marked unfit for use; consumers may refuse to deploy
Revocation is used to flag a vulnerable or broken image without deleting it. Consumers can opt to fail if they resolve to a revoked iteration.
Restoring a Revoked Iteration¶
If revocation was wrong (false alarm), restore via UI or API. The iteration returns to done status.
Image Immutability¶
HCP Packer does not prevent you from deleting the underlying AMI in AWS. If you delete the AMI, consumers pointing to that iteration will fail. Use AWS AMI deregistration protection or Glue code that keeps iterations and AMIs in sync.
Ancestry¶
HCP Packer tracks parent-child relationships: if your build uses a base image from another HCP Packer bucket (via data.hcp-packer-artifact in Packer), it records the lineage. The UI shows ancestry graphs for audit.
Compliance Views¶
HCP Packer Plus includes a "compliance" view showing which channels reference which iterations across buckets, helping you audit whether production is running approved images.
No-Code Patterns for Image Consumption¶
Pair HCP Packer with Terraform's no-code modules:
- A module uses
data.hcp_packer_artifactwith parameterizedchannel_name - Mark the module as "no-code ready" in the private registry
- Consumers launch workspaces via UI, selecting a channel
- End result: a VM provisioned from the latest approved image without writing HCL
Artifacts vs External Identifiers¶
Within HCP Packer:
artifact_id: HCP's internal IDexternal_identifier: the cloud provider's ID (AMI ID, Azure image resource ID, GCP image self-link)
Consumers almost always use external_identifier.
Alternatives to HCP Packer¶
- AWS Systems Manager Parameter Store: store AMI IDs under a path; consumers read via data source. Simple, but no channels, ancestry, or compliance.
- Custom database or git repo: same idea with a home-grown registry. Maintenance cost.
- AMI tagging conventions: tag AMIs with
Environment=production; consumers filter. Works for simple cases.
HCP Packer's value is the full audit trail and channel-based promotion flow. For basic use cases, parameter store may be enough.
Multi-Cloud Pipelines in Practice¶
A mature pipeline typically:
- Builds images for each target cloud in parallel
- Tags and registers with HCP Packer
- Runs smoke tests (Terratest, InSpec) against each artifact
- On pass, promotes to
stagingchannel - After acceptance tests, promotes to
production - Old iterations marked revoked after N days
Exam-Ready Checklist¶
- Can author a build with multiple cloud sources
- Know when to use
only/exceptfor cloud-specific provisioners - Understand HCP Packer buckets, iterations, channels, artifacts
- Can configure
hcp-packer-registrypost-processor - Can consume HCP Packer in Terraform via
data.hcp_packer_artifact - Understand revocation and ancestry
- Know
HCP_CLIENT_IDandHCP_CLIENT_SECRETfor auth