⏱ Duration: 5 Hours
📚 Learning Objectives
- Create EC2 instances with Terraform
- Configure security groups for network access
- Use data sources to fetch AMI IDs
- Output useful information like public IPs
📖 Core Concepts (2 Hours)
Data Sources
Data sources allow Terraform to fetch information from AWS that already exists.
# Fetch the latest Amazon Linux 2023 AMI
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["al2023-ami-*-x86_64"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
# Get current AWS region
data "aws_region" "current" {}
# Get available AZs
data "aws_availability_zones" "available" {
state = "available"
}
# Use data sources
output "ami_id" {
value = data.aws_ami.amazon_linux.id
}
output "current_region" {
value = data.aws_region.current.name
}Security Groups
resource "aws_security_group" "web_sg" {
name = "web-server-sg"
description = "Security group for web server"
# Inbound rule - SSH
ingress {
description = "SSH from anywhere"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # In production, restrict this!
}
# Inbound rule - HTTP
ingress {
description = "HTTP from anywhere"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# Inbound rule - HTTPS
ingress {
description = "HTTPS from anywhere"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# Outbound rule - Allow all
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "web-server-sg"
}
}EC2 Instance
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux.id
instance_type = var.instance_type
key_name = var.key_name
vpc_security_group_ids = [aws_security_group.web_sg.id]
# User data script - runs on first boot
user_data = <<-EOF
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
echo "Hello from Terraform!
" > /var/www/html/index.html
EOF
tags = {
Name = "web-server"
Environment = var.environment
}
}
# Optional: Elastic IP for static public IP
resource "aws_eip" "web_eip" {
instance = aws_instance.web.id
domain = "vpc"
tags = {
Name = "web-server-eip"
}
}Key Pair for SSH
# Option 1: Reference existing key pair
variable "key_name" {
description = "Name of existing EC2 key pair"
type = string
default = "my-key-pair"
}
# Option 2: Create key pair from public key
resource "aws_key_pair" "deployer" {
key_name = "deployer-key"
public_key = file("~/.ssh/id_rsa.pub")
}
# Then use in EC2:
resource "aws_instance" "web" {
# ...
key_name = aws_key_pair.deployer.key_name
}Using count for Multiple Instances
variable "instance_count" {
default = 3
}
resource "aws_instance" "web" {
count = var.instance_count
ami = data.aws_ami.amazon_linux.id
instance_type = "t2.micro"
tags = {
Name = "web-server-${count.index + 1}"
}
}
# Output all IPs
output "instance_ips" {
value = aws_instance.web[*].public_ip
}🔬 Hands-on Lab (2.5 Hours)
Lab 1: Complete EC2 Web Server Project
# Create project
mkdir -p ~/terraform-labs/lab3-ec2
cd ~/terraform-labs/lab3-ec2
# providers.tf
cat > providers.tf << 'EOF'
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.region
}
EOF
# variables.tf
cat > variables.tf << 'EOF'
variable "region" {
description = "AWS region"
type = string
default = "us-east-1"
}
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t2.micro"
}
variable "environment" {
description = "Environment name"
type = string
default = "dev"
}
variable "project_name" {
description = "Project name for tagging"
type = string
default = "terraform-demo"
}
EOF
# data.tf
cat > data.tf << 'EOF'
# Get latest Amazon Linux 2023 AMI
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["al2023-ami-*-x86_64"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
# Get default VPC
data "aws_vpc" "default" {
default = true
}
EOF
# security_group.tf
cat > security_group.tf << 'EOF'
resource "aws_security_group" "web" {
name = "${var.project_name}-web-sg"
description = "Security group for web server"
vpc_id = data.aws_vpc.default.id
ingress {
description = "SSH"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "HTTP"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.project_name}-web-sg"
Environment = var.environment
}
}
EOF
# main.tf
cat > main.tf << 'EOF'
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux.id
instance_type = var.instance_type
vpc_security_group_ids = [aws_security_group.web.id]
user_data = <<-EOF
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
# Create a nice HTML page
cat > /var/www/html/index.html << 'HTML'
Terraform Demo
🎉 Hello from Terraform!
This EC2 instance was created using Infrastructure as Code
Instance ID: $(curl -s http://169.254.169.254/latest/meta-data/instance-id)
HTML
EOF
tags = {
Name = "${var.project_name}-web-server"
Environment = var.environment
ManagedBy = "Terraform"
}
}
EOF
# outputs.tf
cat > outputs.tf << 'EOF'
output "instance_id" {
description = "ID of the EC2 instance"
value = aws_instance.web.id
}
output "instance_public_ip" {
description = "Public IP of the EC2 instance"
value = aws_instance.web.public_ip
}
output "instance_public_dns" {
description = "Public DNS of the EC2 instance"
value = aws_instance.web.public_dns
}
output "security_group_id" {
description = "ID of the security group"
value = aws_security_group.web.id
}
output "ami_id" {
description = "AMI ID used"
value = data.aws_ami.amazon_linux.id
}
output "web_url" {
description = "URL to access the web server"
value = "http://${aws_instance.web.public_ip}"
}
EOFLab 2: Deploy and Test
# Initialize
terraform init
# Format and validate
terraform fmt
terraform validate
# Plan
terraform plan
# Apply
terraform apply -auto-approve
# Get outputs
terraform output
terraform output web_url
# Test the web server (wait a minute for user_data to complete)
curl $(terraform output -raw instance_public_ip)
# View in browser
echo "Visit: $(terraform output -raw web_url)"
# Check EC2 in AWS Console or CLI
aws ec2 describe-instances \
--filters "Name=tag:Name,Values=terraform-demo-web-server" \
--query 'Reservations[].Instances[].{ID:InstanceId,IP:PublicIpAddress,State:State.Name}'
# IMPORTANT: Destroy to avoid charges!
terraform destroy -auto-approve✅ Day 3 Checklist
- Can use data sources to fetch AMIs
- Understand security group configuration
- Can create EC2 instances with user_data
- Know how to output useful information
- Successfully deployed web server with Terraform