When deploying applications on AWS, manually setting up instances every time can be time-consuming. Instead, we can create a pre-configured Amazon Machine Image (AMI) using Packer and Ansible, allowing us to launch instances that are ready to go with minimal setup.
This article explains how to use Packer to build an AWS AMI and Ansible to configure the instance during the build process.
Why Use Packer and Ansible?
- Packer automates the AMI creation process, ensuring consistency and repeatability.
- Ansible allows us to configure the AMI with required software and settings in a declarative manner.
- Using both together helps create a golden image that is secure, optimized, and production-ready.
Repo: https://github.com/man20820/packer-ansible-nginx
Prerequisites
Before getting started, ensure you have the following:
- An AWS account with necessary permissions to create AMIs.
- Packer installed
- Ansible installed
- IAM Credentials configured via ~/.aws/credentials or environment variables.
Folder Structure
❯ tree . ├── files │ └── default ├── nginx.pkr.hcl └── nginx.yaml 1 directory, 3 files
Create the Ansible Playbook
on this article, i will create nginx for example. you can customize your needs..
nginx.yaml
---
- name: Install and Configure Nginx
hosts: all
become: yes # Run as sudo
tasks:
- name: Install Nginx on Debian-based systems
apt:
name: nginx
state: present
update_cache: yes
when: ansible_os_family == "Debian"
- name: Install Nginx on RHEL-based systems
yum:
name: nginx
state: present
when: ansible_os_family == "RedHat"
- name: Start and Enable Nginx Service
systemd:
name: nginx
state: started
enabled: yes
- name: Deploy Custom Nginx Configuration
copy:
src: files/default
dest: /etc/nginx/sites-available/default
owner: root
group: root
mode: '0644'
notify: Restart Nginx
handlers:
- name: Restart Nginx
systemd:
name: nginx
state: restarted
Create the Packer Template
packer {
required_plugins {
amazon = {
source = "github.com/hashicorp/amazon"
version = "~> 1.2.8"
}
ansible = {
version = ">= 1.1.2"
source = "github.com/hashicorp/ansible"
}
}
}
source "amazon-ebs" "custom-ami" {
ami_name = "nginx"
instance_type = "t3.micro"
region = "ap-southeast-3"
source_ami = "ami-0d22ac6a0e117cefe"
ssh_username = "ubuntu"
tags = {
Name = "nginx"
}
}
build {
sources = ["source.amazon-ebs.custom-ami"]
provisioner "ansible" {
playbook_file = "nginx.yaml"
user = "ubuntu"
}
}
Packer Init
❯ packer init nginx.pkr.hcl Installed plugin github.com/hashicorp/amazon v1.2.9 in "/home/man20820/.config/packer/plugins/github.com/hashicorp/amazon/packer-plugin-amazon_v1.2.9_x5.0_linux_amd64" Installed plugin github.com/hashicorp/ansible v1.1.2 in "/home/man20820/.config/packer/plugins/github.com/hashicorp/ansible/packer-plugin-ansible_v1.1.2_x5.0_linux_amd64"
Packer Validate
❯ packer validate nginx.pkr.hcl The configuration is valid.
Packer Build
❯ packer build nginx.pkr.hcl
amazon-ebs.custom-ami: output will be in this color.
==> amazon-ebs.custom-ami: Prevalidating any provided VPC information
==> amazon-ebs.custom-ami: Prevalidating AMI Name: nginx
amazon-ebs.custom-ami: Found Image ID: ami-0d22ac6a0e117cefe
==> amazon-ebs.custom-ami: Creating temporary keypair: packer_67a656ad-546e-a192-4ecb-cf375be35b6a
==> amazon-ebs.custom-ami: Creating temporary security group for this instance: packer_67a656ae-a0cb-d56c-06d6-d7efe4945837
==> amazon-ebs.custom-ami: Authorizing access to port 22 from [0.0.0.0/0] in the temporary security groups...
==> amazon-ebs.custom-ami: Launching a source AWS instance...
amazon-ebs.custom-ami: Instance ID: i-053db8a5cf0dda0ce
==> amazon-ebs.custom-ami: Waiting for instance (i-053db8a5cf0dda0ce) to become ready...
==> amazon-ebs.custom-ami: Using SSH communicator to connect: 108.137.130.187
==> amazon-ebs.custom-ami: Waiting for SSH to become available...
==> amazon-ebs.custom-ami: Connected to SSH!
==> amazon-ebs.custom-ami: Provisioning with Ansible...
amazon-ebs.custom-ami: Setting up proxy adapter for Ansible....
==> amazon-ebs.custom-ami: Executing Ansible: ansible-playbook -e packer_build_name="custom-ami" -e packer_builder_type=amazon-ebs --ssh-extra-args '-o IdentitiesOnly=yes' -e ansible_ssh_private_key_file=/tmp/ansible-key2513666039 -i /tmp/packer-provisioner-ansible4145009209 /home/man20820/workspace/Belajar/packer/nginx.yaml
amazon-ebs.custom-ami:
amazon-ebs.custom-ami: PLAY [Install and Configure Nginx] *********************************************
amazon-ebs.custom-ami:
amazon-ebs.custom-ami: TASK [Gathering Facts] *********************************************************
amazon-ebs.custom-ami: [WARNING]: Platform linux on host default is using the discovered Python
amazon-ebs.custom-ami: interpreter at /usr/bin/python3.12, but future installation of another Python
amazon-ebs.custom-ami: interpreter could change the meaning of that path. See
amazon-ebs.custom-ami: https://docs.ansible.com/ansible-
amazon-ebs.custom-ami: core/2.17/reference_appendices/interpreter_discovery.html for more information.
amazon-ebs.custom-ami: ok: [default]
amazon-ebs.custom-ami:
amazon-ebs.custom-ami: TASK [Install Nginx on Debian-based systems] ***********************************
amazon-ebs.custom-ami: changed: [default]
amazon-ebs.custom-ami:
amazon-ebs.custom-ami: TASK [Install Nginx on RHEL-based systems] *************************************
amazon-ebs.custom-ami: skipping: [default]
amazon-ebs.custom-ami:
amazon-ebs.custom-ami: TASK [Start and Enable Nginx Service] ******************************************
amazon-ebs.custom-ami: ok: [default]
amazon-ebs.custom-ami:
amazon-ebs.custom-ami: TASK [Deploy Custom Nginx Configuration] ***************************************
amazon-ebs.custom-ami: changed: [default]
amazon-ebs.custom-ami:
amazon-ebs.custom-ami: RUNNING HANDLER [Restart Nginx] ************************************************
amazon-ebs.custom-ami: changed: [default]
amazon-ebs.custom-ami:
amazon-ebs.custom-ami: PLAY RECAP *********************************************************************
amazon-ebs.custom-ami: default : ok=5 changed=3 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
amazon-ebs.custom-ami:
==> amazon-ebs.custom-ami: Stopping the source instance...
amazon-ebs.custom-ami: Stopping instance
==> amazon-ebs.custom-ami: Waiting for the instance to stop...
==> amazon-ebs.custom-ami: Creating AMI nginx from instance i-053db8a5cf0dda0ce
amazon-ebs.custom-ami: AMI: ami-0d11b275bef9afba6
==> amazon-ebs.custom-ami: Waiting for AMI to become ready...
==> amazon-ebs.custom-ami: Skipping Enable AMI deprecation...
==> amazon-ebs.custom-ami: Adding tags to AMI (ami-0d11b275bef9afba6)...
==> amazon-ebs.custom-ami: Tagging snapshot: snap-0d58bc0e8cabd1cb6
==> amazon-ebs.custom-ami: Creating AMI tags
amazon-ebs.custom-ami: Adding tag: "Name": "nginx"
==> amazon-ebs.custom-ami: Creating snapshot tags
==> amazon-ebs.custom-ami: Terminating the source AWS instance...
==> amazon-ebs.custom-ami: Cleaning up any extra volumes...
==> amazon-ebs.custom-ami: No volumes to clean up, skipping
==> amazon-ebs.custom-ami: Deleting temporary security group...
==> amazon-ebs.custom-ami: Deleting temporary keypair...
Build 'amazon-ebs.custom-ami' finished after 3 minutes 45 seconds.
==> Wait completed after 3 minutes 45 seconds
==> Builds finished. The artifacts of successful builds are:
--> amazon-ebs.custom-ami: AMIs were created:
ap-southeast-3: ami-0d11b275bef9afba6

Now we have new nginx ami…

Validate
Deploy EC2 using new ami

access the ip to test nginx configuration…

Thank you…