DEV Community

Anil KUMAR
Anil KUMAR

Posted on

Day 22: Two -Tier Architecture Setup On AWS

Today marks the Day 22 of 30 Days of AWS Terraform Challenge by Piyush Sachdeva. Today we will discuss about the designing and implementation of a secure, modular two-tier AWS architecture using Terraform. Think of this like a secure building with a public lobby and a high-security vault.

Architecture Flow:

The design follows a classic two-tier application model, implemented with security and scalability in mind:

┌─────────────────────────────────────────────────────────────┐
│                         VPC (10.0.0.0/16)                   │
│  ┌─────────────────────┐   ┌─────────────────────────────┐  │
│  │   Public Subnet     │   │     Private Subnets         │  │
│  │   (10.0.1.0/24)     │   │  (10.0.2.0/24, 10.0.3.0/24) │  │
│  │                     │   │                             │  │
│  │  ┌───────────────┐  │   │    ┌──────────────────┐     │  │
│  │  │  EC2 (Flask)  │──│───│───►│   RDS MySQL      │     │  │
│  │  │  Web Server   │  │   │    │   Database       │     │  │
│  │  └───────────────┘  │   │    └──────────────────┘     │  │
│  └─────────────────────┘   └─────────────────────────────┘  │
│           │                                                  │
│           ▼                                                  │
│  ┌─────────────────┐                                        │
│  │ Internet Gateway│                                        │
│  └─────────────────┘                                        │
└─────────────────────────────────────────────────────────────┘
           │
           ▼
      Internet (Users)
Enter fullscreen mode Exit fullscreen mode

Web Tier (Public)

  • EC2 instance running a Flask application
  • Deployed in a public subnet
  • Internet access via Internet Gateway
  • Listens on port 80

Data Tier (Private)

  • RDS MySQL instance
  • Deployed in private subnets across multiple AZs
  • No direct inbound internet access
  • Outbound connectivity via NAT Gateway (for patching, backups, etc.)

Secrets Management

  • AWS Secrets Manager stores database credentials
  • Terraform generates a strong random password
  • Secrets stored as JSON (username, password, engine, host)

Network & Security

  • Custom VPC with public and private subnets
  • NAT Gateway for private subnet outbound traffic
  • Tight security group rules enforcing least privilege

The Public Lobby (The Web Tier)

This is where the "front door" of the application lives. It hosts the website that users interact with.

What it is: A computer (EC2) running a simple website (Flask).

Security: It is open to the public so people can visit the site, but it is heavily guarded to only allow web traffic.

The Private Vault (The Data Tier)
This is where all the important information is stored.

What it is: A database (RDS) that holds all the user records.

Security: This area has no internet access. It is buried deep inside the network. The only way to get in is through the "Public Lobby." If a hacker tries to find the database from the outside, it simply doesn’t exist to them.

Secrets Manager:

No Written Passwords: I told the system to create a random, 16-character password automatically.

Hidden Keys: The password is kept in a digital safe. When the website starts up, it asks the safe for the key, uses it to talk to the database, and then throws the "memory" of it away. It’s never saved in a file where people can see it.

Modules:

To keep the code clean, reusable, and production-ready, the project is broken into custom Terraform modules. Each module owns a single responsibility.

secret
Generates a secure database password and stores credentials in AWS Secrets Manager

vpc
Provisions the VPC, public & private subnets, Internet Gateway, route tables, and NAT Gateway

security_group
Creates separate security groups for the web and database tiers

rds
Deploys a private RDS MySQL instance using credentials from Secrets Manager

ec2
Provisions the web server and deploys the Flask app via user data

The root module wires everything together using outputs and input variables.

Implementation:

Secure Secrets Handling:

resource "random_password" "db_password" {
  length           = 16
  special          = true
  override_special = "!#$%&*()-_=+[]{}<>:?"
}

resource "random_id" "suffix" {
  byte_length = 4
}

resource "aws_secretsmanager_secret" "db_password" {
  name        = "${var.project_name}-${var.environment}-db-password-${random_id.suffix.hex}"
  description = "Database password for ${var.project_name}"

  tags = {
    Name        = "${var.project_name}-db-password"
    Environment = var.environment
  }
}

resource "aws_secretsmanager_secret_version" "db_password" {
  secret_id = aws_secretsmanager_secret.db_password.id
  secret_string = jsonencode({
    username = var.db_username
    password = random_password.db_password.result
    engine   = "mysql"
    host     = "" # Will be populated by application or looked up
  })
}
Enter fullscreen mode Exit fullscreen mode

Instead of hardcoding credentials:

  • Terraform generates a 16-character random password resource.
  • Credentials are stored securely in AWS Secrets Manager
  • Secret payload is structured JSON

Only required outputs are passed to dependent modules, making communication easier between modules.

VPC, Subnets, and NAT Gateway:

We have used a custom module of vpc to create all this networking components.

  • Public subnets host the EC2 instance
  • Private subnets host the RDS instance.
  • Internet gateway for instance in Public subnet to access the Internet.

Security Groups:

We used this Security Groups module to make this project Least Privilege by Design.

Web Security Group

  • Allow HTTP (80) from anywhere
  • SSH restricted to a specific IP only

Database Security Group

  • Allow MySQL (3306) only from the web security group
  • No public exposure

Security groups reference each other instead of CIDR blocks — a cleaner and safer approach.

RDS Module:

We uses this module to create a RDS database.

  • Deployed across multiple private subnets
  • Not publicly accessible
  • Ingress limited strictly to web tier
  • Designed for availability and isolation

EC2 Module:

The EC2 instance uses a user data template to:

  • Install system dependencies
  • Deploy a Flask application
  • Inject database connection details

The app exposes:

  1. / – homepage
  2. /health – database connectivity check
  3. /db/info – database metadata

It performs basic insert and read operations to validate end-to-end connectivity.

Implementation:

terraform init
terraform plan
terraform apply
Enter fullscreen mode Exit fullscreen mode

RDS takes time to come up, so wait till RDS is up and running while executing terraform apply.

Validation Steps

After deployment:

  • Verified Flask app via EC2 public DNS
  • Tested /health and /db/info endpoints
  • Confirmed RDS had no public access
  • Checked Secrets Manager values

Conclusion:

This concludes the Mini project of 2 tier architecture using RDS, EC2 and Networking components.

Top comments (0)