DEV Community

Cover image for Building Golden AMIs with HashiCorp Packer: From 15 Minutes to 60 Seconds
Ahmed Belal
Ahmed Belal

Posted on

Building Golden AMIs with HashiCorp Packer: From 15 Minutes to 60 Seconds

πŸ€” The Problem

Every time you launch a new EC2 instance, you spend 10-15 minutes doing the same repetitive tasks:

  • Installing Git, AWS CLI, and development tools
  • Configuring security (firewall, SSH hardening)
  • Setting up monitoring agents
  • Applying system updates

This manual process leads to:

  • ❌ Inconsistent environments
  • ❌ Human errors
  • ❌ Wasted engineering time
  • ❌ Slow deployment cycles

πŸ’‘ The Solution: HashiCorp Packer

Packer is an Infrastructure as Code tool that automates machine image creation. Instead of manually configuring servers, you write code once and Packer builds identical, production-ready images automatically.

Why Packer?

βœ… Consistency - Same image across dev, staging, and production

βœ… Speed - Launch pre-configured servers in seconds

βœ… Version Control - Track image configurations in Git

βœ… Multi-Cloud - Build for AWS, Azure, GCP, VMware from one template

βœ… Immutable Infrastructure - Deploy once, never modify


πŸ› οΈ How Packer Works

Packer follows a simple workflow:

  1. Launch a temporary instance
  2. Provision it with your configurations
  3. Create an image snapshot
  4. Terminate the temporary instance

All automated, all repeatable.


πŸ“ Real-World Example: DevOps Golden AMI

Let me show you how I built a production-ready Ubuntu AMI with Packer.

Project Structure

packer-aws-devops-ami/
β”œβ”€β”€ aws/
β”‚   β”œβ”€β”€ ubuntu-devops-base.pkr.hcl       # Main template
β”‚   β”œβ”€β”€ variables.pkr.hcl                # Variables
β”‚   └── terraform.auto.pkrvars.hcl       # Values
β”œβ”€β”€ scripts/
β”‚   β”œβ”€β”€ 01-update-system.sh
β”‚   β”œβ”€β”€ 02-install-tools.sh
β”‚   β”œβ”€β”€ 03-install-aws-cli.sh
β”‚   β”œβ”€β”€ 04-install-cloudwatch-agent.sh
β”‚   β”œβ”€β”€ 05-install-ssm-agent.sh
β”‚   β”œβ”€β”€ 06-security-hardening.sh
β”‚   β”œβ”€β”€ 07-configure-auto-updates.sh
β”‚   └── 08-cleanup.sh
└── configs/
    β”œβ”€β”€ sshd_config
    └── cloudwatch-config.json
Enter fullscreen mode Exit fullscreen mode

Step 1: Define the Packer Template

File: aws/ubuntu-devops-base.pkr.hcl

packer {
  required_plugins {
    amazon = {
      version = ">= 1.2.8"
      source  = "github.com/hashicorp/amazon"
    }
  }
}

source "amazon-ebs" "ubuntu" {
  ami_name      = "${var.ami_name_prefix}-${formatdate("YYYY-MM-DD-hhmm", timestamp())}"
  instance_type = var.instance_type
  region        = var.aws_region

  source_ami_filter {
    filters = {
      name                = "ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"
      root-device-type    = "ebs"
      virtualization-type = "hvm"
    }
    most_recent = true
    owners      = [var.source_ami_owner]
  }

  ssh_username = var.ssh_username
  tags         = var.tags
}

