Day 22 - Deploying an RDS MySQL Web Application Stack with Terraform Modules
Introduction
For Day 22 of my 30 Days of AWS Terraform challenge, I built a small but complete web application stack using Terraform modules.
The goal of this mini project was to deploy a Flask web application on an EC2 instance and connect it securely to an RDS MySQL database running in private subnets.
This project helped me understand how a real application stack is usually separated into networking, compute, database, and security layers.
Architecture
The architecture includes:
VPC with public and private subnets
EC2 instance in the public subnet
RDS MySQL database in private subnets
Security groups controlling traffic between EC2 and RDS
Internet Gateway for public access to the web server
This diagram should show users accessing the EC2 Flask application through the internet, while the EC2 instance connects privately to RDS MySQL.
Terraform Module Structure
I used a modular Terraform structure instead of putting everything into one large main.tf file.
The project was split into these modules:
VPC module
Security Groups module
RDS module
EC2 module
This screenshot should show the root Terraform files and the modules directory.
The root module calls each child module and passes the required values between them. For example, the VPC module creates the subnet IDs, the security group module creates the EC2 and RDS security groups, and the RDS module uses the private subnet IDs.
This makes the code cleaner, easier to troubleshoot, and easier to reuse in future projects.
VPC and Subnet Design
The VPC uses the CIDR block 10.0.0.0/16.
I created one public subnet for the EC2 web server and two private subnets for RDS. RDS requires a DB subnet group, and AWS recommends placing database subnets across multiple Availability Zones for better availability.
This screenshot should prove that the public and private subnets were created correctly.
Security Group Design
The web server security group allows:
HTTP on port 80
SSH on port 22
The database security group allows:
MySQL on port 3306 only from the EC2 security group
This is the most important security part of the project. The RDS database is not open to the internet. Only the EC2 instance can connect to it.
This screenshot should show port 3306 allowed only from the EC2 security group, not from 0.0.0.0/0.
RDS MySQL Deployment
The RDS MySQL database was deployed inside private subnets. This means users cannot connect to the database directly from the internet.
The Flask application connects to the database using the RDS endpoint, database name, username, and password passed through the EC2 user data script.
This screenshot should show the RDS instance status as Available.
This screenshot should show that RDS is using private subnets.
EC2 and Flask Application
The EC2 instance runs Ubuntu and installs the required packages during bootstrapping.
The user data script installs Python, Flask, MySQL client libraries, creates the Flask app, and runs it as a systemd service.
The application exposes these endpoints:
/ for the home page
/health for database connectivity check
/db-info for database information
This screenshot should show the Terraform apply output and application URL.
This screenshot should show the web application loading in the browser.
This screenshot should prove that the Flask application can connect to the RDS database.
Validation Commands
After deployment, I used Terraform outputs to get the application URL.
terraform output application_url
I also validated the RDS endpoint:
terraform output rds_endpoint
To verify the infrastructure:
terraform state list
To confirm the application from the browser:
Why Modular Terraform Matters
This project showed why Terraform modules are useful.
Instead of writing one large Terraform file, each layer has its own responsibility.
The VPC module handles networking.
The security group module handles access control.
The RDS module handles the database.
The EC2 module handles the application server.
This makes the project easier to read and maintain.
In real environments, this same pattern can be extended for production workloads with ALB, Auto Scaling Groups, Secrets Manager, CloudWatch, and private EC2 access through SSM Session Manager.
Security Improvements for Production
For a demo, this setup works well. For production, I would improve it by:
Restricting SSH to my IP or removing SSH completely
Using AWS Systems Manager Session Manager instead of SSH
Storing database credentials in AWS Secrets Manager
Enabling RDS encryption
Enabling automated backups
Adding CloudWatch logs and alarms
Using an Application Load Balancer instead of direct EC2 access
Moving the EC2 instance to a private subnet and exposing only the ALB
Cleanup
After completing the validation, I destroyed the resources to avoid unnecessary AWS charges.
Key Learnings
This project helped me understand how EC2 and RDS communicate securely inside a VPC. The biggest takeaway was that RDS should not be publicly exposed, and access should be controlled using security group references instead of open CIDR ranges. I also learned how Terraform modules make infrastructure easier to organize, reuse, and troubleshoot. This was a good step toward building production-style AWS application stacks.
Comments
Post a Comment