How to Learn Infrastructure as Code with Packer on AWS: Complete Step by Step Guide for Beginners
By Braincuber Team
Published on April 1, 2026
Infrastructure as Code (IaC) is the practice of managing and provisioning your infrastructure through code rather than manual configuration processes. Instead of clicking through cloud consoles or SSH-ing into servers to install software, you write configuration files that define exactly what your infrastructure should look like. This beginner guide will walk you through the complete journey from understanding IaC concepts to building a custom Amazon Machine Image (AMI) using HashiCorp Packer on AWS.
By the end of this complete tutorial, you will understand the difference between mutable and immutable infrastructure, know the five categories of IaC tools, and have a working custom AMI with Jenkins pre-installed that you can launch on AWS EC2. This step by step guide is designed for beginners with no prior IaC experience.
What You'll Learn:
- What Infrastructure as Code means and why it matters
- Mutable vs immutable infrastructure and their trade-offs
- Five categories of IaC tools with real-world examples
- How Packer works and its core building blocks
- Building a custom AMI with Jenkins baked in
- Launching and testing an EC2 instance from your custom image
Step 1: Understanding Infrastructure as Code
Infrastructure as Code (IaC) refers to the process of managing and supplying infrastructure using code rather than manual methods. The core concept is that you define, deploy, update, and destroy your infrastructure — servers, databases, networks, configurations — by writing and executing code files.
Think of it like writing a recipe. Instead of manually cooking each dish every time, you write down the exact ingredients and steps. Anyone (or any system) can follow that recipe and get the same result. IaC does the same for your infrastructure: you write the recipe once, and the code ensures every server, database, and network is set up identically every single time.
Traditional Approach
Manual server setup, clicking through consoles, SSH commands. Each server is slightly different. Hard to reproduce, hard to audit.
Infrastructure as Code
Configuration defined in files, version controlled, tested, and deployed automatically. Every server is identical. Easy to reproduce and audit.
Step 2: Mutable vs Immutable Infrastructure
Understanding the difference between mutable and immutable infrastructure is fundamental to grasping why tools like Packer exist and why they matter.
Mutable Infrastructure
Mutable infrastructure is infrastructure you can change and configure after it has been deployed. You log into servers, apply fixes, update configurations, install new software, and modify settings. This follows the Build, Deploy, Configure pattern.
For example, you deploy a web application to a cloud server. When a new feature or configuration change is needed, you SSH into that running server and update it. This works fine for a few servers, but as you scale, each server develops its own unique configuration — a problem known as configuration drift.
| Aspect | Benefits | Challenges |
|---|---|---|
| Speed | Quick updates and fixes on existing servers | Provisioning new servers is often a lengthy manual process |
| Customization | Each server can be tailored to exact application needs | Configuration drift makes servers inconsistent |
| Debugging | IT staff knows each server personally for faster diagnosis | Unique configurations make issues hard to reproduce |
| Tracking | Immediate changes without rebuilding | Server changes are not always recorded, version tracking is complex |
| Reliability | Works well for small-scale deployments | Update failures from network issues, unresponsive repositories, DNS downtime |
Immutable Infrastructure
In immutable infrastructure, components are recreated and replaced rather than updated after they are created. Instead of configuring a server after deployment, you configure it before deployment. Every new deployment means building a fresh image with all configurations baked in, then replacing the old server entirely.
This approach makes your infrastructure more consistent, reliable, and scalable. Tools like Packer implement this by creating pre-configured machine images that are ready to go the moment they launch.
| Aspect | Benefits | Challenges |
|---|---|---|
| Consistency | No configuration drift — no adjustments to make after deployment | Cannot change servers in place; must rebuild entirely |
| Versioning | Discrete versioning makes tracking and rollbacks significantly easier | Zero-day vulnerabilities require updating all servers with the same configuration |
| Testing | Uniform setups across servers make testing easier | Increased agility can conflict with standard IT security measures |
| Predictability | Infrastructure is never changed after creation, reducing complexity | Copying data between locations incurs overhead; data must be externalized |
Key Insight
Immutable infrastructure is the foundation of modern cloud-native deployments. By baking configurations into images before deployment, you eliminate configuration drift and make every server identical, predictable, and easy to replace.
Step 3: Five Categories of IaC Tools
Infrastructure as Code tools fall into five broad categories. Understanding these categories helps you choose the right tool for each part of your infrastructure workflow.
Ad Hoc Scripts
Custom scripts in Bash, Python, or any language to automate tasks, configure tools, and run commands on servers. The simplest starting point for IaC.
Configuration Management
Install and manage software on existing servers. Examples: Chef, Puppet, Ansible, SaltStack. Keep clusters homogeneous.
Orchestration Tools
Manage VMs and containers after creation: roll out updates, monitor health, distribute traffic. Examples: Kubernetes, Docker Swarm, Amazon ECS, Nomad.
Provisioning Tools
Create servers, databases, load balancers, SSL certificates. Examples: Terraform, CloudFormation, OpenStack Heat.
Server Templating Tools (Our Focus)
Server templating is a popular alternative to configuration management. Instead of launching servers and configuring them individually, you create a self-contained snapshot of the operating system, software, files, and all characteristics. You then deploy that image across all your servers.
Examples of server templating tools include Packer, Docker, and Vagrant. In this tutorial, we focus on Packer to create a custom Amazon Machine Image (AMI) for AWS.
Step 4: Understanding Packer and Core Building Blocks
Packer is a server templating tool by HashiCorp that creates and customizes machine images with specific applications pre-installed. These images are known as machine images, and each platform has its own format:
| Platform | Image Format | Description |
|---|---|---|
| Amazon | AMI (Amazon Machine Image) | Custom images for AWS EC2 instances |
| VMware | VMDK / VMX | Virtual disk and configuration files |
| VirtualBox | OVF (Open Virtualization Format) | Cross-platform virtual machine format |
Packer lets you generate custom machine images and bake your code and configuration into them before deploying to servers. For any subsequent updates, you destroy the old server and spin up a new one from the updated image, optionally using a load balancer to maintain availability during the transition.
Three Core Packer Building Blocks
Builders
Create the machine and generate the image across platforms. This is the most critical component. The builder block can take an array of different builders from different platforms, allowing you to produce identical images for AWS, Azure, and more from a single configuration.
Provisioners
Install, customize, and configure the machine image. They can use built-in provisioners or third-party tools. The shell provisioner is the most common, accepting scripts, inline commands, or file uploads.
Post-Processors
Run after the image has been built and customized. They can generate manifests, upload to registries, create Docker images, or perform any post-build action you need.
Step 5: Prerequisites and Installation
Before building your first custom AMI, you need to set up the following prerequisites:
AWS Account
Create an AWS account at aws.amazon.com/console. You will need access to the EC2 service.
IAM User with Access Keys
Create an IAM user and download the access key ID and secret access key. Packer needs these credentials to authenticate with AWS.
Packer Installed
Download and install Packer from packer.io. Verify installation by running packer version in your terminal.
Security Warning
Never commit AWS access keys to version control. Store them in a separate variables file and add that file to your .gitignore. We will show you exactly how to do this.
Step 6: Creating Your Packer Configuration Files
Create a project folder called packer_custom_image and inside it create the following files:
| File | Purpose |
|---|---|
packer.json | Main Packer configuration with builders, provisioners, and post-processors |
variable.json | Sensitive information (AWS keys) kept separate from the main config |
setup.sh | Shell script to install and configure Jenkins on the image |
.gitignore | Prevents sensitive files from being pushed to Git |
Step 6.1: Create variable.json
This file holds your sensitive AWS credentials and the source AMI ID. Keeping them separate from the main config means you can safely share packer.json without exposing your keys.
{
"description": "myWebServer",
"access_key": "enter-your-aws-access-key",
"secret_key": "enter-your-aws-secret-key",
"source_ami": "enter-your-source-ami-id"
}
Here is what each field means:
description
The name of the machine image you are creating. This helps you identify it later in the AWS console.
access_key and secret_key
Your IAM user credentials. Packer uses these for authentication and authorization with AWS.
source_ami
The base AMI you want to customize. AWS offers many options like Amazon Linux, Ubuntu, Windows, and Red Hat. We use the Amazon Linux AMI.
Finding Your Source AMI
To find the latest Amazon Linux AMI ID:
Log into AWS Console
Go to your AWS console, click Services, and search for EC2.
Click Launch Instance
We are not actually launching anything — we just need to find and copy the AMI ID.
Select Amazon Linux
Scroll to Application and OS Images (Amazon Machine Image). Choose Amazon Linux and copy the most recent AMI ID.
Important
Close the launch instance page after copying the AMI ID. Do not create or launch an instance at this stage. We only need the AMI ID for our Packer configuration.
Step 6.2: Create packer.json
This is the main configuration file. It defines the builder, provisioners, and post-processors. Let us build it section by section.
Builder Configuration:
{
"builders": [
{
"type": "amazon-ebs",
"access_key": "{{user `access_key`}}",
"secret_key": "{{user `secret_key`}}",
"region": "us-east-1",
"ami_name": "myfirstami",
"source_ami": "{{user `source_ami`}}",
"instance_type": "t2.micro",
"ssh_username": "ec2-user"
}
]
}
Here is what each builder field means:
| Field | Description |
|---|---|
type | Builder type. amazon-ebs creates an AMI backed by EBS volumes |
access_key | AWS access key fetched from variable.json using {{user `variable-name`}} format |
secret_key | AWS secret key fetched from variable.json for authentication |
region | AWS region where the AMI will be created (us-east-1 = N. Virginia) |
ami_name | Custom name for your resulting AMI |
source_ami | Base AMI to customize, fetched from variable.json |
instance_type | Temporary EC2 instance Packer spins up to build the image (t2.micro is free tier eligible) |
ssh_username | SSH username for the temporary instance. For Amazon Linux, this is ec2-user |
Step 6.3: Add Provisioners
Provisioners come after the builders block and take an array as well. We will use a shell provisioner to run our setup.sh script that installs Jenkins.
{
"builders": [ ... ],
"provisioners": [
{
"type": "shell",
"script": "setup.sh"
}
]
}
The shell provisioner is the most basic and widely used. It can accept:
script
Path to an external script file to run (what we are using)
inline
Commands to run directly on the command line
source
Files to upload to the image during provisioning
Step 6.4: Create setup.sh
This shell script installs Jenkins and its dependencies into our image. It runs on the temporary EC2 instance during the Packer build process.
sleep 30
sudo yum update -y
sudo wget -O /etc/yum.repos.d/jenkins.repo \
https://pkg.jenkins.io/redhat-stable/jenkins.repo
sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key
sudo yum upgrade
sudo amazon-linux-extras install java-openjdk11 -y
sudo yum install jenkins -y
sudo systemctl enable jenkins
sudo systemctl start jenkins
sudo systemctl status jenkins
What this script does step by step:
sleep 30
Wait 30 seconds for the temporary instance to fully initialize before running commands
yum update
Update all existing packages on the base Amazon Linux AMI
Add Jenkins Repository
Download and add the Jenkins stable repository and GPG key for package verification
Install Java and Jenkins
Install OpenJDK 11 (required by Jenkins), then install Jenkins itself
Enable and Start Jenkins
Enable Jenkins to start on boot, start the service, and check its status
Step 6.5: Add Post-Processors and Sensitive Variables
Post-processors run after the image is built. We will use a manifest post-processor to output the build artifacts. We also need to specify sensitive variables to exclude from the manifest.
{
"builders": [ ... ],
"provisioners": [ ... ],
"post-processors": [
{
"type": "manifest",
"output": "out.json"
}
],
"sensitive-variables": [
"secret_key",
"access_key"
]
}
The manifest post-processor prints the image architecture details after the build completes. The output file out.json will contain all build artifacts and metadata. The sensitive-variables field ensures your AWS keys are omitted from this output file.
Step 6.6: Create .gitignore
Add variable.json to your .gitignore file to prevent accidentally committing your AWS credentials:
variable.json
Step 7: Building the Custom AMI
Now it is time to run Packer and build your custom image. Open your terminal, navigate to the packer_custom_image directory, and run:
packer build -var-file="variable.json" packer.json
Here is what Packer does when you run this command:
Launches Temporary EC2 Instance
Packer creates a temporary t2.micro instance from your source AMI in the specified region
Runs Provisioners
SSH-es into the instance and executes setup.sh to install Jenkins and all dependencies
Creates AMI Snapshot
Takes a snapshot of the configured instance and registers it as a new AMI named myfirstami
Terminates Temporary Instance
Cleans up the temporary instance. You are only charged for the brief time it was running
Generates Manifest
Creates out.json and out.json.lock with build artifacts and metadata
Success Indicator
When the build completes successfully, you will see out.json and out.json.lock files created in your project directory. These contain your build artifacts and AMI ID.
Step 8: Verifying Your AMI in AWS Console
Now let us verify that your custom image was created successfully in the AWS console:
Log into AWS Console
Go to your AWS console, click Services, search for EC2, and scroll down to the Images section.
Click AMIs
Click on AMIs in the left sidebar. You should see your newly created myfirstami listed with a status of available.
AMI Status
If your AMI shows a status of pending, wait a few minutes. AMI creation takes time as AWS creates the EBS snapshot and registers the image. It will change to available when ready.
Step 9: Launching and Testing the EC2 Instance
Now let us launch an EC2 instance from your custom AMI to verify Jenkins is properly baked in.
Launch Instance from Your AMI
Select Your AMI
From the AMI page, select your recently created AMI and click Launch instance from AMI at the top right corner.
Name the Instance
Under Name and tags, give the instance a name like "My Jenkins Server".
Instance Type
Leave the instance type as the default t2.micro to use AWS free tier.
Create Key Pair
Under Key pair, click Create new key pair. Give it a name and choose .pem format (for Windows PowerShell or Mac/Linux) or .ppk (for PuTTY on Windows). The key file will download automatically — note the download location.
Configure Network Settings
Under Network settings, click Edit and scroll to Inbound security group rules. Add a custom TCP rule for port 8080 (Jenkins default) and allow access from anywhere (or restrict to your IP for better security). Also ensure SSH (port 22) is allowed.
Launch
Leave Configure storage and Advanced Details as defaults. Click Launch and wait for the instance to fully start.
SSH into Your Instance
Once the instance is running, note its Public DNS or Public IP from the instance details page. Open your terminal (PowerShell for Windows) and SSH in:
ssh -i /path/to/key-pair-name.pem ec2-user@instance-public-dns-name
Accept the host fingerprint prompt when it appears. Once connected, verify Jenkins is running:
sudo systemctl status jenkins
If Jenkins is not running, start it with:
sudo systemctl start jenkins
Access Jenkins in Your Browser
Open your favorite browser and navigate to:
http://YOUR_INSTANCE_PUBLIC_IP:8080
You should see the Jenkins unlock screen. To get the initial admin password, run this command inside your EC2 instance terminal:
sudo cat /var/lib/jenkins/secrets/initialAdminPassword
Copy the password, paste it into the Jenkins unlock screen, and follow the setup wizard to install suggested plugins and create your admin user. Your Jenkins instance is now fully operational!
Step 10: Clean Up Resources
To avoid unnecessary AWS charges, clean up your resources when you are done testing:
Terminate EC2 Instance
Go to the EC2 console, select your Jenkins server instance, and click Terminate instance.
Delete the AMI
Go to AMIs, select your custom image, and click Deregister AMI. Then delete the associated EBS snapshot to avoid storage charges.
Cost Reminder
Even stopped EC2 instances incur charges for their EBS volumes. Always terminate instances you no longer need, not just stop them. Deregistering the AMI and deleting its snapshot prevents ongoing storage costs.
Summary
In this complete tutorial, you learned:
What Infrastructure as Code means
Managing infrastructure through code files instead of manual processes, ensuring consistency and reproducibility.
Mutable vs Immutable Infrastructure
Mutable infrastructure is changed in place; immutable infrastructure is replaced entirely with fresh images for each update.
How to Build Custom AMIs with Packer
Using HashiCorp Packer to create a custom Amazon Machine Image with Jenkins pre-installed and configured.
How to Launch and Test on EC2
Deploying your custom AMI as an EC2 instance, SSH-ing in, and verifying Jenkins is running properly.
You can extend this tutorial by adding more software to your image, configuring security hardening, or integrating Packer with Terraform for full infrastructure provisioning. The possibilities are endless with Infrastructure as Code.
Next Steps
Try adding Nginx, Docker, or monitoring tools to your Packer image. Experiment with multi-builder configurations to produce identical images for AWS, Azure, and GCP from a single source file.
Frequently Asked Questions
What is the difference between mutable and immutable infrastructure?
Mutable infrastructure can be changed and configured after deployment (Build, Deploy, Configure pattern). Immutable infrastructure is recreated and replaced rather than updated — configurations are baked into images before deployment, eliminating configuration drift.
What is Packer used for in AWS?
Packer is a HashiCorp server templating tool that creates custom machine images (AMIs) for AWS. It bakes your software, configurations, and dependencies into the image before deployment, so every EC2 instance launched from it is identical and ready to use immediately.
How do I find the source AMI ID for Packer?
Log into the AWS Console, go to EC2, click Launch Instance, and scroll to Application and OS Images. Select your desired base image (e.g., Amazon Linux) and copy the AMI ID. Close the page without launching anything. You can also use the AWS CLI: aws ec2 describe-images.
Does building a Packer AMI cost money on AWS?
Packer creates a temporary EC2 instance (t2.micro in our example) to build the image, which is free tier eligible. The resulting AMI and its EBS snapshots incur small storage charges. Always terminate test instances and deregister unused AMIs to avoid ongoing costs.
How do I access Jenkins after launching from my custom AMI?
Open http://YOUR_EC2_PUBLIC_IP:8080 in your browser. Get the initial admin password by SSH-ing into the instance and running sudo cat /var/lib/jenkins/secrets/initialAdminPassword. Make sure your security group allows inbound traffic on port 8080.
Need Help with AWS Infrastructure?
Our experts can help you design, build, and deploy Infrastructure as Code solutions on AWS. From Packer AMIs to Terraform provisioning, we have you covered.
