Project 7 of 12 · project brief
AWS Infrastructure with Terraform
VPC, EC2 behind an ALB, RDS, S3, IAM, and CloudWatch, built as reusable Terraform modules with remote state and dev/prod environments.
The scenario
ShopLite moves to AWS. Before containers, the team wants the classic three-tier architecture: the API on an Auto Scaling group of EC2 instances behind an Application Load Balancer, PostgreSQL on RDS, uploads on S3, and monitoring in CloudWatch. Everything must be code, reviewable, and reproducible in two environments.
Before you start
- AWS course
IAM, VPC, EC2, ALB, RDS, S3, CloudWatch.
- Terraform course
The build-along covers most of this project step by step.
Stack
Target architecture
Deliverables and requirements
You will hand in
- Terraform modules:
network,app(ASG + ALB),database,storage,monitoring envs/devandenvs/produsing the modules with different sizes- Remote state in S3 with locking; CI that runs fmt/validate/plan on PRs
- CloudWatch dashboard and alarms (5xx rate, target health, RDS CPU/storage)
Functional
- API reachable via the ALB DNS name; unhealthy instances replaced automatically
- Instances have no SSH keys or public IPs; access via SSM Session Manager
- App reads the DB password from Secrets Manager and uploads to S3 via its instance role
Non-functional
- Everything tagged (owner, env, service)
- RDS encrypted, private, Multi-AZ in prod, backups on
terraform planon a clean checkout shows no changes (no drift)- Least-privilege IAM (no
*:*)
Milestones
- 1
State and network
Done when: Remote state bootstrapped; a VPC with public/private subnets across 2+ AZs.
- Bootstrap an S3 state bucket (versioned, encrypted) with state locking
networkmodule: VPC, subnets, IGW, NAT per AZ, route tables- Outputs for subnet IDs
Prove it works
terminal$ terraform -chdir=envs/dev apply && terraform -chdir=envs/dev output -json private_subnet_ids── expected output ──["subnet-0a1b...","subnet-0c2d..."] - 2
App tier: launch template, ASG, ALB
Done when: Auto-healing EC2 instances behind a load balancer.
- Launch template with user data (install JRE, fetch JAR from S3 or run the Docker image, systemd unit)
- ASG min 2 across private subnets with ELB health checks
- ALB + target group + listener; security groups allowing only ALB → app
Stuck? Hints
- Use an instance profile with
AmazonSSMManagedInstanceCoreso you never need SSH.
Prove it works
terminal$ curl -s http://$(terraform -chdir=envs/dev output -raw alb_dns)/actuator/health── expected output ──{"status":"UP"} - 3
Data and storage
Done when: RDS and S3 wired to the app securely.
databasemodule: RDS PostgreSQL in private subnets, SG from app only, password managed in Secrets Managerstoragemodule: S3 bucket, block public access, SSE, lifecycle- IAM policy for the instance role: read that secret, read/write that bucket prefix only
Prove it works
terminal$ aws rds describe-db-instances --db-instance-identifier shoplite-dev --query 'DBInstances[0].[PubliclyAccessible,StorageEncrypted,MultiAZ]'── expected output ──[false, true, false] - 4
Monitoring and environments
Done when: Alarms, a dashboard, and a prod environment from the same modules.
- CloudWatch alarms: ALB 5xx > 1%, unhealthy hosts > 0, RDS free storage < 20%, CPU > 80%, notifying SNS
- Dashboard for the golden signals
envs/prod: bigger instances, Multi-AZ RDS, deletion protection
Prove it works
terminal$ aws cloudwatch describe-alarms --alarm-name-prefix shoplite-prod --query 'MetricAlarms[].AlarmName'── expected output ──["shoplite-prod-alb-5xx","shoplite-prod-unhealthy-hosts","shoplite-prod-rds-cpu","shoplite-prod-rds-storage"] - 5
CI for Terraform
Done when: Plans on PRs, applies from main, with OIDC credentials.
- GitHub Actions with OIDC to assume a role (no long-lived keys)
- PR: fmt, validate, tflint, checkov, plan posted as a comment
- Main: apply dev automatically; prod via a protected environment with approval
Prove it works
terminal$ # PR comment from the pipeline── expected output ──Plan: 2 to add, 1 to change, 0 to destroy.
Would you run this in production?
- ☐Remote state with locking and versioning
- ☐No hard-coded credentials; OIDC in CI; least-privilege IAM
- ☐Private subnets for app and data; no SSH
- ☐Encryption at rest everywhere; backups and deletion protection in prod
- ☐Alarms routed to a human; tags for cost allocation
Stretch goals
- Replace EC2 with ECS Fargate (Terraform course, Stage 3)
- Add CloudFront + WAF in front of the ALB
- Add
terraform testfor the modules (Infrastructure Testing guide)
Show it off
Résumé bullet
Designed and provisioned a production-grade three-tier AWS architecture (VPC, ALB, EC2 Auto Scaling, Multi-AZ RDS, S3, CloudWatch) with modular Terraform, remote state, and OIDC-based CI/CD with plan review and gated prod applies.
Demo script
- Terminate an instance and watch the ASG replace it
- Show an SSM session instead of SSH
- Show a PR with a plan comment and the approval gate for prod
Interview questions about this project
How do you structure Terraform for multiple environments?
Why use SSM Session Manager instead of SSH?