Introduction
Infrastructure as Code (IaC) tools like Terraform have revolutionized how we manage cloud resources. By writing declarative configurations, you can automate the provisioning of infrastructure and reduce the risk of manual errors. In this tutorial, we will walk you through setting up a simple infrastructure on AWS, including a VPC, subnets, an EC2 instance, and a load balancer, all managed by Terraform.
Prerequisites
Before you begin, ensure you have the following:
AWS Account: Access to AWS Management Console.
Terraform Installed: Download and install Terraform from terraform.io.
IAM User with Proper Permissions: A user with access to create VPC, EC2, S3, and other AWS resources.
AWS CLI Installed and Configured: Set up using
aws configure
with your IAM credentials.
Step-1. Initialize Terraform Project
Create a Working Directory
mkdir terraform-aws-setup cd terraform-aws-setup
Add Terraform Configuration Files
Create files:
main.tf
: Contains your resource definitions.variables.tf
: Defines input variables.outputs.tf
: Outputs values like Load Balancer DNS.
Initialize Terraform
terraform init
Step-2. Define AWS VPC and Subnets
The main.tf
file includes:
VPC with CIDR block
10.0.0.0/16
.Two Subnets in different availability zones.
Terraform Code:
resource "aws_vpc" "myvpc" {
cidr_block = var.cidr
}
resource "aws_subnet" "sub1" {
vpc_id = aws_vpc.myvpc.id
cidr_block = "10.0.0.0/24"
availability_zone = "us-east-1a"
map_public_ip_on_launch = true
}
resource "aws_subnet" "sub2" {
vpc_id = aws_vpc.myvpc.id
cidr_block = "10.0.1.0/24"
availability_zone = "us-east-1b"
map_public_ip_on_launch = true
}
Step-3. Set Up Networking
Internet Gateway provides internet access.
Route Table routes traffic through the Internet Gateway.
Terraform Code:
source "aws_internet_gateway" "igw" {
vpc_id = aws_vpc.myvpc.id
}
resource "aws_route_table" "RT" {
vpc_id = aws_vpc.myvpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw.id
}
}
resource "aws_route_table_association" "rta1" {
subnet_id = aws_subnet.sub1.id
route_table_id = aws_route_table.RT.id
}
resource "aws_route_table_association" "rta2" {
subnet_id = aws_subnet.sub2.id
route_table_id = aws_route_table.RT.id
}
Step-4. Configure Security Groups
Allows access to HTTP (port 80) and SSH (port 22) from anywhere.
Terraform Code:
resource "aws_security_group" "webSg" {
name = "web"
vpc_id = aws_vpc.myvpc.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 22
to_port = 22
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"]
}
}
Step-5. Add EC2 Instances
Creates two instances in separate subnets, each using a key pair for SSH access.
The user_data
script sets up Apache with custom HTML content.
Terraform Code:
resource "aws_key_pair" "mykey" {
key_name = "my-terraform-key"
public_key = file("~/.ssh/id_rsa.pub")
}
resource "aws_instance" "webserver1" {
ami = "ami-0866a3c8686eaeeba"
instance_type = "t2.micro"
vpc_security_group_ids = [aws_security_group.webSg.id]
subnet_id = aws_subnet.sub1.id
user_data = base64encode(file("userdata.sh"))
key_name = aws_key_pair.mykey.key_name
}
resource "aws_instance" "webserver2" {
ami = "ami-0866a3c8686eaeeba"
instance_type = "t2.micro"
vpc_security_group_ids = [aws_security_group.webSg.id]
subnet_id = aws_subnet.sub2.id
user_data = base64encode(file("userdata1.sh"))
key_name = aws_key_pair.mykey.key_name
}
Step-6. Configure Load Balancer
Application Load Balancer distributes traffic across instances.
Listener forwards HTTP requests to instances.
Terraform Code:
resource "aws_lb" "myalb" {
name = "myalb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.webSg.id]
subnets = [aws_subnet.sub1.id, aws_subnet.sub2.id]
}
resource "aws_lb_target_group" "tg" {
name = "myTG"
port = 80
protocol = "HTTP"
vpc_id = aws_vpc.myvpc.id
}
resource "aws_lb_target_group_attachment" "attach1" {
target_group_arn = aws_lb_target_group.tg.arn
target_id = aws_instance.webserver1.id
port = 80
}
resource "aws_lb_target_group_attachment" "attach2" {
target_group_arn = aws_lb_target_group.tg.arn
target_id = aws_instance.webserver2.id
port = 80
}
resource "aws_lb_listener" "listener" {
load_balancer_arn = aws_lb.myalb.arn
port = 80
protocol = "HTTP"
default_action {
target_group_arn = aws_lb_target_group.tg.arn
type = "forward"
}
}
Step-7. Outputs
Retrieve the Load Balancer DNS name for accessing the deployed infrastructure.
Terraform Code:
output "loadbalancerdns" {
value = aws_lb.myalb.dns_name
}
Steps to Deploy
Initialize Terraform:
terraform init
Validate the Configuration:
terraform validate
Apply the Configuration:
terraform apply
Confirm with
yes
.Access the Application:
Find the Load Balancer DNS in the output.
Visit:
http://<load-balancer-dns>
.
Clean Up
Destroy the infrastructure to avoid costs:
terraform destroy
Post-Deployment Best Practices
Security:
Restrict SSH (port 22) to trusted IPs.
Enable HTTPS for the Load Balancer.
Encrypt S3 bucket data and limit public access.
Monitoring:
Enable CloudWatch Logs and VPC Flow Logs.
Configure ALB health checks and set up alarms for anomalies.
Cost Optimization:
Downsize underutilized instances.
Use AWS Budgets to monitor spending.
Reliability:
Ensure ALB spans multiple Availability Zones.
Schedule backups and test failover mechanisms.
User Data:
Fix default Apache page issue by overwriting
/var/www/html/index.html
.Automate instance patching in
userdata.sh
.
Terraform State:
Store
terraform.tfstate
in S3 with DynamoDB locking.Regularly run
terraform plan
to detect drift.
Documentation:
- Create an architecture diagram and operational runbook.
These steps ensure your infrastructure is secure, cost-efficient, and reliable.
Conclusion
This setup provisions a robust AWS infrastructure using Terraform, including networking, compute instances, and a load balancer. It serves as a foundational template for deploying scalable applications.