From 349665a585b63e0ee294d7ebf20b9e9bb0a1a5b8 Mon Sep 17 00:00:00 2001 From: gabriel becker Date: Tue, 15 Aug 2023 17:08:02 +1000 Subject: [PATCH] start multi availability region ecs fargate --- README.md | 4 +- aws_fargate/config.tf | 13 ++++ aws_fargate/iam.tf | 39 ++++++++++++ aws_fargate/main.tf | 39 ++++++++++++ aws_fargate/network.tf | 129 +++++++++++++++++++++++++++++++++++++++ aws_fargate/outputs.tf | 0 aws_fargate/variables.tf | 25 ++++++++ 7 files changed, 248 insertions(+), 1 deletion(-) create mode 100644 aws_fargate/config.tf create mode 100644 aws_fargate/iam.tf create mode 100644 aws_fargate/main.tf create mode 100644 aws_fargate/network.tf create mode 100644 aws_fargate/outputs.tf create mode 100644 aws_fargate/variables.tf diff --git a/README.md b/README.md index 9d0168f..54b3b28 100644 --- a/README.md +++ b/README.md @@ -3,4 +3,6 @@ Provisioning scripts for personal learning. References - - [gruntwork](https://blog.gruntwork.io/an-introduction-to-terraform-f17df9c6d180) \ No newline at end of file + - [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 diff --git a/aws_fargate/config.tf b/aws_fargate/config.tf new file mode 100644 index 0000000..1964e32 --- /dev/null +++ b/aws_fargate/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/iam.tf b/aws_fargate/iam.tf new file mode 100644 index 0000000..7986343 --- /dev/null +++ b/aws_fargate/iam.tf @@ -0,0 +1,39 @@ +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 +} + +data "aws_iam_policy_document" "api_exec_assume_role" { + statement { + actions = ["sts:AssumeRole"] + principals { + type = "Service" + identifiers = ["ecs-task.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" { + statement { + effect = "Allow" + actions = [ + "ecr:GetAuthorizationToken", + "ecr:BatchCheckLayerAvailability", + "ecr:GetDownloadUrlForLayer", + "ecr:BatchGetImage", + "logs:CreateLogStream", + "logs:PutLogEvents" + ] + resources = ["*${var.project}*"] + } +} + +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 +} diff --git a/aws_fargate/main.tf b/aws_fargate/main.tf new file mode 100644 index 0000000..cf81252 --- /dev/null +++ b/aws_fargate/main.tf @@ -0,0 +1,39 @@ +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 new file mode 100644 index 0000000..b2ae27c --- /dev/null +++ b/aws_fargate/network.tf @@ -0,0 +1,129 @@ +resource "aws_vpc" "app_vpc" { + cidr_block = "10.0.0.0/16" +} + +resource "aws_subnet" "public_subnet" { + count = length(var.subnets) + + vpc_id = aws_vpc.app_vpc.id + availability_zone = element(keys(var.subnets), count.index) + cidr_block = "10.0.${count.index}.0/25" + tags = { + "Name" = "public | ${element(keys(var.subnets), count.index)}" + } +} + +resource "aws_subnet" "private_subnet" { + count = length(var.subnets) + + vpc_id = aws_vpc.app_vpc.id + availability_zone = element(keys(var.subnets), count.index) + cidr_block = "10.0.${count.index}.128/25" + tags = { + "Name" = "private | ${element(keys(var.subnets), count.index)}" + } +} + +resource "aws_route_table" "public" { + vpc_id = aws_vpc.app_vpc.id + tags = { + "Name" = "public" + } +} + +resource "aws_route_table" "private" { + vpc_id = aws_vpc.app_vpc.id + tags = { + "Name" = "private" + } +} + +resource "aws_route_table_association" "public_subnet" { + count = length(var.subnets) + + subnet_id = element(aws_subnet.public_subnet.*.id, count.index) + 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) + route_table_id = aws_route_table.private.id +} + +resource "aws_eip" "nat" { + vpc = true +} + +resource "aws_internet_gateway" "igw" { + vpc_id = aws_vpc.app_vpc.id +} + +resource "aws_nat_gateway" "ngw" { + count = length(var.subnets) + + subnet_id = element(aws_subnet.private_subnet.*.id, count.index) + allocation_id = aws_eip.nat.id + depends_on = [aws_internet_gateway.igw] +} + +resource "aws_route" "public_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" { + route_table_id = aws_route_table.private.id + destination_cidr_block = "0.0.0.0/0" + nat_gateway_id = aws_nat_gateway.ngw.id +} + +resource "aws_security_group" "http" { + name = "http" + description = "HTTP traffic" + vpc_id = aws_vpc.app_vpc.id + ingress { + from_port = 80 + to_port = 80 + protocol = "TCP" + cidr_blocks = ["0.0.0.0/0"] + } +} + +resource "aws_security_group" "https" { + name = "https" + description = "HTTPS traffic" + vpc_id = aws_vpc.app_vpc.id + ingress { + from_port = 443 + to_port = 443 + protocol = "TCP" + cidr_blocks = ["0.0.0.0/0"] + } +} + +resource "aws_security_group" "egress_all" { + name = "egress-all" + description = "Allow all outbound traffic" + vpc_id = aws_vpc.app_vpc.id + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } +} + +resource "aws_security_group" "ingress_api" { + name = "ingress-api" + description = "Allow ingress to API" + vpc_id = aws_vpc.app_vpc.id + ingress { + from_port = 3000 + to_port = 3000 + protocol = "TCP" + cidr_blocks = ["0.0.0.0/0"] + } +} diff --git a/aws_fargate/outputs.tf b/aws_fargate/outputs.tf new file mode 100644 index 0000000..e69de29 diff --git a/aws_fargate/variables.tf b/aws_fargate/variables.tf new file mode 100644 index 0000000..b4eba8d --- /dev/null +++ b/aws_fargate/variables.tf @@ -0,0 +1,25 @@ +variable "region" { + default = "us-east-1" +} + +variable "profile" { + default = "default" +} + +variable "project" { + default = "template" +} + +variable "container_image" { + default = "ghcr.io/jimmysawczuk/sun-api:latest" +} + +variable "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" + } +}