Terraform Level 1, Task 8: Creating Custom Server Templates (AMIs)¶
Today's task was a fantastic demonstration of the power of Infrastructure as Code for creating reusable components. My objective was to create a custom Amazon Machine Image (AMI) from an existing EC2 instance. This is the foundation of building scalable and consistent server fleets in AWS.
This exercise was particularly insightful because it showed me how Terraform intelligently manages dependencies between resources. I had to define both the source EC2 instance and the resulting AMI in my code, and Terraform automatically understood the correct order of operations. This document is my very detailed, first-person guide to that entire process, breaking down the concepts and every line of code.
Table of Contents¶
- The Task
- My Step-by-Step Solution
- Why Did I Do This? (The "What & Why")
- Deep Dive: A Line-by-Line Explanation of My
main.tfScript - Common Pitfalls
- Exploring the Commands I Used
The Task¶
My objective was to use Terraform to create a new AWS AMI from an existing EC2 instance. The requirements were:
1. The source EC2 instance was named devops-ec2.
2. The new AMI needed to be named devops-ec2-ami.
3. The AMI had to be in an available state upon completion.
4. All the code had to be in a single main.tf file.
My Step-by-Step Solution¶
The process involved updating my existing Terraform file to include a new resource that depended on the first one, and then running the standard three-step workflow.
Phase 1: Writing the Code¶
In the /home/bob/terraform directory, I opened my main.tf file. It already contained the definition for the devops-ec2 instance. I added a new resource block to the end of the file.
# (Provider block and the aws_instance block were already here)
provider "aws" {
region = "us-east-1"
}
resource "aws_instance" "ec2" {
ami = "ami-0c101f26f147fa7fd"
instance_type = "t2.micro"
tags = {
Name = "devops-ec2"
}
}
# This is the new block I added to the file.
resource "aws_ami_from_instance" "devops_ec2_ami" {
name = "devops-ec2-ami"
source_instance_id = aws_instance.ec2.id
}
Phase 2: The Terraform Workflow¶
From my terminal in the same directory, I executed the three core commands.
-
Initialize: This ensured my provider plugins were up to date.
terraform init -
Plan: This "dry run" command was very insightful. It showed me that Terraform intended to create two resources: the
aws_instanceand theaws_ami_from_instance. It correctly identified the dependency between them.terraform plan -
Apply: This command executed the plan. After I confirmed with
yes, Terraform first launched the EC2 instance, waited for it to be running, and then initiated the AMI creation process.Creating an AMI takes a few minutes. Terraform patiently waited for the AMI to become "available" before reporting the final success message:terraform applyApply complete! Resources: 2 added, 0 changed, 0 destroyed.
Why Did I Do This? (The "What & Why")¶
- AMI (Amazon Machine Image): An AMI is a template for a server. It's a complete, bootable snapshot of a server's disk, including the operating system, all system configurations, and any software you've installed (like a web server, your application code, monitoring agents, etc.).
- "Golden Image" Strategy: The process of creating an AMI from a configured instance is the foundation of the "Golden Image" pattern. This is a core DevOps practice for a few key reasons:
1. Speed & Consistency: Instead of launching a bare server and running a long configuration script on it every time, I can do that work once, save it as a golden AMI, and then launch new instances from that AMI in seconds. Every new instance is a perfect, identical copy.
2. Scalability: When creating auto-scaling groups, you must provide an AMI that the group will use to automatically launch new servers when traffic increases. A pre-configured golden AMI ensures that new instances are ready to serve traffic almost instantly.
3. Backup & Recovery: Creating an AMI is a simple and effective way to create a full, point-in-time backup of an entire server.
- Terraform aws_ami_from_instance Resource: This is the specific Terraform resource that tells AWS to create a new AMI based on the current state of a running EC2 instance.
Deep Dive: A Line-by-Line Explanation of My main.tf Script¶
This script was a fantastic demonstration of Terraform's dependency management.
[Image of creating an AMI from a running EC2 instance]
# Standard provider configuration.
provider "aws" {
region = "us-east-1"
}
# The resource block that defines the source EC2 instance.
# I named it "ec2" locally within my Terraform code.
resource "aws_instance" "ec2" {
ami = "ami-0c101f26f147fa7fd"
instance_type = "t2.micro"
tags = {
Name = "devops-ec2"
}
}
# This is the new resource block that defines the AMI to be created.
# I named it "devops_ec2_ami" locally.
resource "aws_ami_from_instance" "devops_ec2_ami" {
# The 'name' argument sets the name of the AMI as it will appear in AWS.
name = "devops-ec2-ami"
# This is the most important line: The Dependency Link.
# The 'source_instance_id' argument requires the ID of an EC2 instance.
# I am providing that ID by referencing an attribute from the resource I defined above.
# The syntax is: <RESOURCE_TYPE>.<LOCAL_NAME>.<ATTRIBUTE>
# So, 'aws_instance.ec2.id' tells Terraform: "Go to the aws_instance resource
# named ec2, and get its 'id' attribute after it has been created."
source_instance_id = aws_instance.ec2.id
}
aws_ami_from_instance resource until the aws_instance resource has been successfully created and has an ID to provide. This is called an implicit dependency, and it's one of Terraform's most powerful features.
Common Pitfalls¶
- Source Instance Not Running: The EC2 instance you are creating an AMI from must be in a running state. If the instance fails to launch, the AMI creation will also fail.
- AMI Creation Time: Creating an AMI is not instant. It can take several minutes. When running terraform apply, it's important to be patient and let Terraform wait for the process to complete.
- Dangling Resources: If the aws_instance resource was defined in a separate Terraform state and I used a data source to find it, I would have to be careful. If I later destroyed the instance, the aws_ami_from_instance resource would now be pointing to a non-existent source, which could cause errors on the next plan or apply.
Exploring the Commands I Used¶
- terraform init: Prepared my working directory by downloading the aws provider plugin.
- terraform plan: Read my code, detected the implicit dependency, and showed me a correct plan to create the instance first, followed by the AMI.
- terraform apply: Executed the plan. It built the instance, waited, then built the AMI, all in the correct order, after I confirmed with yes.
- terraform destroy: While not used in the task, this would be the command to run to clean up. It would also be smart enough to destroy the AMI before destroying the instance it depends on.