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

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


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:

  1. AWS Account: Access to AWS Management Console.

  2. Terraform Installed: Download and install Terraform from terraform.io.

  3. IAM User with Proper Permissions: A user with access to create VPC, EC2, S3, and other AWS resources.

  4. AWS CLI Installed and Configured: Set up using aws configure with your IAM credentials.


Step-1. Initialize Terraform Project

  1. Create a Working Directory

     mkdir terraform-aws-setup
     cd terraform-aws-setup
    
  2. Add Terraform Configuration Files

    • Create files:

  3. 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

  1. Initialize Terraform:

     terraform init
    
  2. Validate the Configuration:

     terraform validate
    
  3. Apply the Configuration:

     terraform apply
    

    Confirm with yes.

  4. 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

  1. Security:

    • Restrict SSH (port 22) to trusted IPs.

    • Enable HTTPS for the Load Balancer.

    • Encrypt S3 bucket data and limit public access.

  2. Monitoring:

    • Enable CloudWatch Logs and VPC Flow Logs.

    • Configure ALB health checks and set up alarms for anomalies.

  3. Cost Optimization:

    • Downsize underutilized instances.

    • Use AWS Budgets to monitor spending.

  4. Reliability:

    • Ensure ALB spans multiple Availability Zones.

    • Schedule backups and test failover mechanisms.

  5. User Data:

    • Fix default Apache page issue by overwriting /var/www/html/index.html.

    • Automate instance patching in userdata.sh.

  6. Terraform State:

    • Store terraform.tfstate in S3 with DynamoDB locking.

    • Regularly run terraform plan to detect drift.

  7. 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.