From 62c8acb0e80f4d1c82e4feb850941ca94f0ee999 Mon Sep 17 00:00:00 2001 From: gabriel becker Date: Wed, 30 Aug 2023 15:26:07 +1000 Subject: [PATCH] Functional non parametrized ecs api template --- .gitignore | 2 +- README.md | 6 +- aws_fargate/alb.tf | 40 +++++++++++ aws_fargate/config.tf | 4 +- aws_fargate/ecs.tf | 61 +++++++++++++++++ aws_fargate/iam.tf | 21 +++--- aws_fargate/main.tf | 39 ----------- aws_fargate/network.tf | 32 ++++----- aws_fargate/outputs.tf | 4 ++ aws_fargate/variables.tf | 27 ++++++-- aws_fargate_2/alb.tf | 38 +++++++++++ aws_fargate_2/config.tf | 13 ++++ aws_fargate_2/ecs.tf | 39 +++++++++++ aws_fargate_2/iam.tf | 24 +++++++ aws_fargate_2/network.tf | 132 +++++++++++++++++++++++++++++++++++++ aws_fargate_2/service.tf | 23 +++++++ aws_fargate_2/variables.tf | 44 +++++++++++++ 17 files changed, 472 insertions(+), 77 deletions(-) create mode 100644 aws_fargate/alb.tf create mode 100644 aws_fargate/ecs.tf delete mode 100644 aws_fargate/main.tf create mode 100644 aws_fargate_2/alb.tf create mode 100644 aws_fargate_2/config.tf create mode 100644 aws_fargate_2/ecs.tf create mode 100644 aws_fargate_2/iam.tf create mode 100644 aws_fargate_2/network.tf create mode 100644 aws_fargate_2/service.tf create mode 100644 aws_fargate_2/variables.tf diff --git a/.gitignore b/.gitignore index fa6e750..fc25ff6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ .terraform .terraform* -*.tfstate* +*.tfstate diff --git a/README.md b/README.md index 54b3b28..65cf78a 100644 --- a/README.md +++ b/README.md @@ -4,5 +4,7 @@ Provisioning scripts for personal learning. References - [gruntwork](https://blog.gruntwork.io/an-introduction-to-terraform-f17df9c6d180) - - https://section411.com/2019/07/hello-world/ - - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_task_definition \ No newline at end of file + - [section41 blog post](https://section411.com/2019/07/hello-world/) tutorial on aws fargate with terraform +- aws ecs task definition [[terraform]] [documentation](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_task_definition) +- [dwmkerr](https://dwmkerr.com/dynamic-and-configurable-availability-zones-in-terraform/) blog tutorial on how to use terraform interpotaion to create dynamic resources given an array of parameters +- [Setup ECS Fargate and CloudFront with Terraform](https://awstip.com/ecs-fargate-with-terraform-9cd755d46039) tutorial that works but I can't figure how its connected to all subents \ No newline at end of file diff --git a/aws_fargate/alb.tf b/aws_fargate/alb.tf new file mode 100644 index 0000000..1f9549e --- /dev/null +++ b/aws_fargate/alb.tf @@ -0,0 +1,40 @@ +resource "aws_lb_target_group" "api_lb_target" { + name = "my-api" + port = 3000 + protocol = "HTTP" + target_type = "ip" + vpc_id = aws_vpc.app_vpc.id + health_check { + enabled = true + path = "/health" + } + depends_on = [aws_alb.api_lb] +} + +resource "aws_alb" "api_lb" { + name = "${var.project}-api-lb" + internal = false + load_balancer_type = "application" + subnets = [for s in aws_subnet.public_subnet : s.id] + security_groups = [ + aws_security_group.http.id, + aws_security_group.https.id, + aws_security_group.egress_all.id, + aws_security_group.ingress_api.id, + ] + depends_on = [aws_internet_gateway.igw] +} + +resource "aws_alb_listener" "api_http_listener" { + load_balancer_arn = aws_alb.api_lb.arn + port = "80" + protocol = "HTTP" + default_action { + type = "forward" + target_group_arn = aws_lb_target_group.api_lb_target.arn + } +} + +output "alb_url" { + value = "http://${aws_alb.api_lb.dns_name}" +} diff --git a/aws_fargate/config.tf b/aws_fargate/config.tf index 1964e32..284a562 100644 --- a/aws_fargate/config.tf +++ b/aws_fargate/config.tf @@ -8,6 +8,6 @@ terraform { } provider "aws" { - region = var.region + region = var.region profile = var.profile -} \ No newline at end of file +} diff --git a/aws_fargate/ecs.tf b/aws_fargate/ecs.tf new file mode 100644 index 0000000..9fd7586 --- /dev/null +++ b/aws_fargate/ecs.tf @@ -0,0 +1,61 @@ +locals { + api_name = "${var.project}-api" +} + +resource "aws_ecs_cluster" "my_cluster" { + name = "my_cluster" +} + +resource "aws_ecs_service" "api_ecs" { + name = local.api_name + task_definition = aws_ecs_task_definition.api_task.arn + cluster = aws_ecs_cluster.my_cluster.id + launch_type = "FARGATE" + load_balancer { + target_group_arn = aws_lb_target_group.api_lb_target.arn + container_name = local.api_name + container_port = "3000" + } + desired_count = 1 + network_configuration { + assign_public_ip = false + security_groups = [ + aws_security_group.egress_all.id, + aws_security_group.ingress_api.id, + ] + subnets = [for s in aws_subnet.private_subnet : s.id] + } +} + +resource "aws_ecs_task_definition" "api_task" { + family = local.api_name + execution_role_arn = aws_iam_role.api_exec_role.arn + cpu = 256 + memory = 512 + requires_compatibilities = ["FARGATE"] + network_mode = "awsvpc" + + container_definitions = jsonencode([ + { + name = local.api_name, + image = var.container_image, + portMappings = [ + { + containerPort = 3000 + } + ], + logConfiguration = { + logDriver = "awslogs", + options = { + awslogs-region = var.region, + awslogs-group = "/ecs/${local.api_name}", + awslogs-stream-prefix = "ecs" + } + } + } + ]) +} + +resource "aws_cloudwatch_log_group" "log_group" { + name = "/ecs/${local.api_name}" +} diff --git a/aws_fargate/iam.tf b/aws_fargate/iam.tf index 7986343..68c20e0 100644 --- a/aws_fargate/iam.tf +++ b/aws_fargate/iam.tf @@ -1,24 +1,20 @@ resource "aws_iam_role" "api_exec_role" { name = "${var.project}-exec-role" - assume_role_policy = data.aws_iam_policy_document.api_exec_assume_role.json + assume_role_policy = data.aws_iam_policy_document.api_exec_assume_role_policy_statement.json } -data "aws_iam_policy_document" "api_exec_assume_role" { +data "aws_iam_policy_document" "api_exec_assume_role_policy_statement" { statement { actions = ["sts:AssumeRole"] principals { type = "Service" - identifiers = ["ecs-task.amazonaws.com"] + identifiers = ["ecs-tasks.amazonaws.com"] } } } -resource "aws_iam_role_policy_attachment" "ecs_exec_iam_attach_assume_role" { - role = aws_iam_role.api_exec_role.name - policy_arn = data.aws_iam_policy_document.api_exec_assume_role -} -data "aws_iam_policy_document" "ecs_exec_role" { +data "aws_iam_policy_document" "ecs_exec_policy_statement" { statement { effect = "Allow" actions = [ @@ -29,11 +25,16 @@ data "aws_iam_policy_document" "ecs_exec_role" { "logs:CreateLogStream", "logs:PutLogEvents" ] - resources = ["*${var.project}*"] + resources = ["*"] } } +resource "aws_iam_policy" "ecs_exec_policy" { + name = "${var.project}-ecs_exec_policy" + policy = data.aws_iam_policy_document.ecs_exec_policy_statement.json +} + resource "aws_iam_role_policy_attachment" "ecs_exec_iam_attach_rules" { role = aws_iam_role.api_exec_role.name - policy_arn = data.aws_iam_policy_document.ecs_exec_role.json + policy_arn = aws_iam_policy.ecs_exec_policy.arn } diff --git a/aws_fargate/main.tf b/aws_fargate/main.tf deleted file mode 100644 index cf81252..0000000 --- a/aws_fargate/main.tf +++ /dev/null @@ -1,39 +0,0 @@ -resource "aws_ecs_service" "api_ecs" { - name = "${var.project}-api" - task_definition = aws_ecs_task_definition.api_task.arn - launch_type = "FARGATE" -} - -resource "aws_ecs_task_definition" "api_task" { - family = "${var.project}-api" - cpu = 256 - memory = 512 - requires_compatibilities = ["FARGATE"] - network_mode = "awsvpc" - - execution_role_arn = aws_iam_role.api_exec_role.arn - - container_definitions = jsonencode([ - { - name = "${var.project}-api", - image = var.container_image, - portMappings = [ - { - containerPort = 3000 - } - ], - logConfiguration = { - logDriver = "awslogs", - options = { - awslogs-region = var.region, - awslogs-group = "/ecs/${var.project}-api", - awslogs-stream-prefix = "ecs" - } - } - } - ]) -} - -resource "aws_cloudwatch_log_group" "log_group" { - name = "/ecs/${var.project}-api" -} diff --git a/aws_fargate/network.tf b/aws_fargate/network.tf index b2ae27c..b020da2 100644 --- a/aws_fargate/network.tf +++ b/aws_fargate/network.tf @@ -3,24 +3,24 @@ resource "aws_vpc" "app_vpc" { } resource "aws_subnet" "public_subnet" { - count = length(var.subnets) + count = length(var.public_subnets) vpc_id = aws_vpc.app_vpc.id - availability_zone = element(keys(var.subnets), count.index) - cidr_block = "10.0.${count.index}.0/25" + availability_zone = element(keys(var.public_subnets), count.index) + cidr_block = element(values(var.public_subnets), count.index) tags = { - "Name" = "public | ${element(keys(var.subnets), count.index)}" + "Name" = "public | ${element(keys(var.public_subnets), count.index)}" } } resource "aws_subnet" "private_subnet" { - count = length(var.subnets) + count = length(var.private_subnets) vpc_id = aws_vpc.app_vpc.id - availability_zone = element(keys(var.subnets), count.index) - cidr_block = "10.0.${count.index}.128/25" + availability_zone = element(keys(var.private_subnets), count.index) + cidr_block = element(values(var.private_subnets), count.index) tags = { - "Name" = "private | ${element(keys(var.subnets), count.index)}" + "Name" = "private | ${element(keys(var.private_subnets), count.index)}" } } @@ -39,16 +39,12 @@ resource "aws_route_table" "private" { } resource "aws_route_table_association" "public_subnet" { - count = length(var.subnets) - - subnet_id = element(aws_subnet.public_subnet.*.id, count.index) + subnet_id = aws_subnet.public_subnet[0].id route_table_id = aws_route_table.public.id } resource "aws_route_table_association" "private_subnet" { - count = length(var.subnets) - - subnet_id = element(aws_subnet.private_subnet.*.id, count.index) + subnet_id = aws_subnet.private_subnet[0].id route_table_id = aws_route_table.private.id } @@ -61,20 +57,18 @@ resource "aws_internet_gateway" "igw" { } resource "aws_nat_gateway" "ngw" { - count = length(var.subnets) - - subnet_id = element(aws_subnet.private_subnet.*.id, count.index) + subnet_id = aws_subnet.private_subnet[0].id allocation_id = aws_eip.nat.id depends_on = [aws_internet_gateway.igw] } -resource "aws_route" "public_igw" { +resource "aws_route" "igw" { route_table_id = aws_route_table.public.id destination_cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.igw.id } -resource "aws_route" "private_ngw" { +resource "aws_route" "ngw" { route_table_id = aws_route_table.private.id destination_cidr_block = "0.0.0.0/0" nat_gateway_id = aws_nat_gateway.ngw.id diff --git a/aws_fargate/outputs.tf b/aws_fargate/outputs.tf index e69de29..b1d1d1f 100644 --- a/aws_fargate/outputs.tf +++ b/aws_fargate/outputs.tf @@ -0,0 +1,4 @@ +# output "ecs-ip" { +# description = "ecs-ip" +# value = [for s in aws_eip.nat.map : s.public_ip] +# } \ No newline at end of file diff --git a/aws_fargate/variables.tf b/aws_fargate/variables.tf index b4eba8d..7b51b5b 100644 --- a/aws_fargate/variables.tf +++ b/aws_fargate/variables.tf @@ -1,9 +1,9 @@ variable "region" { - default = "us-east-1" + default = "ap-southeast-2" } variable "profile" { - default = "default" + default = "superuser" } variable "project" { @@ -14,12 +14,31 @@ variable "container_image" { default = "ghcr.io/jimmysawczuk/sun-api:latest" } -variable "subnets" { +variable "zones" { + type = set(string) + default = [ + "ap-southeast-2a", + "ap-southeast-2b", + "ap-southeast-2c", + ] +} + +variable "public_subnets" { description = "Availability zone for instance associated with ip ranges" type = map(any) default = { "ap-southeast-2a" = "10.0.1.0/25" "ap-southeast-2b" = "10.0.2.0/25" - "ap-southeast-2c" = "10.0.1.128/25" + "ap-southeast-2c" = "10.0.3.0/25" + } +} + +variable "private_subnets" { + description = "Availability zone for instance associated with ip ranges" + type = map(any) + default = { + "ap-southeast-2a" = "10.0.1.128/25" + "ap-southeast-2b" = "10.0.2.128/25" + "ap-southeast-2c" = "10.0.3.128/25" } } diff --git a/aws_fargate_2/alb.tf b/aws_fargate_2/alb.tf new file mode 100644 index 0000000..4a0f155 --- /dev/null +++ b/aws_fargate_2/alb.tf @@ -0,0 +1,38 @@ +resource "aws_lb_target_group" "my_api" { + name = "my-api" + port = 3000 + protocol = "HTTP" + target_type = "ip" + vpc_id = aws_vpc.prod_vpc.id + health_check { + enabled = true + path = "/health" + } + depends_on = [aws_alb.my_api] +} +resource "aws_alb" "my_api" { + name = "my-api-lb" + internal = false + load_balancer_type = "application" + subnets = [ + aws_subnet.public_a.id, + aws_subnet.public_b.id + ] + security_groups = [ + aws_security_group.http.id, + aws_security_group.egress_all.id, + ] + depends_on = [aws_internet_gateway.igw] +} +resource "aws_alb_listener" "my_api_http" { + load_balancer_arn = aws_alb.my_api.arn + port = "80" + protocol = "HTTP" + default_action { + type = "forward" + target_group_arn = aws_lb_target_group.my_api.arn + } +} +output "alb_url" { + value = "http://${aws_alb.my_api.dns_name}" +} diff --git a/aws_fargate_2/config.tf b/aws_fargate_2/config.tf new file mode 100644 index 0000000..1964e32 --- /dev/null +++ b/aws_fargate_2/config.tf @@ -0,0 +1,13 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~>4.0" + } + } +} + +provider "aws" { + region = var.region + profile = var.profile +} \ No newline at end of file diff --git a/aws_fargate_2/ecs.tf b/aws_fargate_2/ecs.tf new file mode 100644 index 0000000..74767dc --- /dev/null +++ b/aws_fargate_2/ecs.tf @@ -0,0 +1,39 @@ +resource "aws_ecs_cluster" "my_cluster" { + name = "my_cluster" +} + +resource "aws_ecs_task_definition" "my_api" { + family = "my-api" + execution_role_arn = aws_iam_role.my_api_task_execution_role.arn + container_definitions = <