Building a Two-Tier Architecture with Terraform: My Hands-On Experience
As a cloud enthusiast delving into Infrastructure as Code (IaC), I recently embarked on creating a two-tier architecture using Terraform. This project was both a challenge and a valuable learning experience, teaching me about efficient infrastructure design, optimizing Terraform modules, and securing cloud resources. In this blog, I’ll walk you through my journey, highlighting the challenges, solutions, and insights I gained along the way.
What is a Two-Tier Architecture?
A two-tier architecture separates the application layer from the database layer, enhancing scalability, security, and maintainability. For this project, I focused on creating:
Application Layer: Hosted by an Auto Scaling Group (ASG) managing EC2 instances.
Load Balancer Layer: An Application Load Balancer (ALB) directing incoming traffic to healthy instances.
This setup ensures high availability and scalability while maintaining a simple architecture.
Project Overview
Here’s a snapshot of my architecture:
S3 Backend: Used for Terraform state management.
VPC with Public Subnets: All resources were deployed in public subnets, secured through well-configured security groups.
Load Balancer: An ALB distributing traffic to application servers.
Auto Scaling Group: Dynamically managing application servers to ensure consistent performance.
.
Next we move to creating a VPC with two public subnets, each in a separate availability zone:
#Create VPC
resource "aws_vpc" "main" {
cidr_block = var.cidr_block
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = var.vpc_name
}
}
#Create Subnets
resource "aws_subnet" "public" {
count = length(var.public_subnets)
vpc_id = aws_vpc.main.id
cidr_block = var.public_subnets[count.index]
availability_zone = var.availability_zones[count.index]
map_public_ip_on_launch = true
tags = {
Name = "${var.vpc_name}-public-${count.index}"
}
}
The configuration included an internet gateway and route tables to enable internet access.
#### **Step 2: Configuring the Auto Scaling Group (ASG)**
The ASG dynamically created and managed EC2 instances, simplifying scaling:
hcl
resource "aws_launch_template" "lt" {
name_prefix = var.name_prefix
image_id = var.image_id
instance_type = var.instance_type
network_interfaces {
associate_public_ip_address = true
security_groups = var.security_groups
}
user_data = base64encode(var.user_data)
}
#### **Step 3: Adding a Load Balancer**
The ALB ensured efficient traffic distribution and health monitoring:
hcl
resource "aws_lb" "alb" {
name = var.name
internal = false
load_balancer_type = "application"
security_groups = var.security_groups
subnets = var.subnets
}
#### **Step 4: Securing Resources**
Security groups restricted access to critical resources:
hcl
Create Security Group for ALB
resource "aws_security_group" "alb_sg" {
name = "alb-sg"
description = "Security group for the Application Load Balancer"
vpc_id = aws_vpc.main.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # Allow HTTP traffic from the internet
}
ingress {
from_port = 443
to_port = 443
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 = "sg-alb"
}
}
Create Security Group for ASG
resource "aws_security_group" "asg_sg" {
name = "asg-sg"
description = "Security group for EC2 instances in the ASG"
vpc_id = aws_vpc.main.id
ingress {
description = "Allow HTTP traffic from ALB"
from_port = 80
to_port = 80
protocol = "tcp"
security_groups = [aws_security_group.alb_sg.id] # Reference ALB SG
}
egress {
description = "Allow all outbound traffic"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "asg-sg"
}
}
The ALB accepted HTTP traffic, while EC2 instances only allowed traffic from the ALB.
#### **Step 5: Deploying the Infrastructure**
While the above codes are just snipets.[Clone](https://github.com/Otumiky/scalable-web-app) the main repo to proceed with the steps below.
**change directory to dev and configure the root main.tf**
bash
cd envs/dev
I used the following commands to deploy the setup:
1. **Initialize Terraform:**
bash
terraform init
2. **Plan the Infrastructure:**
bash
terraform plan
3. **Apply the Configuration:**
bash
terraform apply
The output included the ALB DNS name, which was used for testing.
#### **Step 6: Testing the Setup**
I tested the deployment by accessing the ALB DNS name in a browser, confirming that the traffic was correctly routed to the application servers.
---
### **Screenshots of AWS Components**
*(Include screenshots here, such as VPC and subnets, ALB configuration, and target groups, with captions explaining their relevance.)*
---
### **Challenges and Solutions**
1. **Redundant EC2 Module:**
Initially, I used a standalone EC2 module alongside the ASG, which caused redundancy. Removing it simplified the design and avoided potential conflicts.
2. **Security Concerns with Public Subnets:**
Deploying resources in public subnets required meticulous security group configurations to balance accessibility and security.
---
### **Key Learnings**
1. **Optimize Module Usage:** Avoid redundancy by understanding each module's functionality.
2. **Security Matters:** Even in public subnets, effective security group configuration can ensure safety.
3. **Iterate and Improve:** Testing and refining designs are critical for effective infrastructure management.
---
### **Conclusion**
Building this two-tier architecture with Terraform was a rewarding journey that enhanced my understanding of cloud architecture and IaC best practices. I hope this blog inspires you to take on similar projects and simplifies your path to mastering Terraform.
Find the complete Terraform code in my [GitHub repository](https://github.com/Otumiky/scalable-web-app). Feel free to connect on LinkedIn or leave a comment below with any questions or feedback!
---
SOCIAL SHARE CARD GENERATOR