Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

17 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

AWS Terraform Deployment β€” Flask Backend + Express Frontend

Student Assignment | Docker, AWS, Terraform
A full-stack student registration app (Flask + Express) deployed across three configurations on AWS.


πŸ“ Repository Structure

docker-assignment/
β”œβ”€β”€ backend/                          # Flask REST API (Python)
β”‚   β”œβ”€β”€ app.py
β”‚   β”œβ”€β”€ requirements.txt
β”‚   └── Dockerfile
β”œβ”€β”€ frontend/                         # Express frontend (Node.js + EJS)
β”‚   β”œβ”€β”€ server.js
β”‚   β”œβ”€β”€ package.json
β”‚   β”œβ”€β”€ views/
β”‚   └── Dockerfile
β”œβ”€β”€ docker-compose.yml                # Local development
└── terraform/
    β”œβ”€β”€ backend-bootstrap/            # S3 + DynamoDB state backend (run first)
    β”‚   └── main.tf
    β”œβ”€β”€ part1-single-ec2/             # Part 1 β€” single EC2
    β”‚   β”œβ”€β”€ main.tf
    β”‚   β”œβ”€β”€ variables.tf
    β”‚   β”œβ”€β”€ outputs.tf
    β”‚   └── user_data.sh
    β”œβ”€β”€ part2-separate-ec2/           # Part 2 β€” two EC2 instances
    β”‚   β”œβ”€β”€ main.tf
    β”‚   β”œβ”€β”€ variables.tf
    β”‚   β”œβ”€β”€ outputs.tf
    β”‚   β”œβ”€β”€ flask_userdata.sh
    β”‚   └── express_userdata.sh
    └── part3-ecs-fargate/            # Part 3 β€” ECS Fargate + ECR + ALB
        β”œβ”€β”€ main.tf
        β”œβ”€β”€ variables.tf
        β”œβ”€β”€ outputs.tf
        └── push-to-ecr.sh

βš™οΈ Prerequisites

Tool Version Install
AWS CLI v2+ aws.amazon.com/cli
Terraform v1.3+ terraform.io
Docker 24+ docker.com
Git any git-scm.com

AWS Configuration:

aws configure
# Enter: Access Key ID, Secret Access Key, Region (us-east-1), Output (json)

Create EC2 Key Pair (for Parts 1 & 2):

aws ec2 create-key-pair \
  --key-name docker-assignment-key \
  --query 'KeyMaterial' \
  --output text > docker-assignment-key.pem

chmod 400 docker-assignment-key.pem

πŸ—„οΈ Step 0 β€” Bootstrap Terraform Remote State (Run Once)

Before deploying any part, create the shared S3 + DynamoDB backend:

cd terraform/backend-bootstrap
terraform init
terraform plan
terraform apply -auto-approve

What this creates:

  • S3 bucket: docker-assignment-tfstate (versioned + encrypted)
  • DynamoDB table: terraform-lock (state locking)

🟒 Part 1 β€” Both Apps on a Single EC2 Instance

Architecture

Internet
   β”‚
   β–Ό
EC2 (Ubuntu 22.04 β€” t2.micro)
   β”œβ”€β”€ Flask Backend  :5000  (PM2 process)
   └── Express Frontend :3000  (PM2 process)

Resources Created

  • Security Group (ports 22, 3000, 5000)
  • EC2 Instance with Cloud-Init user data
  • PM2 process manager (auto-restart on crash/reboot)

Deploy

cd terraform/part1-single-ec2
terraform init
terraform plan
terraform apply

Expected Output

instance_public_ip = "54.x.x.x"
flask_url          = "http://54.x.x.x:5000"
express_url        = "http://54.x.x.x:3000"
ssh_command        = "ssh -i docker-assignment-key.pem ubuntu@54.x.x.x"

Verify

# Health check Flask
curl http://54.x.x.x:5000/health
# β†’ {"status":"ok"}

# Open Express frontend in browser
open http://54.x.x.x:3000

# SSH and check PM2 processes
ssh -i docker-assignment-key.pem ubuntu@54.x.x.x
pm2 list
pm2 logs

Destroy

terraform destroy -auto-approve

πŸ”΅ Part 2 β€” Flask and Express on Separate EC2 Instances

Architecture

Internet
   β”‚
   β”œβ”€β”€β–Ί Express EC2 (Subnet B) :3000
   β”‚         β”‚  FLASK_URL=http://<private-ip>:5000
   β”‚         β–Ό  (internal VPC traffic)
   └──► Flask EC2   (Subnet A) :5000

Resources Created

  • VPC (10.0.0.0/16) with 2 public subnets across 2 AZs
  • Internet Gateway + Route Tables
  • 2 Security Groups (Flask SG allows 5000 from Express SG)
  • 2 EC2 Instances (Flask in subnet-a, Express in subnet-b)
  • Systemd services for auto-restart

Deploy

cd terraform/part2-separate-ec2
terraform init
terraform plan
terraform apply

Expected Output

flask_public_ip   = "54.x.x.x"
express_public_ip = "3.y.y.y"
flask_url         = "http://54.x.x.x:5000"
express_url       = "http://3.y.y.y:3000"
flask_health_check = "http://54.x.x.x:5000/health"

Verify

# Flask health check (public)
curl http://54.x.x.x:5000/health

# Express frontend (reaches Flask via private IP internally)
open http://3.y.y.y:3000

# Check Flask systemd service
ssh -i docker-assignment-key.pem ubuntu@54.x.x.x
sudo systemctl status flask-backend
sudo journalctl -u flask-backend -n 50

