Last active
April 30, 2023 09:08
Revisions
-
gunzip revised this gist
Apr 30, 2023 . 1 changed file with 506 additions and 49 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,79 +1,536 @@ # Create the whole infrastructure to run the Strapi CMS webserver # and the static frontend that uses nextjs. # Terraform provider configuration variable "db_name" { description = "The name of the database" default = "my_database" } variable "db_username" { description = "The username for the database" default = "my_username" } resource "random_password" "db_password" { length = 16 special = true } variable "db_password" { description = "The password for the database" default = random_password.db_password.result } #################################### resource "aws_vpc" "vpc" { cidr_block = "10.0.0.0/16" tags = { Name = "vpc" ResourceGroup = "devportal" } } resource "aws_subnet" "strapi_subnet" { vpc_id = aws_vpc.vpc.id cidr_block = "10.0.1.0/24" tags = { Name = "strapi-subnet" ResourceGroup = "devportal" } } resource "aws_db_subnet_group" "strapi_subnet_group" { name = "strapi-subnet-group" subnet_ids = [aws_subnet.strapi_subnet.id] description = "Strapi subnet group" tags = { ResourceGroup = "devportal" } } ############ # Define a CloudWatch Logs log group for the ECS task resource "aws_cloudwatch_log_group" "strapi_logs" { name = "/ecs/strapi-logs" retention_in_days = 7 } resource "aws_ecs_cluster" "strapi_cluster" { name = "strapi-cluster" } # Create an IAM policy to allow ECS tasks to write logs to CloudWatch resource "aws_iam_policy" "ecs_task_logging_policy" { name = "ecs-task-logging-policy" policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = [ "logs:CreateLogStream", "logs:PutLogEvents" ] Resource = "arn:aws:logs:*:*:*" } ] }) } # Create an IAM role for ECS task execution resource "aws_iam_role" "ecs_task_execution_role" { name = "ecs-task-execution-role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Principal = { Service = "ecs-tasks.amazonaws.com" } Action = "sts:AssumeRole" } ] }) # Attach the task execution role to the logging policy policy = aws_iam_policy.ecs_task_logging_policy.arn } # This allows traffic from the ALB to the ECS service # and from the ECS service to the database resource "aws_security_group" "ecs_security_group" { name_prefix = "ecs-" vpc_id = aws_vpc.vpc.id ingress { from_port = 0 to_port = 65535 protocol = "tcp" cidr_blocks = [ aws_vpc.vpc.cidr_block, ] } tags = { Environment = "production" ResourceGroup = "devportal" } } # This defines the container that will run in the ECS service resource "aws_ecs_task_definition" "strapi_task" { family = "strapi-task" execution_role_arn = aws_iam_role.ecs_task_execution_role.arn network_mode = "awsvpc" requires_compatibilities = ["FARGATE"] container_definitions = jsonencode([ { name = "strapi-container" image = "docker.pkg.github.com/OWNER/REPO/IMAGE:TAG" portMappings = [ { containerPort = 1337 protocol = "tcp" } ] environment = [ { name = "DATABASE_URL" value = format("postgres://%s:%s@%s/%s", aws_db_instance.strapi_db.username, aws_db_instance.strapi_db.password, aws_db_instance.strapi_db.endpoint, aws_db_instance.strapi_db.db_name) } ] logConfiguration = { logDriver = "awslogs" options = { awslogs-group = aws_cloudwatch_log_group.strapi_logs.name awslogs-region = "eu-west-1" awslogs-stream-prefix = "ecs" } } } ]) } resource "aws_ecs_service" "strapi_service" { name = "strapi-service" cluster = aws_ecs_cluster.strapi_cluster.id task_definition = aws_ecs_task_definition.strapi_task.arn launch_type = "FARGATE" desired_count = 1 network_configuration { assign_public_ip = true security_groups = [aws_security_group.ecs_security_group.id] subnets = [ aws_subnet.strapi_subnet.id ] } load_balancer { target_group_arn = aws_lb_target_group.strapi_target_group.arn container_name = "strapi-container" container_port = 1337 } } resource "aws_lb_target_group" "strapi_target_group" { name_prefix = "strapi-target-group" port = 1337 protocol = "HTTP" target_type = "ip" vpc_id = aws_vpc.vpc.id health_check { interval = 30 path = "/" port = "traffic-port" protocol = "HTTP" timeout = 10 healthy_threshold = 3 unhealthy_threshold = 3 matcher = "200" } tags = { Environment = "production" ResourceGroup = "devportal" } } ############## resource "aws_security_group" "rds_security_group" { name_prefix = "rds-" vpc_id = aws_vpc.vpc.id ingress { from_port = 5432 to_port = 5432 protocol = "tcp" security_groups = [ aws_security_group.ecs_security_group.id ] } tags = { Environment = "production" ResourceGroup = "devportal" } } resource "aws_db_instance" "strapi_db" { allocated_storage = 10 # GB engine = "postgres" engine_version = "13.2" instance_class = "db.t2.micro" identifier = "strapi-db" name = var.db_name username = var.db_username password = var.db_password skip_final_snapshot = false db_subnet_group_name = aws_db_subnet_group.strapi_subnet_group.name vpc_security_group_ids = [aws_security_group.rds_security_group.id] backup_retention_period = 7 # retention period in days preferred_backup_window = "01:00-02:00" # backup window in UTC time backup_window = "02:00-03:00" # maintenance window in UTC time tags = { Name = "strapi-db" ResourceGroup = "devportal" } } resource "aws_s3_bucket" "static_files_bucket" { bucket = "static-files-bucket" acl = "public-read" tags = { Name = "static-files-bucket" Environment = "production" ResourceGroup = "devportal" } } # CDN resource "aws_cloudfront_distribution" "cdn" { origin { domain_name = aws_s3_bucket.static_files_bucket.bucket_regional_domain_name origin_id = "frontend-static-files-bucket" } default_cache_behavior { allowed_methods = ["GET", "HEAD", "OPTIONS"] cached_methods = ["GET", "HEAD", "OPTIONS"] target_origin_id = "S3-my-static-files-bucket" forwarded_values { query_string = false cookies { forward = "none" } } viewer_protocol_policy = "redirect-to-https" min_ttl = 0 default_ttl = 3600 max_ttl = 86400 } enabled = true is_ipv6_enabled = true price_class = "PriceClass_100" default_root_object = "index.html" tags = { Name = "cloudfront-distribution" Environment = "production" ResourceGroup = "devportal" } } resource "aws_cloudwatch_dashboard" "dashboard" { dashboard_name = "my-dashboard" dashboard_body = jsonencode({ "widgets" : [ { "type" : "metric", "x" : 0, "y" : 0, "width" : 6, "height" : 6, "properties" : { "metrics" : [ [ "AWS/EC2", "CPUUtilization", "InstanceId", "${aws_ecs_task_definition.strapi_task.family}:*" ] ], "period" : 300, "stat" : "Average", "region" : "eu-west-1", "title" : "CPU Utilization" } }, { "type" : "metric", "x" : 0, "y" : 6, "width" : 6, "height" : 6, "properties" : { "metrics" : [ [ "AWS/ECS", "CPUUtilization", "ClusterName", "${aws_ecs_cluster.strapi_cluster.name}", "ServiceName", "${aws_ecs_service.strapi_service.name}" ] ], "period" : 300, "stat" : "Average", "region" : "eu-west-1", "title" : "ECS CPU Utilization" } }, { "type" : "metric", "x" : 6, "y" : 0, "width" : 6, "height" : 6, "properties" : { "metrics" : [ [ "AWS/EC2", "NetworkIn", "InstanceId", "${aws_ecs_task_definition.strapi_task.family}:*" ], [ "AWS/EC2", "NetworkOut", "InstanceId", "${aws_ecs_task_definition.strapi_task.family}:*" ] ], "period" : 300, "stat" : "Sum", "region" : "eu-west-1", "title" : "Network Traffic" } }, { "type" : "metric", "x" : 6, "y" : 6, "width" : 6, "height" : 6, "properties" : { "metrics" : [ [ "AWS/ECS", "MemoryUtilization", "ClusterName", "${aws_ecs_cluster.strapi_cluster.name}", "ServiceName", "${aws_ecs_service.strapi_service.name}" ] ], "period" : 300, "stat" : "Average", "region" : "eu-west-1", "title" : "ECS Memory Utilization" } } ] }) tags = { ResourceGroup = "devportal" } } # Route53 resource "aws_route53_zone" "dns_zone" { name = "example.com" tags = { ResourceGroup = "devportal" } } resource "aws_route53_record" "domain_record" { zone_id = aws_route53_zone.domain.zone_id name = "strapi.example.com" type = "A" alias { name = aws_lb.alb.dns_name zone_id = aws_lb.alb.zone_id evaluate_target_health = false } tags = { ResourceGroup = "devportal" } } resource "aws_acm_certificate" "cert" { domain_name = "strapi.example.com" validation_method = "DNS" tags = { Terraform = "true" } lifecycle { create_before_destroy = true } } resource "aws_acm_certificate_validation" "cert_validation" { certificate_arn = aws_acm_certificate.cert.arn validation_record_fqdns = aws_route53_record.cert_validation.fqdn } resource "aws_route53_record" "cert_validation" { for_each = { for dvo in aws_acm_certificate.cert.domain_validation_options : dvo.domain_name => { name = dvo.resource_record_name record = dvo.resource_record_value type = dvo.resource_record_type } } allow_overwrite = true name = each.value.name records = [each.value.record] ttl = 60 type = each.value.type zone_id = data.aws_route53_zone.dns_zone.zone_id } resource "aws_security_group" "alb" { name = "alb_security_group" description = "Allow inbound traffic for ALB" ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } ingress { from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } } resource "aws_lb" "alb" { name = "strapi-alb" internal = false load_balancer_type = "application" security_groups = [aws_security_group.alb.id] # security_groups = [aws_security_group.ecs_security_group.id] subnets = [ aws_subnet.strapi_subnet.id ] } resource "aws_lb_listener" "https" { load_balancer_arn = aws_lb.alb.arn port = 443 protocol = "HTTPS" ssl_policy = "ELBSecurityPolicy-TLS-1-2-2017-01" certificate_arn = aws_acm_certificate_validation.cert_validation.certificate_arn default_action { type = "forward" target_group_arn = aws_lb_target_group.strapi_target_group.arn } } resource "aws_route53_record" "strapi" { name = "strapi.example.com" type = "A" zone_id = aws_route53_zone.dns_zone.zone_id alias { name = aws_lb.alb.dns_name zone_id = aws_lb.alb.zone_id evaluate_target_health = false } } #################### # output "strapi_public_ip" { # value = aws_eip.strapi_eip.public_ip # } # output "strapi_dashboard_url" { # value = "http://strapi.example.com:1337/admin" # } -
gunzip revised this gist
Apr 30, 2023 . 1 changed file with 53 additions and 479 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,49 +1,65 @@ # Uncomment the following to use S3 as a backend for Terraform state. # You will need to create the S3 bucket and DynamoDB table first. # terraform { # backend "s3" { # bucket = "devportal-terraform-state" # key = "terraform.tfstate" # region = "eu-west-1" # dynamodb_table = "terraform-state-lock" # kms_key_id = "alias/terraform-bucket-key" # encrypt = true # } # } resource "aws_kms_key" "terraform_bucket_key" { description = "This key is used to encrypt bucket objects" deletion_window_in_days = 30 enable_key_rotation = true } resource "aws_kms_alias" "key_alias" { name = "alias/terraform-bucket-key" target_key_id = aws_kms_key.terraform-bucket-key.key_id } resource "aws_s3_bucket" "tf_state_bucket" { bucket = "devportal-terraform-state" acl = "private" lifecycle { prevent_destroy = true } server_side_encryption_configuration { rule { apply_server_side_encryption_by_default { kms_master_key_id = aws_kms_key.terraform_bucket_key.arn sse_algorithm = "aws:kms" } } } } resource "aws_s3_bucket_versioning" "tf_state_bucket_versioning" { bucket = aws_s3_bucket.tf_state_bucket.id versioning_configuration { status = "Enabled" } } resource "aws_s3_bucket_public_access_block" "block" { bucket = aws_s3_bucket.tf_state_bucket.id block_public_acls = true block_public_policy = true ignore_public_acls = true restrict_public_buckets = true } resource "aws_dynamodb_table" "lock_table" { name = "terraform-state-lock" hash_key = "LockID" read_capacity = 1 write_capacity = 1 @@ -52,454 +68,12 @@ resource "aws_dynamodb_table" "lock_table" { type = "S" } lifecycle { prevent_destroy = true } tags = { Environment = "production" ResourceGroup = "devportal" } } -
gunzip revised this gist
Apr 29, 2023 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -466,7 +466,7 @@ resource "aws_route53_record" "domain_record" { name = "strapi.example.com" type = "A" ttl = "300" records = ["${aws_eip.strapi_eip.public_ip}"] tags = { ResourceGroup = "devportal" } -
gunzip created this gist
Apr 29, 2023 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,505 @@ # Create the whole infrastructure to run the Strapi CMS webserver # and the static frontend that uses nextjs. # Terraform provider configuration terraform { required_version = "~> 1.2.0" backend "s3" { bucket = "devportal-terraform-state" key = "devportal/terraform.tfstate" region = "eu-west-1" } backend "dynamodb" { table = "terraform-state-lock" hash_key = "devportal-lockid" max_lock_time = "10m" endpoint = "dynamodb.eu-west-1.amazonaws.com" } required_providers { aws = { source = "hashicorp/aws" version = "~> 4.0" } dynamodb = { source = "hashicorp/aws" version = "~> 4.0" } } } provider "aws" { region = "eu-west-1" } provider "dynamodb" { region = "eu-west-1" endpoint = "dynamodb.eu-west-1.amazonaws.com" } resource "aws_dynamodb_table" "lock_table" { name = "terraform-state-lock" hash_key = "devportal-lockid" read_capacity = 1 write_capacity = 1 attribute { name = "LockID" type = "S" } tags = { Environment = "production" ResourceGroup = "devportal" } } #################################### resource "aws_vpc" "vpc" { cidr_block = "10.0.0.0/16" tags = { Name = "vpc" ResourceGroup = "devportal" } } resource "aws_subnet" "strapi_subnet" { vpc_id = aws_vpc.vpc.id cidr_block = "10.0.1.0/24" tags = { Name = "strapi-subnet" ResourceGroup = "devportal" } } resource "aws_db_subnet_group" "strapi_subnet_group" { name = "strapi-subnet-group" subnet_ids = [aws_subnet.strapi_subnet.id] description = "Strapi subnet group" tags = { ResourceGroup = "devportal" } } ############ # Define a CloudWatch Logs log group for the ECS task resource "aws_cloudwatch_log_group" "strapi_logs" { name = "/ecs/strapi-logs" retention_in_days = 7 } resource "aws_ecs_cluster" "strapi_cluster" { name = "strapi-cluster" } # Create an IAM policy to allow ECS tasks to write logs to CloudWatch resource "aws_iam_policy" "ecs_task_logging_policy" { name = "ecs-task-logging-policy" policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = [ "logs:CreateLogStream", "logs:PutLogEvents" ] Resource = "arn:aws:logs:*:*:*" } ] }) } # Create an IAM role for ECS task execution resource "aws_iam_role" "ecs_task_execution_role" { name = "ecs-task-execution-role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Principal = { Service = "ecs-tasks.amazonaws.com" } Action = "sts:AssumeRole" } ] }) # Attach the task execution role to the logging policy policy = aws_iam_policy.ecs_task_logging_policy.arn } resource "aws_security_group" "ecs_security_group" { name_prefix = "ecs-" vpc_id = aws_vpc.vpc.id ingress { from_port = 0 to_port = 65535 protocol = "tcp" cidr_blocks = [ aws_vpc.vpc.cidr_block, ] } tags = { Environment = "production" ResourceGroup = "devportal" } } # Create a task definition for the ECS service resource "aws_ecs_task_definition" "strapi_task" { family = "strapi-task" execution_role_arn = aws_iam_role.ecs_task_execution_role.arn network_mode = "awsvpc" requires_compatibilities = ["FARGATE"] container_definitions = jsonencode([ { name = "strapi-container" image = "docker.pkg.github.com/OWNER/REPO/IMAGE:TAG" portMappings = [ { containerPort = 1337 protocol = "tcp" } ] environment = [ { name = "DATABASE_URL" value = format("postgres://%s:%s@%s/%s", aws_db_instance.strapi_db.username, aws_db_instance.strapi_db.password, aws_db_instance.strapi_db.endpoint, aws_db_instance.strapi_db.db_name) } ] } ]) network_configuration { subnets = [aws_subnet.strapi_subnet.id] security_groups = [aws_security_group.ecs_security_group.id] assign_public_ip = "DISABLED" } logConfiguration = { logDriver = "awslogs" options = { "awslogs-group" = aws_cloudwatch_log_group.strapi_logs.name "awslogs-region" = aws_cloudwatch_log_group.strapi_logs.region "awslogs-stream-prefix" = "strapi-container" } } } resource "aws_ecs_service" "strapi_service" { name = "strapi-service" cluster = aws_ecs_cluster.strapi_cluster.id task_definition = aws_ecs_task_definition.strapi_task.arn desired_count = 1 network_configuration { assign_public_ip = true security_groups = [aws_security_group.ecs_security_group.id] subnets = [ aws_subnet.strapi_subnet.id ] } load_balancer { target_group_arn = aws_lb_target_group.strapi_target_group.arn container_name = "strapi-container" container_port = 1337 } depends_on = [aws_lb_target_group_attachment.strapi] } resource "aws_lb_target_group" "strapi_target_group" { name_prefix = "strapi-target-group" port = 1337 protocol = "HTTP" target_type = "ip" vpc_id = aws_vpc.vpc.id health_check { interval = 30 path = "/health" port = "traffic-port" protocol = "HTTP" timeout = 10 healthy_threshold = 3 unhealthy_threshold = 3 matcher = "200" } tags = { Environment = "production" ResourceGroup = "devportal" } } ############## resource "aws_security_group" "rds_security_group" { name_prefix = "rds-" vpc_id = aws_vpc.vpc.id ingress { from_port = 5432 to_port = 5432 protocol = "tcp" security_groups = [ aws_security_group.ecs_security_group.id ] } tags = { Environment = "production" ResourceGroup = "devportal" } } resource "aws_db_instance" "strapi_db" { allocated_storage = 10 # GB engine = "postgres" engine_version = "13.2" instance_class = "db.t2.micro" identifier = "strapi-db" name = var.db_name username = var.db_username password = var.db_password skip_final_snapshot = false db_subnet_group_name = aws_db_subnet_group.strapi_subnet_group.name vpc_security_group_ids = [aws_security_group.rds_security_group.id] backup_retention_period = 7 # retention period in days preferred_backup_window = "01:00-02:00" # backup window in UTC time backup_window = "02:00-03:00" # maintenance window in UTC time tags = { Name = "strapi-db" ResourceGroup = "devportal" } } resource "aws_s3_bucket" "static_files_bucket" { bucket = "static-files-bucket" acl = "public-read" tags = { Name = "static-files-bucket" Environment = "production" ResourceGroup = "devportal" } } # CDN resource "aws_cloudfront_distribution" "cdn" { origin { domain_name = aws_s3_bucket.static_files_bucket.bucket_regional_domain_name origin_id = "S3-my-static-files-bucket" } default_cache_behavior { allowed_methods = ["GET", "HEAD", "OPTIONS"] cached_methods = ["GET", "HEAD", "OPTIONS"] target_origin_id = "S3-my-static-files-bucket" forwarded_values { query_string = false cookies { forward = "none" } } viewer_protocol_policy = "redirect-to-https" min_ttl = 0 default_ttl = 3600 max_ttl = 86400 } enabled = true is_ipv6_enabled = true price_class = "PriceClass_100" default_root_object = "index.html" tags = { Name = "cloudfront-distribution" Environment = "production" ResourceGroup = "devportal" } } resource "aws_cloudwatch_dashboard" "dashboard" { dashboard_name = "my-dashboard" dashboard_body = jsonencode({ "widgets" : [ { "type" : "metric", "x" : 0, "y" : 0, "width" : 6, "height" : 6, "properties" : { "metrics" : [ [ "AWS/EC2", "CPUUtilization", "InstanceId", "${aws_ecs_task_definition.strapi_task.family}:*" ] ], "period" : 300, "stat" : "Average", "region" : "eu-west-1", "title" : "CPU Utilization" } }, { "type" : "metric", "x" : 0, "y" : 6, "width" : 6, "height" : 6, "properties" : { "metrics" : [ [ "AWS/ECS", "CPUUtilization", "ClusterName", "${aws_ecs_cluster.strapi_cluster.name}", "ServiceName", "${aws_ecs_service.strapi_service.name}" ] ], "period" : 300, "stat" : "Average", "region" : "eu-west-1", "title" : "ECS CPU Utilization" } }, { "type" : "metric", "x" : 6, "y" : 0, "width" : 6, "height" : 6, "properties" : { "metrics" : [ [ "AWS/EC2", "NetworkIn", "InstanceId", "${aws_ecs_task_definition.strapi_task.family}:*" ], [ "AWS/EC2", "NetworkOut", "InstanceId", "${aws_ecs_task_definition.strapi_task.family}:*" ] ], "period" : 300, "stat" : "Sum", "region" : "eu-west-1", "title" : "Network Traffic" } }, { "type" : "metric", "x" : 6, "y" : 6, "width" : 6, "height" : 6, "properties" : { "metrics" : [ [ "AWS/ECS", "MemoryUtilization", "ClusterName", "${aws_ecs_cluster.strapi_cluster.name}", "ServiceName", "${aws_ecs_service.strapi_service.name}" ] ], "period" : 300, "stat" : "Average", "region" : "eu-west-1", "title" : "ECS Memory Utilization" } } ] }) tags = { ResourceGroup = "devportal" } } # Route53 resource "aws_route53_zone" "domain" { name = "example.com." tags = { ResourceGroup = "devportal" } } resource "aws_route53_record" "domain_record" { zone_id = aws_route53_zone.domain.zone_id name = "strapi.example.com" type = "A" ttl = "300" records = ["${aws_instance.webserver.private_ip}"] tags = { ResourceGroup = "devportal" } } #################### # Elastic IP for EC2 instance resource "aws_eip" "strapi_eip" { vpc = true tags = { Name = "strapi-eip" ResourceGroup = "devportal" } } # Associate Elastic IP with ECS instance resource "aws_eip_association" "strapi_eip_association" { instance_id = aws_ecs_instance.strapi_instance.id allocation_id = aws_eip.strapi_eip.id tags = { Name = "strapi-eip-association" ResourceGroup = "devportal" } } # Outputs output "strapi_public_ip" { value = aws_eip.strapi_eip.public_ip } output "strapi_dashboard_url" { value = "https://${aws_eip.strapi_eip.public_ip}:1337/admin" }