build {
  sources = ["source.amazon-ebs.ubuntu"]

  provisioner "shell" {
    inline = ["cloud-init status --wait"]
  }

  provisioner "file" {
    source      = "../configs/sshd_config"
    destination = "/tmp/sshd_config"
  }

  provisioner "file" {
    source      = "../configs/cloudwatch-config.json"
    destination = "/tmp/cloudwatch-config.json"
  }

  provisioner "shell" {
    scripts = [
      "../scripts/01-update-system.sh",
      "../scripts/02-install-tools.sh",
      "../scripts/03-install-aws-cli.sh",
      "../scripts/04-install-cloudwatch-agent.sh",
      "../scripts/05-install-ssm-agent.sh",
      "../scripts/06-security-hardening.sh",
      "../scripts/07-configure-auto-updates.sh",
      "../scripts/08-cleanup.sh"
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Define Variables

File: aws/variables.pkr.hcl

variable "aws_region" {
  type        = string
  description = "AWS region to build AMI"
  default     = "eu-north-1"
}

variable "instance_type" {
  type        = string
  description = "EC2 instance type for building"
  default     = "t3.micro"
}

variable "ami_name_prefix" {
  type        = string
  description = "Prefix for AMI name"
  default     = "devops-base-ubuntu"
}

variable "source_ami_owner" {
  type        = string
  description = "AWS account ID of AMI owner"
  default     = "099720109477"
}

variable "ssh_username" {
  type        = string
  description = "SSH username"
  default     = "ubuntu"
}

variable "tags" {
  type = map(string)
  default = {
    Environment = "production"
    ManagedBy   = "Packer"
    Project     = "DevOps-Base-Image"
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Create Provisioning Scripts

Example: scripts/02-install-tools.sh

#!/bin/bash
set -e

echo "==> Installing core DevOps tools..."

sudo apt-get update
sudo apt-get install -y \
    git \
    curl \
    wget \
    vim \
    nano \
    unzip \
    tar \
    htop \
    tree \
    jq \
    net-tools \
    software-properties-common

echo "==> Core tools installed successfully"
Enter fullscreen mode Exit fullscreen mode

Example: scripts/06-security-hardening.sh

#!/bin/bash
set -e

echo "==> Applying security hardening..."

sudo apt-get install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw --force enable

sudo apt-get install -y fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban

sudo cp /tmp/sshd_config /etc/ssh/sshd_config
sudo chmod 644 /etc/ssh/sshd_config

echo "==> Security hardening completed"
Enter fullscreen mode Exit fullscreen mode

Step 4: Build the AMI

cd aws/
packer init .
packer validate .
packer build .
Enter fullscreen mode Exit fullscreen mode

Build Output

==> amazon-ebs.ubuntu: Launching a source AWS instance...
==> amazon-ebs.ubuntu: Waiting for instance to become ready...
==> amazon-ebs.ubuntu: Connected to SSH!
==> amazon-ebs.ubuntu: Provisioning with shell script: ../scripts/01-update-system.sh
==> amazon-ebs.ubuntu: Provisioning with shell script: ../scripts/02-install-tools.sh
==> amazon-ebs.ubuntu: Provisioning with shell script: ../scripts/03-install-aws-cli.sh
...
==> amazon-ebs.ubuntu: Creating AMI devops-base-ubuntu-2025-01-15-1430
==> amazon-ebs.ubuntu: AMI: ami-0123456789abcdef0
==> amazon-ebs.ubuntu: Terminating the source AWS instance...
Build 'amazon-ebs.ubuntu' finished after 6 minutes 32 seconds.

==> Builds finished. The artifacts of successful builds are:
--> amazon-ebs.ubuntu: AMIs were created:
eu-north-1: ami-0123456789abcdef0
Enter fullscreen mode Exit fullscreen mode

πŸ“Š Results

Metric Before After Improvement
Deployment Time 15 minutes 60 seconds 95% faster
Consistency Manual (errors) 100% identical βœ…
Security Varies Always hardened βœ…
Cost per Build N/A $0.01 Minimal

🎯 What's Included in the AMI

βœ… Ubuntu 22.04 LTS - Latest stable release

βœ… AWS CLI v2 - Cloud management

βœ… Git - Version control

βœ… CloudWatch Agent - Monitoring

βœ… SSM Agent - Remote management

βœ… Security Hardening - UFW firewall, Fail2ban, SSH hardening

βœ… Automatic Updates - Unattended security patches

βœ… DevOps Tools - curl, wget, vim, htop, tree, jq


πŸš€ Launch an Instance from Your AMI

aws ec2 run-instances \
  --image-id ami-0123456789abcdef0 \
  --instance-type t3.micro \
  --key-name your-key-pair \
  --security-group-ids sg-xxxxxx \
  --subnet-id subnet-xxxxxx \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=DevOps-Server}]'
Enter fullscreen mode Exit fullscreen mode

Result: Fully configured server ready in 60 seconds! πŸŽ‰


πŸ’‘ Best Practices

  1. Version Your Images - Use timestamps or semantic versioning in AMI names
  2. Automate in CI/CD - Build new AMIs on every commit
  3. Test Before Production - Launch test instances from new AMIs
  4. Clean Up Old AMIs - Deregister unused images to save costs
  5. Use Variables - Make templates reusable across environments
  6. Document Changes - Keep a changelog for image updates

πŸ”— Full Project

The complete project with all scripts and configurations is available on GitHub:

GitHub logo engabelal / packer-aws-devops-ami

Packer template to build hardened Ubuntu 22.04 AMI with DevOps tools, AWS CLI, CloudWatch Agent, and security configurations.

Packer AWS DevOps Base AMI

GitHub Repository: https://github.com/engabelal/packer-aws-devops-ami

Golden AMI with essential DevOps tools for AWS EC2 instances.


πŸ“– What is Packer?

Packer is an open-source tool by HashiCorp that automates the creation of machine images (AMIs, Docker images, VMware templates, etc.) across multiple platforms from a single source configuration.

Why Use Packer?

  • Consistency - Create identical machine images for dev, staging, and production
  • Speed - Pre-baked images launch in seconds vs. minutes of configuration
  • Version Control - Track image configurations in Git like any other code
  • Multi-Cloud - Build images for AWS, Azure, GCP, VMware from the same template
  • Automation - Integrate with CI/CD pipelines for automated image builds

What Can You Do With Packer?

βœ… Golden Images - Pre-configure OS, tools, and security settings βœ… Immutable Infrastructure - Deploy servers that never change after creation βœ… Faster Deployments - Launch fully-configured instances instantly βœ… Compliance - Ensure all…


πŸŽ“ Key Takeaways

  • Packer automates machine image creation across multiple cloud platforms
  • Golden AMIs reduce deployment time from minutes to seconds
  • Infrastructure as Code ensures consistency and repeatability
  • Security hardening can be baked into every image
  • Cost-effective - builds cost pennies and save hours

🀝 What's Next?

Try building your own Golden AMI! Start with:

  1. Clone the repository
  2. Customize the scripts for your needs
  3. Run packer build
  4. Launch instances from your AMI

Have questions? Drop them in the comments below! πŸ‘‡


Connect with me:

Top comments (0)