Automating AMI Creation with Packer and CI/CD

Packer AWS AMI CI/CD Automation

Custom Amazon Machine Images (AMIs) are essential for maintaining consistency across your EC2 infrastructure. Packer, combined with a CI/CD pipeline, enables automated, repeatable AMI creation.

Why Use Packer?

Manual AMI creation is error-prone and time-consuming. Packer provides:

  • Infrastructure as Code: AMI configuration in version control
  • Reproducibility: Same configuration always produces the same result
  • Multi-cloud support: Build images for AWS, Azure, GCP, and more
  • Integration: Works seamlessly with CI/CD pipelines

Basic Packer Template Structure

Here’s a simple Packer template for an Amazon Linux 2023 AMI:

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

source "amazon-ebs" "al2023" {
  ami_name      = "custom-al2023-{{timestamp}}"
  instance_type = "t3.micro"
  region        = var.region
  source_ami_filter {
    filters = {
      name                = "al2023-ami-*-x86_64"
      root-device-type    = "ebs"
      virtualization-type = "hvm"
    }
    most_recent = true
    owners      = ["amazon"]
  }
  ssh_username = "ec2-user"
}

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

  provisioner "shell" {
    scripts = [
      "scripts/update-system.sh",
      "scripts/install-tools.sh",
      "scripts/configure-services.sh"
    ]
  }
}

CI/CD Integration

Azure DevOps Pipeline Example

trigger:
  branches:
    include:
      - main
  paths:
    include:
      - src/al-2023/*

stages:
  - stage: Build
    jobs:
      - job: PackerBuild
        steps:
          - task: PackerTool@0
            inputs:
              version: '1.12.0'
          
          - script: |
              packer validate -var-file=env/lab.pkrvars.hcl src/al-2023
            displayName: 'Validate Packer Template'
          
          - script: |
              packer build -var-file=env/lab.pkrvars.hcl src/al-2023
            displayName: 'Build AMI'
            env:
              AWS_ACCESS_KEY_ID: $(AWS_ACCESS_KEY_ID)
              AWS_SECRET_ACCESS_KEY: $(AWS_SECRET_ACCESS_KEY)

Managing AMI Versions

Use SSM Parameter Store to track the latest AMI:

# Extract AMI ID from Packer manifest
ami_id=$(jq -r '.builds[-1].artifact_id|split(":")[1]' manifest.json)

# Update SSM parameter
aws ssm put-parameter \
  --name "/ami/al-2023/latest" \
  --value $ami_id \
  --type String \
  --overwrite

Best Practices

1. Use Session Manager Instead of SSH

Configure Packer to use SSM Session Manager for provisioning:

source "amazon-ebs" "al2023" {
  # ... other settings
  communicator                 = "ssh"
  ssh_interface                = "session_manager"
  iam_instance_profile         = "packer-builder-profile"
}

2. Encrypt AMIs

Always encrypt your AMIs:

source "amazon-ebs" "al2023" {
  # ... other settings
  encrypt_boot = true
  kms_key_id   = var.kms_key_id
}

3. Tag Your AMIs

Apply consistent tags for tracking:

source "amazon-ebs" "al2023" {
  # ... other settings
  tags = {
    Name        = "Custom AL2023"
    Environment = var.environment
    BuildDate   = "{{timestamp}}"
    Version     = var.version
  }
}

4. Implement Automated Testing

Test your AMI after creation:

# Launch test instance
instance_id=$(aws ec2 run-instances \
  --image-id $ami_id \
  --instance-type t3.micro \
  --query 'Instances[0].InstanceId' \
  --output text)

# Run tests
# ...

# Terminate test instance
aws ec2 terminate-instances --instance-ids $instance_id

Conclusion

Automating AMI creation with Packer and CI/CD ensures consistent, secure, and versioned machine images. This approach reduces human error, speeds up deployments, and provides a clear audit trail of infrastructure changes.

Start small with a basic template and gradually add more sophistication as your needs grow.