π€ 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:
- Launch a temporary instance
- Provision it with your configurations
- Create an image snapshot
- 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
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"
]
}
}
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"
}
}
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"
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"
Step 4: Build the AMI
cd aws/
packer init .
packer validate .
packer build .
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
π 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}]'
Result: Fully configured server ready in 60 seconds! π
π‘ Best Practices
- Version Your Images - Use timestamps or semantic versioning in AMI names
- Automate in CI/CD - Build new AMIs on every commit
- Test Before Production - Launch test instances from new AMIs
- Clean Up Old AMIs - Deregister unused images to save costs
- Use Variables - Make templates reusable across environments
- Document Changes - Keep a changelog for image updates
π Full Project
The complete project with all scripts and configurations is available on GitHub:
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:
- Clone the repository
- Customize the scripts for your needs
- Run
packer build
- Launch instances from your AMI
Have questions? Drop them in the comments below! π
Connect with me:
- GitHub: @engabelal
- LinkedIn: linkedin.com/in/engabelal
Top comments (0)