SIMON CULL
Cloud & Security Engineer

Production-Grade AWS Three-Tier Architecture with Terraform

Highly available, scalable, and secure three-tier application infrastructure across three availability zones — built with Terraform, incorporating defense-in-depth security controls including NACLs, VPC Flow Logs, SSM Session Manager, GuardDuty and AWS Config.



Services Used

AWS Services: VPC, Subnets (public/private/isolated), Internet Gateway, NAT Gateway, Route Tables, Network ACLs, VPC Flow Logs, VPC Endpoints (Gateway + Interface), EC2, ECS (EC2-backed), ECR, Auto Scaling Groups, Application Load Balancer (external + internal), RDS Aurora (Multi-AZ, read replicas, autoscaling), ElastiCache (Redis), EFS, S3, CloudFront, WAF, Shield, Route 53, ACM, SSM Session Manager, SQS, CloudWatch, SNS, KMS, IAM, GuardDuty, AWS Config, CloudTrail, Secrets Manager, Security Hub IaC / DevOps: Terraform, Terraform remote state (S3 + DynamoDB locking) Protocols / Concepts: HTTPS/TLS (443), HTTP (80), MySQL (3306), Redis (6379), Flask/app port (5000), SSL termination, Defense-in-depth, Multi-AZ HA, Least privilege IAM, Encryption at rest + in transit, Stateful vs stateless firewalling (SGs vs NACLs) Demo stack: Nginx, EC2 user data (instance metadata), bash scripting DNS / Certs: Route 53, ACM


Steps Taken

Provider, region and statement to allow access to all availability zones in region.

VPC with CIDR block of 10.0.0.0/16, within the RFC 1918 ranges of IP addresses reserved for private networks.

I used the Terraform cidrsubnet() function to dynamically generate the subnets. The '8' value means 8 bits are added to the prefix length, resulting in '/24' subnets. The count value specifies how many subnets to create, and when used in the function, which subnets to create.

The public route table provides a route to the internet using the internet gateway, it is then explicitly associated with the public subnets. The private route table provides a route to the internet by using the NAT gateway in the public subnet. The private route table is then explicitly associated with the private subnets.

Internet Gateway in the VPC; NAT Gateway in a public subnet; the NAT Gateway requires a static, elastic IP.

The auto scaling group requires a launch template. The launch template defines which machine image to use for the created EC2 instance and which security group to assign to it. In the public launch template, the instances will be automatically assigned public IP addresses. The auto scaling desired capacity is set to 2. The vpc_zone_identifier statement specifies the available subnets created using the cidrsubnet function.

In the private auto scaling group the vpc_zone_identifier statement specifies the first and second of the created private subnets. The third and fourth private subnets will be used for the RDS database.

The load balancers for the public and private subnets are both Application Load Balancers, used for distributing HTTP and HTTPS traffic across multiple targets, including EC2 instances. The public load balancer serves internet-facing traffic, the private load balancer serves internal traffic.

These security group rules configure inbound and outbound network traffic for the public EC2 instances. Inbound, the rules permit HTTP traffic on port 80 from any IP address (0.0.0.0/0) and SSH traffic on port 22 from the CIDR block of the VPC. Outbound, the rules allow all traffic (all ports and protocols) to any destination.

These security group rules for private EC2 instances significantly enhance security compared to the prior configuration by restricting both SSH and HTTP access exclusively to the VPC's CIDR block. This isolates instances from the public internet, reducing the attack surface. Outbound traffic remains open, allowing instances to initiate outbound connections to the internet through the NAT Gateway.

This sets up two MySQL database instances on AWS RDS, along with the necessary networking components. The RDS subnet group consists of the third and fourth of the private subnets. Each instance has 20GB of gp2 storage, runs MySQL 8.0.40, uses the db.t3.micro instance class, is named "mydb", uses the username "admin" and a password of "password" (not recommended).

The security group rules allow inbound TCP traffic on port 3306 (the default MySQL port) from within the VPC’s CIDR block, and all outbound traffic. This code creates two small, identical MySQL database servers for development or testing, properly secured and placed within the VPC.




Selection of infrastructure running in AWS Console after running Terraform apply

After running 'terraform init / plan / apply' in the CLI, the infrastructure is built within 4-5 minutes
VPC layout
Private and public subnets
Private and public load balancers
NAT Gateway with Elastic IP
RDS instances
RDS security group
Public security group
Private security group
Run 'terraform destroy' in the CLI and the entire created infrastructure is removed