# Check Express systemd service
ssh -i docker-assignment-key.pem ubuntu@3.y.y.y
sudo systemctl status express-frontend
sudo journalctl -u express-frontend -n 50

Security Group Flow

Internet ──► express_sg (3000/tcp) ──► Express EC2
Express EC2 ──► flask_sg (5000/tcp, source: express_sg) ──► Flask EC2

Destroy

terraform destroy -auto-approve

🟣 Part 3 β€” Docker on ECS Fargate with ECR + ALB

Architecture

Internet
   β”‚
   β–Ό  port 80
Application Load Balancer (ALB)
   β”‚
   β”œβ”€β”€β”€β”€β”€β”€ /api/* ──────► ECS Service: Flask  (Fargate) :5000
   β”‚                       (ECR: docker-assignment-flask-backend)
   β”‚
   └────── /* ──────────► ECS Service: Express (Fargate) :3000
                           (ECR: docker-assignment-express-frontend)

Resources Created

Resource Count Detail
ECR Repositories 2 flask-backend, express-frontend
VPC 1 10.0.0.0/16
Public Subnets 2 Multi-AZ
Internet Gateway 1
Security Groups 3 ALB, flask-sg, express-sg
IAM Role 1 ECS Task Execution
CloudWatch Log Groups 2 7-day retention
ECS Cluster 1 Container Insights enabled
ECS Task Definitions 2
ECS Services 2 Fargate launch type
ALB 1
Target Groups 2
ALB Listener Rules 2 Path-based routing

Step 1 β€” Deploy Infrastructure

cd terraform/part3-ecs-fargate
terraform init
terraform plan
terraform apply

Step 2 β€” Push Docker Images to ECR

# From project root (where backend/ and frontend/ folders live)
chmod +x terraform/part3-ecs-fargate/push-to-ecr.sh
./terraform/part3-ecs-fargate/push-to-ecr.sh us-east-1

What this does:

  1. Authenticates Docker to ECR
  2. Builds backend/Dockerfile β†’ pushes to ECR flask repo
  3. Builds frontend/Dockerfile β†’ pushes to ECR express repo

Step 3 β€” Force ECS to Pull New Images

aws ecs update-service \
  --cluster docker-assignment-cluster \
  --service docker-assignment-flask-service \
  --force-new-deployment \
  --region us-east-1

aws ecs update-service \
  --cluster docker-assignment-cluster \
  --service docker-assignment-express-service \
  --force-new-deployment \
  --region us-east-1

Expected Output

ecr_flask_url   = "123456789.dkr.ecr.us-east-1.amazonaws.com/docker-assignment-flask-backend"
ecr_express_url = "123456789.dkr.ecr.us-east-1.amazonaws.com/docker-assignment-express-frontend"
alb_dns_name    = "docker-assignment-alb-123456.us-east-1.elb.amazonaws.com"
frontend_url    = "http://docker-assignment-alb-123456.us-east-1.elb.amazonaws.com"
flask_api_url   = "http://docker-assignment-alb-123456.us-east-1.elb.amazonaws.com/api"
flask_health_url = "http://docker-assignment-alb-123456.us-east-1.elb.amazonaws.com/health"

Verify

# Flask health check (via ALB)
curl http://<alb-dns-name>/health
# β†’ {"status":"ok"}

# Flask submit endpoint
curl -X POST http://<alb-dns-name>/submit \
  -H "Content-Type: application/json" \
  -d '{"name":"John","student_id":"S001","email":"j@uni.edu","course":"CS","grade":"A"}'

# Express frontend
open http://<alb-dns-name>

# Check ECS task status
aws ecs describe-services \
  --cluster docker-assignment-cluster \
  --services docker-assignment-flask-service docker-assignment-express-service \
  --region us-east-1

# View CloudWatch logs
aws logs tail /ecs/docker-assignment/flask-backend --follow
aws logs tail /ecs/docker-assignment/express-frontend --follow

Destroy

terraform destroy -auto-approve

πŸš€ Local Development

Run both apps locally with Docker Compose:

# Build and start
docker compose up --build

# Access
# Express frontend: http://localhost:3000
# Flask backend:    http://localhost:5000/health

# Stop
docker compose down

πŸ”‘ General Requirements Checklist

Requirement Status
variables.tf in all parts βœ…
outputs.tf in all parts βœ…
S3 remote state backend βœ…
DynamoDB state locking βœ…
terraform plan before apply βœ…
Security groups with least privilege βœ…
User data / Cloud-Init automation βœ…
ECR repositories for Docker images βœ…
ECS Fargate cluster + services βœ…
ALB with path-based routing βœ…
CloudWatch logs for ECS βœ…
Resource tagging βœ…

🌐 API Reference

Flask Backend Endpoints

Method Endpoint Description
GET /health Health check β†’ {"status":"ok"}
POST /submit Submit student registration

Sample POST /submit:

{
  "name": "John Doe",
  "student_id": "S12345",
  "email": "john@uni.edu",
  "course": "Computer Science",
  "grade": "A"
}

Response:

{
  "success": true,
  "message": "Student 'John Doe' submitted successfully!",
  "data": {
    "name": "John Doe",
    "student_id": "S12345",
    "email": "john@uni.edu",
    "course": "Computer Science",
    "grade": "A",
    "grade_status": "Excellent"
  }
}

πŸ‘€ Author

Mohammad Noman Qadri
Docker + AWS + Terraform Assignment
GitHub: nomanqadri34/docker-assignment

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages