Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
app = Flask(__name__)


@app.route("/health", methods=["GET"])
def health_check():
return jsonify({"status": "healthy", "message": "API is running"}), 200


@app.route("/", methods=["GET"])
def get_days():
return jsonify(days)
Expand Down
30 changes: 30 additions & 0 deletions deploy/deploy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/bash

# Set your AWS region and ECR repo details
AWS_REGION="us-east-1"
REPO_NAME="flask-api"
IMAGE_TAG="latest"

# Get AWS Account ID
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)

# Build Docker image
echo "Building Docker image..."
docker build -t $REPO_NAME .

# Authenticate Docker to ECR
echo "Authenticating to AWS ECR..."
aws ecr get-login-password --region $AWS_REGION | \
docker login --username AWS --password-stdin $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com

# Create repo if it doesn't exist
echo "Checking or creating ECR repository..."
aws ecr describe-repositories --repository-names "$REPO_NAME" --region $AWS_REGION >/dev/null 2>&1 || \
aws ecr create-repository --repository-name "$REPO_NAME" --region $AWS_REGION

# Tag and push Docker image
echo "Tagging and pushing image to ECR..."
docker tag $REPO_NAME:$IMAGE_TAG $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$REPO_NAME:$IMAGE_TAG
docker push $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$REPO_NAME:$IMAGE_TAG

echo "✅ Deployment script completed."
25 changes: 25 additions & 0 deletions terraform/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
provider "aws" {
region = var.aws_region
}

module "network" {
source = "./modules/network"
vpc_cidr = var.vpc_cidr
app_name = var.app_name
}

module "ecs" {
source = "./modules/ecs"
vpc_id = module.network.vpc_id
subnet_ids = module.network.public_subnets
cluster_name = var.app_name
image_url = var.ecr_image_url
}

module "alb" {
source = "./modules/alb"
vpc_id = module.network.vpc_id
subnet_ids = module.network.public_subnets
target_port = 5000
app_name = var.app_name
}
11 changes: 11 additions & 0 deletions terraform/modules/alb/alb.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
resource "aws_lb" "app_lb" {
name = "${var.app_name}-alb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.alb_sg.id]
subnets = var.subnet_ids
}

output "alb_dns_name" {
value = aws_lb.app_lb.dns_name
}
10 changes: 10 additions & 0 deletions terraform/modules/alb/listener.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
resource "aws_lb_listener" "listener" {
load_balancer_arn = aws_lb.app_lb.arn
port = 80
protocol = "HTTP"

default_action {
type = "forward"
target_group_arn = aws_lb_target_group.tg.arn
}
}
12 changes: 12 additions & 0 deletions terraform/modules/alb/target_group.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
resource "aws_lb_target_group" "tg" {
name = "${var.app_name}-tg"
port = var.target_port
protocol = "HTTP"
vpc_id = var.vpc_id

health_check {
path = "/health"
matcher = "200"
interval = 30
}
}
3 changes: 3 additions & 0 deletions terraform/modules/ecs/ecs_cluster.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
resource "aws_ecs_cluster" "cluster" {
name = var.cluster_name
}
19 changes: 19 additions & 0 deletions terraform/modules/ecs/ecs_service.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
resource "aws_ecs_service" "service" {
name = var.cluster_name
cluster = aws_ecs_cluster.cluster.id
task_definition = aws_ecs_task_definition.task.arn
desired_count = 1
launch_type = "FARGATE"

network_configuration {
subnets = var.subnet_ids
security_groups = [aws_security_group.ecs_sg.id]
assign_public_ip = true
}

load_balancer {
target_group_arn = module.alb.tg_arn
container_name = "flask"
container_port = 5000
}
}
21 changes: 21 additions & 0 deletions terraform/modules/ecs/iam_roles.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
resource "aws_iam_role" "execution_role" {
name = "ecsTaskExecutionRole"

assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Action = "sts:AssumeRole",
Effect = "Allow",
Principal = {
Service = "ecs-tasks.amazonaws.com"
}
}
]
})
}

resource "aws_iam_role_policy_attachment" "execution_role_policy" {
role = aws_iam_role.execution_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}
20 changes: 20 additions & 0 deletions terraform/modules/ecs/task_definition.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
resource "aws_ecs_task_definition" "task" {
family = var.cluster_name
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
cpu = "256"
memory = "512"

execution_role_arn = aws_iam_role.execution_role.arn
container_definitions = jsonencode([
{
name = "flask"
image = var.image_url
portMappings = [{
containerPort = 5000
hostPort = 5000
protocol = "tcp"
}]
}
])
}
20 changes: 20 additions & 0 deletions terraform/modules/network/internet_gateway.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
resource "aws_internet_gateway" "igw" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.app_name}-igw"
}
}

resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw.id
}
}

resource "aws_route_table_association" "a" {
count = 2
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
}
38 changes: 38 additions & 0 deletions terraform/modules/network/security_groups.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
resource "aws_security_group" "alb_sg" {
name = "${var.app_name}-alb-sg"
description = "Allow HTTP"
vpc_id = aws_vpc.main.id

ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}

egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}

resource "aws_security_group" "ecs_sg" {
name = "${var.app_name}-ecs-sg"
vpc_id = aws_vpc.main.id

ingress {
from_port = 5000
to_port = 5000
protocol = "tcp"
security_groups = [aws_security_group.alb_sg.id]
}

egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
20 changes: 20 additions & 0 deletions terraform/modules/network/subnet.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
data "aws_availability_zones" "available" {}

resource "aws_subnet" "public" {
count = 2
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(aws_vpc.main.cidr_block, 8, count.index)
availability_zone = data.aws_availability_zones.available.names[count.index]
map_public_ip_on_launch = true
tags = {
Name = "${var.app_name}-subnet-${count.index}"
}
}

output "public_subnets" {
value = aws_subnet.public[*].id
}

output "vpc_id" {
value = aws_vpc.main.id
}
7 changes: 7 additions & 0 deletions terraform/modules/network/vpc.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
tags = {
Name = "${var.app_name}-vpc"
}
}
3 changes: 3 additions & 0 deletions terraform/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
output "alb_dns_name" {
value = module.alb.alb_dns_name
}
16 changes: 16 additions & 0 deletions terraform/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
variable "aws_region" {
default = "us-east-1"
}

variable "vpc_cidr" {
default = "10.0.0.0/16"
}

variable "app_name" {
default = "flask-api"
}

variable "ecr_image_url" {
description = "ECR Image URL"
type = string
}