From 18a7489183267d83f26c20b80181bccd6e9b5574 Mon Sep 17 00:00:00 2001 From: aprathip Date: Mon, 7 Jul 2025 15:32:26 +0530 Subject: [PATCH] Assessment for DevOps by Anvesh --- app.py | 5 +++ deploy/deploy.sh | 30 +++++++++++++++ terraform/main.tf | 25 ++++++++++++ terraform/modules/alb/alb.tf | 11 ++++++ terraform/modules/alb/listener.tf | 10 +++++ terraform/modules/alb/target_group.tf | 12 ++++++ terraform/modules/ecs/ecs_cluster.tf | 3 ++ terraform/modules/ecs/ecs_service.tf | 19 ++++++++++ terraform/modules/ecs/iam_roles.tf | 21 ++++++++++ terraform/modules/ecs/task_definition.tf | 20 ++++++++++ terraform/modules/network/internet_gateway.tf | 20 ++++++++++ terraform/modules/network/security_groups.tf | 38 +++++++++++++++++++ terraform/modules/network/subnet.tf | 20 ++++++++++ terraform/modules/network/vpc.tf | 7 ++++ terraform/outputs.tf | 3 ++ terraform/variables.tf | 16 ++++++++ 16 files changed, 260 insertions(+) create mode 100755 deploy/deploy.sh create mode 100644 terraform/main.tf create mode 100644 terraform/modules/alb/alb.tf create mode 100644 terraform/modules/alb/listener.tf create mode 100644 terraform/modules/alb/target_group.tf create mode 100644 terraform/modules/ecs/ecs_cluster.tf create mode 100644 terraform/modules/ecs/ecs_service.tf create mode 100644 terraform/modules/ecs/iam_roles.tf create mode 100644 terraform/modules/ecs/task_definition.tf create mode 100644 terraform/modules/network/internet_gateway.tf create mode 100644 terraform/modules/network/security_groups.tf create mode 100644 terraform/modules/network/subnet.tf create mode 100644 terraform/modules/network/vpc.tf create mode 100644 terraform/outputs.tf create mode 100644 terraform/variables.tf diff --git a/app.py b/app.py index 7042ec7..91049ee 100644 --- a/app.py +++ b/app.py @@ -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) diff --git a/deploy/deploy.sh b/deploy/deploy.sh new file mode 100755 index 0000000..e3eec22 --- /dev/null +++ b/deploy/deploy.sh @@ -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." \ No newline at end of file diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 0000000..1684280 --- /dev/null +++ b/terraform/main.tf @@ -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 +} \ No newline at end of file diff --git a/terraform/modules/alb/alb.tf b/terraform/modules/alb/alb.tf new file mode 100644 index 0000000..6ea2967 --- /dev/null +++ b/terraform/modules/alb/alb.tf @@ -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 +} \ No newline at end of file diff --git a/terraform/modules/alb/listener.tf b/terraform/modules/alb/listener.tf new file mode 100644 index 0000000..3265951 --- /dev/null +++ b/terraform/modules/alb/listener.tf @@ -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 + } +} \ No newline at end of file diff --git a/terraform/modules/alb/target_group.tf b/terraform/modules/alb/target_group.tf new file mode 100644 index 0000000..1435558 --- /dev/null +++ b/terraform/modules/alb/target_group.tf @@ -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 + } +} \ No newline at end of file diff --git a/terraform/modules/ecs/ecs_cluster.tf b/terraform/modules/ecs/ecs_cluster.tf new file mode 100644 index 0000000..ee4f0ef --- /dev/null +++ b/terraform/modules/ecs/ecs_cluster.tf @@ -0,0 +1,3 @@ +resource "aws_ecs_cluster" "cluster" { + name = var.cluster_name +} diff --git a/terraform/modules/ecs/ecs_service.tf b/terraform/modules/ecs/ecs_service.tf new file mode 100644 index 0000000..fb713d0 --- /dev/null +++ b/terraform/modules/ecs/ecs_service.tf @@ -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 + } +} \ No newline at end of file diff --git a/terraform/modules/ecs/iam_roles.tf b/terraform/modules/ecs/iam_roles.tf new file mode 100644 index 0000000..3423a67 --- /dev/null +++ b/terraform/modules/ecs/iam_roles.tf @@ -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" +} diff --git a/terraform/modules/ecs/task_definition.tf b/terraform/modules/ecs/task_definition.tf new file mode 100644 index 0000000..d00b411 --- /dev/null +++ b/terraform/modules/ecs/task_definition.tf @@ -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" + }] + } + ]) +} \ No newline at end of file diff --git a/terraform/modules/network/internet_gateway.tf b/terraform/modules/network/internet_gateway.tf new file mode 100644 index 0000000..70e71e2 --- /dev/null +++ b/terraform/modules/network/internet_gateway.tf @@ -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 +} \ No newline at end of file diff --git a/terraform/modules/network/security_groups.tf b/terraform/modules/network/security_groups.tf new file mode 100644 index 0000000..d50dcbe --- /dev/null +++ b/terraform/modules/network/security_groups.tf @@ -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"] + } +} \ No newline at end of file diff --git a/terraform/modules/network/subnet.tf b/terraform/modules/network/subnet.tf new file mode 100644 index 0000000..f5fa26b --- /dev/null +++ b/terraform/modules/network/subnet.tf @@ -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 +} \ No newline at end of file diff --git a/terraform/modules/network/vpc.tf b/terraform/modules/network/vpc.tf new file mode 100644 index 0000000..0b93357 --- /dev/null +++ b/terraform/modules/network/vpc.tf @@ -0,0 +1,7 @@ +resource "aws_vpc" "main" { + cidr_block = var.vpc_cidr + enable_dns_hostnames = true + tags = { + Name = "${var.app_name}-vpc" + } +} \ No newline at end of file diff --git a/terraform/outputs.tf b/terraform/outputs.tf new file mode 100644 index 0000000..7f08c9d --- /dev/null +++ b/terraform/outputs.tf @@ -0,0 +1,3 @@ +output "alb_dns_name" { + value = module.alb.alb_dns_name +} diff --git a/terraform/variables.tf b/terraform/variables.tf new file mode 100644 index 0000000..706e081 --- /dev/null +++ b/terraform/variables.tf @@ -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 +} \ No newline at end of file