AWS with Terraform (Day 22)
Building a 2-Tier AWS Architecture with Terraform (EC2 + RDS)
Learning, Building, and Moving Forward as a DevOps Engineer
Day 22 of my DevOps journey is complete.
Despite being in an active job search phase and managing real-life responsibilities, I’m continuing to focus on hands-on, production-style cloud architecture. Today’s work was about designing and implementing a secure, modular two-tier AWS architecture using Terraform — the kind of setup that forms the backbone of many real-world applications.
This wasn’t a demo-only exercise. I pulled the code, extended it, and implemented a NAT Gateway with proper routing to enable secure outbound access for the database tier — exactly how it should be done in real environments.
Architecture Overview
The design follows a classic two-tier application model, implemented with security and scalability in mind:
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
Terraform Project Structure
To keep the code clean, reusable, and production-ready, the project is broken into custom Terraform modules. Each module owns a single responsibility.
Modules Used
secret
Generates a secure database password and stores credentials in AWS Secrets Managervpc
Provisions the VPC, public & private subnets, Internet Gateway, route tables, and NAT Gatewaysecurity_group
Creates separate security groups for the web and database tiersrds
Deploys a private RDS MySQL instance using credentials from Secrets Managerec2
Provisions the web server and deploys the Flask app via user data
The root module wires everything together using outputs and input variables.
Key Implementation Details
Secure Secrets Handling
Instead of hardcoding credentials:
Terraform generates a 16-character random password
Credentials are stored securely in AWS Secrets Manager
Secret payload is structured JSON:
{
"username": "app_user",
"password": "generated-secret",
"engine": "mysql",
"host": "rds-endpoint"
}
Only required outputs are passed to dependent modules, reducing blast radius.
VPC, Subnets, and NAT Gateway
Public subnets host the EC2 instance
Private subnets host the RDS instance
NAT Gateway enables outbound access from private subnets
Route tables are explicitly configured so MySQL traffic routes through NAT
This is a commonly missed step — without NAT and routing, private RDS instances often fail during patching or updates.
Security Groups – 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 in Private Subnets
Deployed across multiple private subnets
Not publicly accessible
Ingress limited strictly to web tier
Designed for availability and isolation
EC2 Application Deployment
The EC2 instance uses a user data template to:
Install system dependencies
Deploy a Flask application
Inject database connection details
The app exposes:
/– homepage/health– database connectivity check/db/info– database metadata
It performs basic insert and read operations to validate end-to-end connectivity.
Terraform Workflow Used
terraform init
terraform plan
terraform apply
terraform destroy
RDS provisioning takes time (often 10–15 minutes), so patience is part of the process.
Validation Steps
After deployment:
Verified Flask app via EC2 public DNS
Tested
/healthand/db/infoendpointsConfirmed RDS had no public access
Checked Secrets Manager values
Validated private subnet routing via NAT Gateway
Best Practices Reinforced
Use Terraform modules for clarity and reuse
Never expose databases publicly
Restrict SSH access aggressively
Always configure NAT for private subnet outbound access
Destroy non-production environments to manage cost
Review
terraform planbefore applying
Diagram
What’s Next
Future enhancements to this architecture:
Auto Scaling Group + Application Load Balancer
Runtime secret retrieval using IAM roles
Secrets Manager automatic rotation
Environment isolation using Terraform workspaces
Personal Note
I’m actively looking for a DevOps / Cloud Engineer role.
Even during a challenging phase, I’m staying consistent with:
Real AWS architecture work
Terraform best practices
Security-first design
Public documentation of learning
If your team values hands-on DevOps engineers who build, break, fix, and document, I’d love to connect.
Day 22 completed. Still building. Still learning. Still moving forward.
Here is the session link:
Comments
Post a Comment