What it does
Packer starts a temporary VM from a base image, runs provisioners (shell scripts, Ansible, file uploads) to install and harden it, then saves the result as a new image (Azure Managed Image / Compute Gallery, AWS AMI, qcow2). Terraform then creates servers from that image. This is the immutable infrastructure idea: you replace servers with a new image instead of patching them by hand.
Templates are written in HCL (*.pkr.hcl) with source (where to build) and build (what to do) blocks.
How to write it
packer init|fmt|validate|build [OPTIONS] TEMPLATE_OR_DIRMain parts
Commands
initDIR- Install the plugins in
required_plugins. fmtDIR- Format templates.
validateDIR- Check templates.
buildDIR- Build the image(s).
inspectDIR- Show the parts of a template.
console- Try expressions.
pluginsinstall|installed- Manage plugins.
Options 8
Most used
build flags
Nothing matches this filter.
Examples 10
$ run as a normal user# run as root (or with sudo)
1The workflow
packer init . packer fmt . packer validate . packer build .2An Azure image template
cat rhel-base.pkr.hcl packer { required_plugins { azure = { source = "github.com/hashicorp/azure", version = "~> 2" } ansible = { source = "github.com/hashicorp/ansible", version = "~> 1" } } } source "azure-arm" "rhel" { use_azure_cli_auth = true location = "westeurope" vm_size = "Standard_B2s" os_type = "Linux" image_publisher = "RedHat" image_offer = "RHEL" image_sku = "9-lvm-gen2" managed_image_name = "rhel9-base-${formatdate("YYYYMMDD", timestamp())}" managed_image_resource_group_name = "rg-images" } build { sources = ["source.azure-arm.rhel"] provisioner "ansible" { playbook_file = "harden.yml" } provisioner "shell" { inline = ["sudo dnf -y update", "sudo /usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync"] } }3Build with variables
packer build -var-file=prod.pkrvars.hcl .4Build one source only
packer build -only=azure-arm.rhel .5Keep the VM when a step fails, to debug
packer build -on-error=ask .6Build a local QEMU image for the lab
packer build -only=qemu.rhel9 .7Use the image in Terraform
cat vm.tf data "azurerm_image" "base" { name_regex = "^rhel9-base-" sort_descending = true resource_group_name = "rg-images" }8Scan the built image in CI
trivy vm --scanners vuln azure:///subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-images/providers/Microsoft.Compute/images/rhel9-base-202610279Check the template parts
packer inspect .10Rebuild every month in a pipeline
cat .github/workflows/image.yml on: { schedule: [{ cron: "0 3 1 * *" }] }
Nothing matches this filter.
Watch out
- Remove SSH keys, shell history and machine IDs at the end (deprovision), or every server shares them.
- Do not bake secrets into images — fetch them at boot (Key Vault, cloud-init).
Files
*.pkr.hcl- Templates.
*.pkrvars.hcl- Variable values.
Tips to remember
- Version images by date and keep a few old ones for rollback.
Exit codes
0 | Success. |
1 | Error. |