Setting Up Infrastructure on AWS Using Terraform: A Step-by-Step Guide

I'm an IT professional and business analyst, sharing my day-to-day troubleshooting challenges to help others gain practical experience while exploring the latest technology trends and DevOps practices. My goal is to create a space for exchanging ideas, discussing solutions, and staying updated with evolving tech practices.
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 configurewith your IAM credentials.
Step-1. Initialize Terraform Project
Create a Working Directory
mkdir terraform-aws-setup cd terraform-aws-setupAdd 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 initValidate the Configuration:
terraform validateApply the Configuration:
terraform applyConfirm 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.tfstatein S3 with DynamoDB locking.Regularly run
terraform planto 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.




