Deploying a Highly Available Web Application on AWS with Terraform: A Step-by-Step Guide

Deploying a Highly Available Web Application on AWS with Terraform: A Step-by-Step Guide

As organizations embrace cloud-native technologies, one of the critical components for achieving scalability and efficiency is implementing Infrastructure as Code (IaC). Among the many tools available, Terraform has gained significant popularity for provisioning infrastructure across various cloud providers. This blog post will delve into how to deploy a highly available and scalable web application on AWS using Terraform. We’ll walk through the steps required, provide necessary code snippets, and share best practices to ensure a robust infrastructure setup.

Why Terraform?

Terraform is an open-source IaC tool developed by HashiCorp. It enables you to define cloud infrastructure in declarative configuration files. With Terraform, you can provision, update, and version your infrastructure safely and efficiently, taking advantage of its state management and extensive support for multiple cloud platforms.

Setting Up Your Environment

Before you start, ensure you have the following prerequisites:

  • AWS account with necessary permissions
  • Terraform installed on your machine (version 0.13 or later)
  • Basic knowledge of AWS services like EC2, VPC, and Auto Scaling groups

Step-by-Step Guide to Deploy a Web Application Using Terraform

1. Configure AWS Access

Configure AWS CLI with your credentials. Run the following command and follow the prompts:

aws configure

2. Define Provider Configuration

Create a directory for your Terraform configuration files. In this directory, create a file named main.tf and define the provider configuration:

provider "aws" {
  region = "us-west-2"
}

3. Create a VPC

A Virtual Private Cloud (VPC) will host your infrastructure. Add the following code to main.tf to create a VPC:

resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "main_vpc"
  }
}

4. Create Subnets

Create public and private subnets within the VPC:

resource "aws_subnet" "public" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "10.0.1.0/24"
  map_public_ip_on_launch = true
  availability_zone       = "us-west-2a"

  tags = {
    Name = "public_subnet"
  }
}

resource "aws_subnet" "private" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.2.0/24"
  availability_zone = "us-west-2a"

  tags = {
    Name = "private_subnet"
  }
}

5. Create an Internet Gateway

Public subnets require an Internet Gateway for outbound traffic:

resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "main_igw"
  }
}

6. Create Route Tables

Associate the Internet Gateway with the public subnet routing table:

resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.igw.id
  }

  tags = {
    Name = "public_rt"
  }
}

resource "aws_route_table_association" "public_assoc" {
  subnet_id      = aws_subnet.public.id
  route_table_id = aws_route_table.public.id
}

7. Create Security Groups

Define security groups to control inbound and outbound traffic:

resource "aws_security_group" "web_sg" {
  vpc_id = aws_vpc.main.id

  ingress {
    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 = "web_sg"
  }
}

8. Launch EC2 Instances

Use an Auto Scaling group to ensure high availability and scalability:

resource "aws_launch_configuration" "web_lc" {
  name          = "web-launch-configuration"
  image_id      = "ami-0c55b159cbfafe1f0" // Amazon Linux 2 AMI
  instance_type = "t2.micro"

  security_groups = [aws_security_group.web_sg.id]
  
  user_data = <<-EOF
              #!/bin/bash
              yum update -y
              yum install -y httpd
              systemctl start httpd
              systemctl enable httpd
              echo "Hello, World" > /var/www/html/index.html
            EOF

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_autoscaling_group" "web_asg" {
  launch_configuration = aws_launch_configuration.web_lc.id
  min_size             = 2
  max_size             = 5
  desired_capacity     = 2
  vpc_zone_identifier  = [aws_subnet.public.id]

  tag {
    key                 = "Name"
    value               = "web_asg_instance"
    propagate_at_launch = true
  }

  lifecycle {
    create_before_destroy = true
  }
}

9. Output the Application URL

Finally, output the application URL for easy access:

output "app_url" {
  value = "http://${aws_autoscaling_group.web_asg.instances[0].public_dns}"
}

Deploying Your Infrastructure

With your Terraform configuration ready, follow these steps to deploy your infrastructure:

1. Initialize Terraform

terraform init

2. Create an Execution Plan

terraform plan

3. Apply the Changes

terraform apply

Best Practices and Lessons Learned

1. Maintain State Files Securely

Store Terraform state files in a remote backend like AWS S3 with state locking enabled to prevent concurrent edits.

2. Modularize Configurations

Break down your Terraform configurations into reusable modules for better organization and maintainability.

3. Use Version Control

Track changes to your Terraform configuration files using a version control system like Git to ensure auditability and collaboration.

4. Validate Configurations

Regularly validate your configurations using terraform validate and apply linting tools to maintain code quality.

Conclusion

Terraform empowers teams to manage and provision infrastructure efficiently by adopting Infrastructure as Code. By following this guide, you can deploy a highly available and scalable web application on AWS with ease. Have you utilized Terraform in your cloud-native projects? Share your experiences and tips in the comments below!

Read more