diff --git a/.github/workflows/project.yml b/.github/workflows/project.yml new file mode 100644 index 0000000..31f532b --- /dev/null +++ b/.github/workflows/project.yml @@ -0,0 +1,133 @@ +name: .NETProject + +on: + push: + branches: [ "main" ] + + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 6.0.x + - name: Restore dependencies + run: dotnet restore + - name: Build + run: dotnet build --no-restore + - name: Test + run: dotnet test --no-build --verbosity normal + + - name: Check if Docker Compose is installed + run: | + if ! command -v docker-compose &> /dev/null + then + echo "Docker Compose is not installed. Installing..." + sudo apt-get update + sudo apt-get install -y docker-compose + else + echo "Docker Compose is already installed." + fi + + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Docker + uses: docker/setup-buildx-action@v1 + + - name: Configure AWS CLI + run: | + aws configure set aws_access_key_id ${{ secrets.AWS_ACCESS_KEY_ID }} + aws configure set aws_secret_access_key ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws configure set region ${{ secrets.AWS_DEFAULT_REGION }} + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v2 + with: + registry: ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_DEFAULT_REGION }}.amazonaws.com + + - name: Build and push Docker image -Migrator + run: | + docker build -t ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_DEFAULT_REGION }}.amazonaws.com/${{ secrets.MIGRATOR }}:latest --target migrator . + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_DEFAULT_REGION }}.amazonaws.com/${{ secrets.MIGRATOR }}:latest + + - name: Build and push Docker image - WEB + run: | + docker build -t ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_DEFAULT_REGION }}.amazonaws.com/${{ secrets.WEB }}:latest --target web . + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_DEFAULT_REGION }}.amazonaws.com/${{ secrets.WEB }}:latest + + + + - name: Build and push Docker image -Auth + run: | + docker build -t ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_DEFAULT_REGION }}.amazonaws.com/${{ secrets.AUTH }}:latest --target auth . + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_DEFAULT_REGION }}.amazonaws.com/${{ secrets.AUTH }}:latest + + + + - name: Build and push Docker image - App + run: | + docker build -t ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_DEFAULT_REGION }}.amazonaws.com/${{ secrets.HOST }}:latest --target host . + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_DEFAULT_REGION }}.amazonaws.com/${{ secrets.HOST }}:latest + + + - name: Update ECS service + run: | + aws ecs update-service --cluster bookstore-cluster --service app --force-new-deployment --debug + + + + - name: Update ECS service + run: | + aws ecs update-service --cluster bookstore-cluster --service migrator --force-new-deployment --debug + + + - name: Update ECS service + run: | + aws ecs update-service --cluster bookstore-cluster --service auth --force-new-deployment --debug + + + - name: Update ECS service + run: | + aws ecs update-service --cluster bookstore-cluster --service web --force-new-deployment --debug + + + + # - name: Build and push Docker image - DB + # run: | + # docker build -t ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_DEFAULT_REGION }}.amazonaws.com/${{ secrets.DB }}:latest --target db . + # docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_DEFAULT_REGION }}.amazonaws.com/${{ secrets.DB }}:latest + + + + # - name: Update ECS service + # run: | + # aws ecs update-service --cluster ${{ secrets.AWS_ECS_CLUSTER }} --service ${{ secrets.HOST }} --force-new-deployment + + + # - name: Update ECS service + # run: | + # aws ecs update-service --cluster ${{ secrets.AWS_ECS_CLUSTER }} --service ${{ secrets.AUTH }} --force-new-deployment + + + # - name: Update ECS service + # run: | + # aws ecs update-service --cluster ${{ secrets.AWS_ECS_CLUSTER }} --service ${{ secrets.WEB }} --force-new-deployment + + + + # - name: Update ECS service + # run: | + # aws ecs update-service --cluster ${{ secrets.AWS_ECS_CLUSTER }} --service ${{ secrets.MIGRATOR }} --force-new-deployment + + + + + + + diff --git a/Bookstore-Infra/.gitignore b/Bookstore-Infra/.gitignore new file mode 100644 index 0000000..8715e83 --- /dev/null +++ b/Bookstore-Infra/.gitignore @@ -0,0 +1,39 @@ +secret.tf +secret_password.json +secret.username.json +.terraform.lock.hcl + +# Local .terraform directories +**/.terraform/* + +# .tfstate files +*.tfstate +*.tfstate.* + +# Crash log files +crash.log +crash.*.log + +# Exclude all .tfvars files, which are likely to contain sensitive data, such as +# password, private keys, and other secrets. These should not be part of version +# control as they are data points which are potentially sensitive and subject +# to change depending on the environment. +*.tfvars +*.tfvars.json + +# Ignore override files as they are usually used to override resources locally and so +# are not checked in +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Include override files you do wish to add to version control using negated pattern +# !example_override.tf + +# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan +# example: *tfplan* + +# Ignore CLI configuration files +.terraformrc +terraform.rc \ No newline at end of file diff --git a/Bookstore-Infra/.terraform.lock.hcl b/Bookstore-Infra/.terraform.lock.hcl new file mode 100644 index 0000000..896c545 --- /dev/null +++ b/Bookstore-Infra/.terraform.lock.hcl @@ -0,0 +1,41 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.25.0" + hashes = [ + "h1:NkAtnv7ueO1PbcZRWxDsfgHGmzxG7gUuTbMgCe4l9zw=", + "zh:163244f73f13b013d9a6a267731df5971d03a3cdcdefb6d280dcbb39e482d76b", + "zh:179debe1fcfff552589c949e068ff7cae5f4be6b00b48f20933029795210f4c1", + "zh:1c66da7cd54614c57d3f7fa0f5f2b51864358250d360cdb6c403f4248ab15fd4", + "zh:360cdc430c4e79a7b9b791832460e9caccafd9dd1d9bb9a95293aaebce5d506c", + "zh:40eb5dd1e678528fb881b448624c68f10f1878bfbbf4160c829bb2255f8ae159", + "zh:76a30a8a5cc9132202929c4ec5dd4f1ac4089af73a0d417a965cd23801e69526", + "zh:7fbd7796e787635640ba5b489b6b1cd9294156e7f222f70f0b9672d0302bbf67", + "zh:8408832a7540e758397e55dba2134b7428b7318d515f7226dd548231bc61e3df", + "zh:97db8d60e240701b7010eff29e3d44360856b7f3e72a217d1a57d6315bbb8e08", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:9f9c7b9203ac445b65ca8f3cfa176d9e86dc5572ce925490d7fabb04ebf4f829", + "zh:ca5c18799ab858a1addcbfb10cf227e013eb12c110b7d750c969fc8711ff578b", + "zh:cdd503da9f461ad5bc119cb3003be85b3752457b552f41ed20315e1239e3084a", + "zh:f8aaad1544823803dfd03e22ed260e706524baf5509cb1c4cf2be9d982c0fa6c", + "zh:ff283a7aaf509b3a9ba17a2efbc2faccbc69d84e913b23f2434dd5102becac3c", + ] +} + +provider "registry.terraform.io/hashicorp/template" { + version = "2.2.0" + hashes = [ + "h1:N2IXrfDhxid5pFKHISN9V23ptHJyFZEdojUegdx5YkM=", + "zh:01702196f0a0492ec07917db7aaa595843d8f171dc195f4c988d2ffca2a06386", + "zh:09aae3da826ba3d7df69efeb25d146a1de0d03e951d35019a0f80e4f58c89b53", + "zh:09ba83c0625b6fe0a954da6fbd0c355ac0b7f07f86c91a2a97849140fea49603", + "zh:0e3a6c8e16f17f19010accd0844187d524580d9fdb0731f675ffcf4afba03d16", + "zh:45f2c594b6f2f34ea663704cc72048b212fe7d16fb4cfd959365fa997228a776", + "zh:77ea3e5a0446784d77114b5e851c970a3dde1e08fa6de38210b8385d7605d451", + "zh:8a154388f3708e3df5a69122a23bdfaf760a523788a5081976b3d5616f7d30ae", + "zh:992843002f2db5a11e626b3fc23dc0c87ad3729b3b3cff08e32ffb3df97edbde", + "zh:ad906f4cebd3ec5e43d5cd6dc8f4c5c9cc3b33d2243c89c5fc18f97f7277b51d", + "zh:c979425ddb256511137ecd093e23283234da0154b7fa8b21c2687182d9aea8b2", + ] +} diff --git a/Bookstore-Infra/ACM.tf b/Bookstore-Infra/ACM.tf new file mode 100644 index 0000000..21bc5d9 --- /dev/null +++ b/Bookstore-Infra/ACM.tf @@ -0,0 +1,85 @@ + # # Variables + # variable "domain_name" { + # description = "bookstore-Public-ALB-2112943775.us-east-1.elb.amazonaws.com" + # } + + # # ACM Certificate + # resource "aws_acm_certificate" "ssl_certificate" { + # domain_name = var.domain_name + # validation_method = "DNS" + + # lifecycle { + # create_before_destroy = true + # } + # } + + # # Route 53 Hosted Zone + # resource "aws_route53_zone" "my_zone" { + # name = var.domain_name + # } + + # # ACM Certificate DNS Validation Record + # resource "aws_route53_record" "acm_validation" { + # for_each = aws_acm_certificate.ssl_certificate.domain_validation_options + + # name = each.value.resource_record_name + # type = each.value.resource_record_type + # records = [each.value.resource_record_value] + # zone_id = aws_route53_zone.my_zone.zone_id + # ttl = 60 + # } + + + +# ############################ Validation using EMAIL ############################################################################################################# + + + +# # ACM Certificate +# resource "aws_acm_certificate" "ssl_certificate" { +# domain_name = "bookstore-Public-ALB-2112943775.us-east-1.elb.amazonaws.com" +# validation_method = "EMAIL" + +# lifecycle { +# create_before_destroy = true +# } +# } + +# # Wait for ACM Certificate Validation (Email) +# resource "aws_acm_certificate_validation" "ssl_certificate_validation" { +# certificate_arn = aws_acm_certificate.ssl_certificate.arn +# } + +# # ALB Listener Rule for HTTPS +# resource "aws_lb_listener" "alb_listener_https" { +# load_balancer_arn = aws_lb.alb.arn +# port = 443 +# protocol = "HTTPS" +# ssl_policy = "ELBSecurityPolicy-2016-08" +# certificate_arn = aws_acm_certificate_validation.ssl_certificate_validation.certificate_arn + +# default_action { +# type = "forward" +# target_group_arn = aws_lb_target_group.lb_target_group.arn +# } +# } + + +# resource "aws_acm_certificate" "ssl_certificate" { +# domain_name = "bookstore-Public-ALB-2112943775.us-east-1.elb.amazonaws.com" +# validation_method = "DNS" +# } + +# resource "aws_acm_certificate_validation" "ssl_certificate_validation" { +# certificate_arn = aws_acm_certificate.ssl_certificate.arn +# validation_record_fqdns = aws_acm_certificate.ssl_certificate.domain_validation_options.*.resource_record_name +# } + +# # Ensure ACM certificate validation completes before creating other resources +# resource "null_resource" "wait_for_acm_validation" { +# depends_on = [aws_acm_certificate_validation.ssl_certificate_validation] + +# provisioner "local-exec" { +# command = "echo ACM certificate has been successfully validated!" +# } +# } diff --git a/Bookstore-Infra/README.md b/Bookstore-Infra/README.md new file mode 100644 index 0000000..fda01be --- /dev/null +++ b/Bookstore-Infra/README.md @@ -0,0 +1 @@ +# Bookstore-Infra \ No newline at end of file diff --git a/Bookstore-Infra/asg_alb.tf b/Bookstore-Infra/asg_alb.tf new file mode 100644 index 0000000..b887adb --- /dev/null +++ b/Bookstore-Infra/asg_alb.tf @@ -0,0 +1,151 @@ + resource "aws_launch_configuration" "ecs_launch" { + name = "${var.project}-Launch-config" + image_id = var.AMI + instance_type = var.asg_instance_type + key_name = var.key_name + user_data = "#!/bin/bash \n echo ECS_CLUSTER=${aws_ecs_cluster.ecs_cluster.name} >> /etc/ecs/ecs.config" + #user_data = base64encode(var.lg_user_data) + + security_groups = [aws_security_group.ecs_sg.id] + + iam_instance_profile = aws_iam_instance_profile.ecs_agent.name +} + + +resource "aws_autoscaling_group" "ecs_asg" { + name = "${var.project}-ASG" + vpc_zone_identifier = [for subnet in aws_subnet.public_subnets[*] : subnet.id] + launch_configuration = aws_launch_configuration.ecs_launch.name + desired_capacity = var.desired_capacity + min_size = var.min_size + max_size = var.max_size + health_check_grace_period = var.health_check_grace_period + health_check_type = "EC2" +} + +resource "aws_lb" "alb" { + name = "${var.project}-Public-ALB" + internal = false + load_balancer_type = "application" + security_groups = [aws_security_group.ecs_sg.id] + subnets = [for subnet in aws_subnet.public_subnets : subnet.id] + tags = { + Createdwith = "Terraform" + } +} + +resource "aws_lb_target_group" "lb_target_group" { + name = "${var.project}-Target-Group" + port = 80 + protocol = "HTTP" + vpc_id = aws_vpc.main.id + target_type = "instance" + tags = { + Createdwith = "Terraform" + } +} + + +resource "aws_autoscaling_attachment" "asg_to_target_group" { + autoscaling_group_name = aws_autoscaling_group.ecs_asg.name + lb_target_group_arn = aws_lb_target_group.lb_target_group.arn +} + +resource "aws_lb_listener" "alb_listener" { + load_balancer_arn = aws_lb.alb.arn + port = "80" + protocol = "HTTP" + default_action { + type = "forward" + target_group_arn = aws_lb_target_group.lb_target_group.arn + } +} +# resource "aws_lb_listener" "alb_listener_https" { +# load_balancer_arn = aws_lb.alb.arn +# port = 443 +# protocol = "HTTPS" +# ssl_policy = "ELBSecurityPolicy-2016-08" +# certificate_arn = aws_acm_certificate.ssl_certificate.arn + +# default_action { +# type = "forward" +# target_group_arn = aws_lb_target_group.lb_target_group.arn +# } +# } + + + + +#scale up policy +resource "aws_autoscaling_policy" "scale_up" { + name = "${var.project}-asg-scale-up" + autoscaling_group_name = aws_autoscaling_group.ecs_asg.name + adjustment_type = "ChangeInCapacity" + scaling_adjustment = "1" #increasing instance by 1 + cooldown = "300" + policy_type = "SimpleScaling" +} + +# scale up alarm +resource "aws_cloudwatch_metric_alarm" "scale_up_alarm" { + alarm_name = "${var.project}-asg-scale-up-alarm" + alarm_description = "asg-scale-up-cpu-alarm" + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = "5" + metric_name = "CPUUtilization" + namespace = "AWS/EC2" + period = "60" + statistic = "Average" + threshold = "60" + dimensions = { + "AutoScalingGroupName" = aws_autoscaling_group.ecs_asg.name + } + actions_enabled = true + alarm_actions = [aws_autoscaling_policy.scale_up.arn] +} + +# Alarm for average CPU Utilization og greater than 60% for constant 20 minutes +resource "aws_cloudwatch_metric_alarm" "constant_cpu_60_percent_up_alarm" { + alarm_name = "${var.project}-constant_cpu_60_percent_up_alarm" + alarm_description = "asg-scale-up-cpu-alarm" + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = "20" + metric_name = "CPUUtilization" + namespace = "AWS/EC2" + period = "60" + statistic = "Average" + threshold = "60" + dimensions = { + "AutoScalingGroupName" = aws_autoscaling_group.ecs_asg.name + } + actions_enabled = true + alarm_actions = [aws_autoscaling_policy.scale_up.arn] +} + +# scale down policy +resource "aws_autoscaling_policy" "scale_down" { + name = "${var.project}-asg-scale-down" + autoscaling_group_name = aws_autoscaling_group.ecs_asg.name + adjustment_type = "ChangeInCapacity" + scaling_adjustment = "-1" # decreasing instance by 1 + cooldown = "300" + policy_type = "SimpleScaling" +} + +# scale down alarm +resource "aws_cloudwatch_metric_alarm" "scale_down_alarm" { + alarm_name = "${var.project}-asg-scale-down-alarm" + alarm_description = "asg-scale-down-cpu-alarm" + comparison_operator = "LessThanOrEqualToThreshold" + evaluation_periods = "20" + metric_name = "CPUUtilization" + namespace = "AWS/EC2" + period = "60" + statistic = "Average" + threshold = "40" + dimensions = { + "AutoScalingGroupName" = aws_autoscaling_group.ecs_asg.name + } + actions_enabled = true + alarm_actions = [aws_autoscaling_policy.scale_down.arn] +} diff --git a/Bookstore-Infra/bookstore b/Bookstore-Infra/bookstore new file mode 100644 index 0000000..b4e783c --- /dev/null +++ b/Bookstore-Infra/bookstore @@ -0,0 +1,38 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +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 +-----END OPENSSH PRIVATE KEY----- diff --git a/Bookstore-Infra/bookstore.pub b/Bookstore-Infra/bookstore.pub new file mode 100644 index 0000000..76b55dd --- /dev/null +++ b/Bookstore-Infra/bookstore.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCVmdK0kXX1LGpROGFC9tK3ZNxXrDBHsSV7rSZEtr4cL92E4Nbk5736IXbO3UQ5sxPYcp7XIVY6nMME1fm1qWID462jdkhJZRePOASJuPxnznVpEldDRpeA7RupCpZdHC1XKy2HyYo9OAII8IGoPwHKd///uy0TaLkL3bBceomqcW9qffhUpVsOCojGU31QjbE3Rvs0oWgorOzP19+997nodTfVCCxooy0Yp0R7NCT5zCtLgkYKI4xH66o+/H6KQVsXGwN6bANphyGy3vahFzocd0lF9iEHyT79zbnExpUR/TELdYfr1qgKjrAHMXo49tFamPyWy0w+i2ndiLgz8C87sgfVZOPf/Lmzr689w58/RQBOwNIBDt5CKhPOIz1sIIO8KyHSHST72FwBDSh9aMA2mfIorLQdLV1lxoWMK8wlDEZZDZ9LgdAv5zdhjqPwpaQ/y+JPCXja4JYrAdQsKsa8IX5Dtgl40c4pFpjUuzEPugsPve91HtUKc71JPrwqlJ0= admin@DESKTOP-GAU9HFH diff --git a/Bookstore-Infra/ecr.tf b/Bookstore-Infra/ecr.tf new file mode 100644 index 0000000..efedc29 --- /dev/null +++ b/Bookstore-Infra/ecr.tf @@ -0,0 +1,8 @@ +resource "aws_ecr_repository" "ecr_repo" { + count = length(var.repository_names) + name = var.repository_names[count.index] + + image_scanning_configuration { + scan_on_push = true + } +} \ No newline at end of file diff --git a/Bookstore-Infra/ecs.tf b/Bookstore-Infra/ecs.tf new file mode 100644 index 0000000..5de4323 --- /dev/null +++ b/Bookstore-Infra/ecs.tf @@ -0,0 +1,168 @@ +resource "aws_ecs_cluster" "ecs_cluster" { + name = "${var.project}-cluster" +} + +####################### ECS task for app ####################### + +resource "aws_ecs_task_definition" "app_task_definition" { + family = "app" + requires_compatibilities = ["EC2"] + container_definitions = jsonencode([ + { + "name" : "app-container", + "image" : "app:latest", + "portMappings" : [ + { + "containerPort" : 80, + "hostPort" : 80 + } + ], + "memory" : 512, + "memoryReservation" : 256, + "dockerLabels" : { + "image" : "nginx" + } + }, + ]) +} + +resource "aws_ecs_service" "app" { + name = "app" + cluster = aws_ecs_cluster.ecs_cluster.id + task_definition = aws_ecs_task_definition.app_task_definition.arn + desired_count = 1 +} + + +####################### ECS task for auth ####################### + + + +resource "aws_ecs_task_definition" "auth_task_definition" { + family = "auth" + requires_compatibilities = ["EC2"] + container_definitions = jsonencode([ + { + "name" : "auth-container", + "image" : "auth:latest", + "portMappings" : [ + { + "containerPort" : 80, + "hostPort" : 80 + } + ], + "memory" : 512, + "memoryReservation" : 256, + "dockerLabels" : { + "image" : "nginx" + } + }, + ]) +} + +resource "aws_ecs_service" "auth" { + name = "auth" + cluster = aws_ecs_cluster.ecs_cluster.id + task_definition = aws_ecs_task_definition.auth_task_definition.arn + desired_count = 1 +} + + + +####################### ECS task for db ####################### + + + +# resource "aws_ecs_task_definition" "db_task_definition" { +# family = "db" +# requires_compatibilities = ["EC2"] +# container_definitions = jsonencode([ +# { +# "name" : "db", +# "image" : "db:latest", +# "portMappings" : [ +# { +# "containerPort" : 80, +# "hostPort" : 80 +# } +# ], +# "memory" : 512, +# "memoryReservation" : 256, +# "dockerLabels" : { +# "image" : "nginx" +# } +# }, +# ]) +# } +# resource "aws_ecs_service" "db" { +# name = "db" +# cluster = aws_ecs_cluster.ecs_cluster.id +# task_definition = aws_ecs_task_definition.db_task_definition.arn +# desired_count = 1 +# } + + +####################### ECS task for migrator ####################### + + + +resource "aws_ecs_task_definition" "migrator_task_definition" { + family = "migrator" + requires_compatibilities = ["EC2"] + container_definitions = jsonencode([ + { + "name" : "migrator", + "image" : "migrator:latest", + "portMappings" : [ + { + "containerPort" : 80, + "hostPort" : 80 + } + ], + "memory" : 512, + "memoryReservation" : 256, + "dockerLabels" : { + "image" : "nginx" + } + }, + ]) +} + +resource "aws_ecs_service" "migrator" { + name = "migrator" + cluster = aws_ecs_cluster.ecs_cluster.id + task_definition = aws_ecs_task_definition.migrator_task_definition.arn + desired_count = 1 +} + + +####################### ECS task for web ####################### + +resource "aws_ecs_task_definition" "web_task_definition" { + family = "web" + requires_compatibilities = ["EC2"] + container_definitions = jsonencode([ + { + "name" : "web", + "image" : "web:latest", + "portMappings" : [ + { + "containerPort" : 80, + "hostPort" : 80 + } + ], + "memory" : 512, + "memoryReservation" : 256, + "dockerLabels" : { + "image" : "nginx" + } + }, + ]) +} + +resource "aws_ecs_service" "web" { + name = "web" + cluster = aws_ecs_cluster.ecs_cluster.id + task_definition = aws_ecs_task_definition.web_task_definition.arn + desired_count = 1 +} \ No newline at end of file diff --git a/Bookstore-Infra/output.tf b/Bookstore-Infra/output.tf new file mode 100644 index 0000000..dbc1f97 --- /dev/null +++ b/Bookstore-Infra/output.tf @@ -0,0 +1,7 @@ +output "EIP" { + value = aws_eip.nat_eip.address +} + +# output "launch_configuration_name" { +# value = aws_launch_configuration.ecs_launch_config.name +# } diff --git a/Bookstore-Infra/provider.tf b/Bookstore-Infra/provider.tf new file mode 100644 index 0000000..92f28e8 --- /dev/null +++ b/Bookstore-Infra/provider.tf @@ -0,0 +1,14 @@ +provider "aws" { + region = var.region +} + + +terraform { + backend "s3" { + + bucket = "main-project-bucket" + key = "bookstore/terraform.tfstate" + region = "us-east-1" + encrypt = true + } +} \ No newline at end of file diff --git a/Bookstore-Infra/rds.tf b/Bookstore-Infra/rds.tf new file mode 100644 index 0000000..e294eea --- /dev/null +++ b/Bookstore-Infra/rds.tf @@ -0,0 +1,24 @@ +resource "aws_db_subnet_group" "db_subnet_group" { + subnet_ids = [for subnet in aws_subnet.private_subnets : subnet.id] +} +resource "aws_db_instance" "postgres" { + identifier = "postgres" + allocated_storage = 7 + backup_retention_period = 2 + backup_window = "01:00-01:30" + maintenance_window = "sun:03:00-sun:03:30" + engine = "postgres" + engine_version = "14.7" + instance_class = var.db_instance_class + username = aws_secretsmanager_secret.unique_username_secret.name + password = aws_secretsmanager_secret.unique_password_secret.name + + # username = aws_secretsmanager_secret_version.db_username_version_new1.arn + # password = aws_secretsmanager_secret_version.db_password_version_new1.arn + port = "5432" + db_subnet_group_name = aws_db_subnet_group.db_subnet_group.id + vpc_security_group_ids = [aws_security_group.rds_sg.id, aws_security_group.ecs_sg.id] + skip_final_snapshot = true + final_snapshot_identifier = "worker-final" + publicly_accessible = false +} diff --git a/Bookstore-Infra/role.tf b/Bookstore-Infra/role.tf new file mode 100644 index 0000000..04a1fd1 --- /dev/null +++ b/Bookstore-Infra/role.tf @@ -0,0 +1,25 @@ +data "aws_iam_policy_document" "ecs_agent" { + statement { + actions = ["sts:AssumeRole"] + + principals { + type = "Service" + identifiers = ["ec2.amazonaws.com"] + } + } +} + +resource "aws_iam_role" "ecs_agent" { + name = "ecs-agent" + assume_role_policy = data.aws_iam_policy_document.ecs_agent.json +} + +resource "aws_iam_instance_profile" "ecs_agent" { + name = "ecs-agent" + role = aws_iam_role.ecs_agent.name +} + +resource "aws_iam_role_policy_attachment" "ecs_agent" { + role = aws_iam_role.ecs_agent.name + policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role" +} \ No newline at end of file diff --git a/Bookstore-Infra/secret_password.json b/Bookstore-Infra/secret_password.json new file mode 100644 index 0000000..ae76ede --- /dev/null +++ b/Bookstore-Infra/secret_password.json @@ -0,0 +1,4 @@ +{ + "password": "AdminBookstore@001" + } + \ No newline at end of file diff --git a/Bookstore-Infra/secret_username.json b/Bookstore-Infra/secret_username.json new file mode 100644 index 0000000..3c5f3a8 --- /dev/null +++ b/Bookstore-Infra/secret_username.json @@ -0,0 +1,4 @@ +{ + "username": "admin" + } + \ No newline at end of file diff --git a/Bookstore-Infra/secrete.tf b/Bookstore-Infra/secrete.tf new file mode 100644 index 0000000..1dd77b1 --- /dev/null +++ b/Bookstore-Infra/secrete.tf @@ -0,0 +1,64 @@ +# # Define the Secrets Manager secret for username +# resource "aws_secretsmanager_secret" "db_username_secret_with_new_name" { +# name = "TopSecretUsernameSecret" # Change the name +# } + +# # Load the template file for username +# data "template_file" "username" { +# template = file("secret_username.json") +# } + +# # Create the secret version for username +# resource "aws_secretsmanager_secret_version" "db_username_version_with_new_name" { +# secret_id = aws_secretsmanager_secret.db_username_secret_with_new_name.id +# secret_string = data.template_file.username.rendered +# } + +# # Define the Secrets Manager secret for password +# resource "aws_secretsmanager_secret" "db_password_secret_with_new_name" { +# name = "ClassifiedPasswordSecret" # Change the name +# } + +# # Load the template file for password +# data "template_file" "password" { +# template = file("secret_password.json") +# } + +# # Create the secret version for password +# resource "aws_secretsmanager_secret_version" "db_password_version_with_new_name" { +# secret_id = aws_secretsmanager_secret.db_password_secret_with_new_name.id +# secret_string = data.template_file.password.rendered +# } + + +# Define the Secrets Manager secret for username +resource "aws_secretsmanager_secret" "unique_username_secret" { + name = "TopSecretUsernameSecret" +} + +# Load the template file for username +data "template_file" "username" { + template = file("secret_username.json") +} + +# Create the secret version for username +resource "aws_secretsmanager_secret_version" "unique_username_version" { + secret_id = aws_secretsmanager_secret.unique_username_secret.id + secret_string = data.template_file.username.rendered +} + +# Define the Secrets Manager secret for password +resource "aws_secretsmanager_secret" "unique_password_secret" { + name = "ClassifiedPasswordSecret" +} + +# Load the template file for password +data "template_file" "password" { + template = file("secret_password.json") +} + +# Create the secret version for password +resource "aws_secretsmanager_secret_version" "unique_password_version" { + secret_id = aws_secretsmanager_secret.unique_password_secret.id + secret_string = data.template_file.password.rendered +} diff --git a/Bookstore-Infra/security_group.tf b/Bookstore-Infra/security_group.tf new file mode 100644 index 0000000..dc86d97 --- /dev/null +++ b/Bookstore-Infra/security_group.tf @@ -0,0 +1,46 @@ +resource "aws_security_group" "ecs_sg" { + vpc_id = aws_vpc.main.id + name = "VPC_SG" + + + dynamic "ingress" { + for_each = [22, 80, 443, 3306, 5432] + iterator = port + content { + description = "sg" + from_port = port.value + to_port = port.value + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + } + + dynamic "egress" { + for_each = [1] + content { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + } +} + +resource "aws_security_group" "rds_sg" { + vpc_id = aws_vpc.main.id + + ingress { + protocol = "tcp" + from_port = 5432 + to_port = 5432 + cidr_blocks = ["0.0.0.0/0"] + security_groups = [aws_security_group.ecs_sg.id] + } + + egress { + from_port = 0 + to_port = 65535 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } +} \ No newline at end of file diff --git a/Bookstore-Infra/variable.tf b/Bookstore-Infra/variable.tf new file mode 100644 index 0000000..2547675 --- /dev/null +++ b/Bookstore-Infra/variable.tf @@ -0,0 +1,111 @@ +variable "project" { + type = string + description = "Project Title" + default = "bookstore" +} + + +variable "region" { + type = string + description = "Default Region for Project" + default = "us-east-1" +} +variable "vpc_cidr" { + type = string + description = "Project Title" + default = "10.0.0.0/16" +} + +variable "public_subnet_cidrs" { + type = list(string) + description = "Public Subnet CIDR values" + default = ["10.0.1.0/24", "10.0.3.0/24", "10.0.5.0/24"] +} + +variable "private_subnet_cidrs" { + type = list(string) + description = "Private Subnet CIDR values" + default = ["10.0.2.0/24", "10.0.4.0/24", "10.0.6.0/24"] +} + +variable "azs" { + type = list(string) + description = "Availability Zones" + default = ["us-east-1a", "us-east-1b", "us-east-1c"] +} + +variable "AMI" { + type = string + description = "Project Title" + default = "ami-0eab4597a7e92d75d" #"ami-0077a350292f081f3"#"ami-054c337ee5048c313"#"ami-0dfda8a3ee7678578" #"ami-0dce57de6dcc3a6cc" +} + + +# variable "lg_user_data" { +# type = string +# description = "Project Title" +# default = <<-EOT +# #!/bin/bash +# echo ECS_CLUSTER=my-cluster >> /etc/ecs/ecs.config +# EOT +# } +# variable "lg_user_data" { +# type = string +# description = "Project Title" +# default = "#!/bin/bash \n echo ECS_CLUSTER=my-cluster >> /etc/ecs/ecs.config" +# } + +variable "asg_instance_type" { + type = string + description = "Instance Type" + default = "t3.micro" +} + +variable "key_name" { + type = string + description = "Key Name" + # default = "./bookstore.pub" + default = "Rumit_Key" +} + +########################################################################## + +variable "desired_capacity" { + type = number + description = "Desired number of Instances ASG can Create." + default = 1 +} + +variable "min_size" { + type = number + description = "Minimum Number of Instances ASG can Create." + default = 1 +} + +variable "max_size" { + type = number + description = "Maximum Number of Instances ASG can Create." + default = 5 +} + +variable "health_check_grace_period" { + type = number + description = "Instance Type" + default = 300 +} + +variable "db_instance_class" { + type = string + description = "Instance Type" + default = "db.t3.micro" +} + +variable "repository_names" { + type = list(string) + default = [ "host", "web", "auth", "migrator"] +} + +# variable "launch_configuration_name" { +# description = "output of the AWS launch_configuration" + +# } \ No newline at end of file diff --git a/Bookstore-Infra/vpc.tf b/Bookstore-Infra/vpc.tf new file mode 100644 index 0000000..bc4cc09 --- /dev/null +++ b/Bookstore-Infra/vpc.tf @@ -0,0 +1,98 @@ +resource "aws_vpc" "main" { + cidr_block = var.vpc_cidr + enable_dns_hostnames = true + enable_dns_support = true + tags = { + Name = "${var.project}-VPC" + Createdwith = "Terraform" + } +} + +resource "aws_subnet" "public_subnets" { + count = length(var.public_subnet_cidrs) + vpc_id = aws_vpc.main.id + cidr_block = element(var.public_subnet_cidrs, count.index) + availability_zone = element(var.azs, count.index) + map_public_ip_on_launch = true + tags = { + Name = "${var.project}Public-Subnet-${count.index + 1}" + Createdwith = "Terraform" + } +} + +resource "aws_subnet" "private_subnets" { + count = length(var.private_subnet_cidrs) + vpc_id = aws_vpc.main.id + cidr_block = element(var.private_subnet_cidrs, count.index) + availability_zone = element(var.azs, count.index) + tags = { + Name = "${var.project}-Private-Subnet-${count.index + 1}" + Createdwith = "Terraform" + } +} + +resource "aws_internet_gateway" "gw" { + vpc_id = aws_vpc.main.id + tags = { + Name = "${var.project}-IG" + Createdwith = "Terraform" + } +} + + +# Elastic-IP (eip) for NAT +resource "aws_eip" "nat_eip" { + domain = "vpc" + depends_on = [aws_internet_gateway.gw] + tags = { + Name = "${var.project}-EIP" + Createdwith = "Terraform" + } + +} + +# NAT +resource "aws_nat_gateway" "nat" { + allocation_id = aws_eip.nat_eip.id + subnet_id = element(aws_subnet.public_subnets.*.id, 0) + tags = { + Name = "${var.project}-NAT" + Createdwith = "Terraform" + } +} + +resource "aws_route_table" "public_rt" { + vpc_id = aws_vpc.main.id + route { + cidr_block = "0.0.0.0/0" + gateway_id = aws_internet_gateway.gw.id + } + tags = { + Name = "${var.project}-Public-Route-Table" + Createdwith = "Terraform" + } +} + +resource "aws_route_table" "private_rt" { + vpc_id = aws_vpc.main.id + route { + cidr_block = "0.0.0.0/0" + nat_gateway_id = aws_nat_gateway.nat.id + } + tags = { + Name = "${var.project}-Private-Route-Table" + Createdwith = "Terraform" + } +} + +resource "aws_route_table_association" "public_subnet_asso" { + count = length(var.public_subnet_cidrs) + subnet_id = element(aws_subnet.public_subnets[*].id, count.index) + route_table_id = aws_route_table.public_rt.id +} + +resource "aws_route_table_association" "private_subnet_asso" { + count = length(var.public_subnet_cidrs) + subnet_id = element(aws_subnet.private_subnets[*].id, count.index) + route_table_id = aws_route_table.private_rt.id +} diff --git a/Project-work-Documentation.docx b/Project-work-Documentation.docx new file mode 100644 index 0000000..8959ecf Binary files /dev/null and b/Project-work-Documentation.docx differ