diff --git a/modules/aws_ec2_standalone/main.tf b/modules/aws_ec2_standalone/main.tf index 0cd4334..9d4ac2a 100644 --- a/modules/aws_ec2_standalone/main.tf +++ b/modules/aws_ec2_standalone/main.tf @@ -13,14 +13,14 @@ provider "aws" { data "aws_ami" "this" { most_recent = true # get the latest version - filter { - name = "name" - values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"] + filter { + name = "name" + values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"] } filter { - name = "virtualization-type" - values = ["hvm"] + name = "virtualization-type" + values = ["hvm"] } owners = [ diff --git a/modules/aws_ecs/ecs.tf b/modules/aws_ecs/ecs.tf index 35cad4f..da35612 100644 --- a/modules/aws_ecs/ecs.tf +++ b/modules/aws_ecs/ecs.tf @@ -23,7 +23,7 @@ resource "aws_ecs_cluster_capacity_providers" "this" { # Required setup for EC2 instances (if not using Fargate) data "aws_ami" "this" { most_recent = true # get the latest version - name_regex = "^amzn2-ami-ecs-hvm-\\d\\.\\d\\.\\d{8}-x86_64-ebs$" + name_regex = "^amzn2-ami-ecs-hvm-\\d\\.\\d\\.\\d{8}-x86_64-ebs$" filter { name = "virtualization-type" @@ -67,7 +67,7 @@ resource "aws_launch_configuration" "this" { # Allow the EC2 instances to access AWS resources on your behalf, using this instance profile and the permissions defined there iam_instance_profile = aws_iam_instance_profile.ec2[0].arn - + lifecycle { create_before_destroy = true } diff --git a/modules/aws_ecs/loadbalancers.tf b/modules/aws_ecs/loadbalancers.tf index 1dc1bb3..177752c 100644 --- a/modules/aws_ecs/loadbalancers.tf +++ b/modules/aws_ecs/loadbalancers.tf @@ -3,22 +3,54 @@ resource "aws_lb" "this" { idle_timeout = var.alb_idle_timeout security_groups = [aws_security_group.alb.id] - subnets = var.subnet_ids + subnets = var.alb_subnet_ids != null ? var.alb_subnet_ids : var.subnet_ids } -resource "aws_lb_listener" "this" { +resource "aws_lb_listener" "http" { load_balancer_arn = aws_lb.this.arn port = 80 protocol = "HTTP" + dynamic "default_action" { + for_each = var.alb_certificate_arn == null ? [1] : [] + + content { + type = "forward" + target_group_arn = aws_lb_target_group.this.arn + } + } + + dynamic "default_action" { + for_each = var.alb_certificate_arn != null ? [1] : [] + + content { + type = "redirect" + + redirect { + port = "443" + protocol = "HTTPS" + status_code = "HTTP_301" + } + } + } +} + +resource "aws_lb_listener" "https" { + count = var.alb_certificate_arn != null ? 1 : 0 + certificate_arn = var.alb_certificate_arn + load_balancer_arn = aws_lb.this.arn + port = "443" + protocol = "HTTPS" + ssl_policy = "ELBSecurityPolicy-TLS-1-2-2017-01" + default_action { - type = "forward" target_group_arn = aws_lb_target_group.this.arn + type = "forward" } } resource "aws_lb_listener_rule" "this" { - listener_arn = aws_lb_listener.this.arn + listener_arn = var.alb_certificate_arn != null ? aws_lb_listener.https[0].arn : aws_lb_listener.http.arn priority = 1 action { @@ -49,4 +81,4 @@ resource "aws_lb_target_group" "this" { healthy_threshold = 3 unhealthy_threshold = 2 } -} \ No newline at end of file +} diff --git a/modules/aws_ecs/locals.tf b/modules/aws_ecs/locals.tf index 39a8d10..5839e4e 100644 --- a/modules/aws_ecs/locals.tf +++ b/modules/aws_ecs/locals.tf @@ -49,40 +49,40 @@ locals { }, # Workflows-specific { - "name": "WORKFLOW_BACKEND_HOST", - "value": "http://workflow-backend.retoolsvc:3000" + "name" : "WORKFLOW_BACKEND_HOST", + "value" : "http://workflow-backend.retoolsvc:3000" }, { - "name": "WORKFLOW_TEMPORAL_CLUSTER_NAMESPACE", - "value": var.temporal_cluster_config.namespace + "name" : "WORKFLOW_TEMPORAL_CLUSTER_NAMESPACE", + "value" : var.temporal_cluster_config.namespace }, { - "name": "WORKFLOW_TEMPORAL_CLUSTER_FRONTEND_HOST", - "value": var.temporal_cluster_config.host + "name" : "WORKFLOW_TEMPORAL_CLUSTER_FRONTEND_HOST", + "value" : var.temporal_cluster_config.host }, { - "name": "WORKFLOW_TEMPORAL_CLUSTER_FRONTEND_PORT", - "value": var.temporal_cluster_config.port + "name" : "WORKFLOW_TEMPORAL_CLUSTER_FRONTEND_PORT", + "value" : var.temporal_cluster_config.port }, { - "name": "WORKFLOW_TEMPORAL_TLS_ENABLED", - "value": tostring(var.temporal_cluster_config.tls_enabled) + "name" : "WORKFLOW_TEMPORAL_TLS_ENABLED", + "value" : tostring(var.temporal_cluster_config.tls_enabled) } ] ) temporal_mtls_config = ( - var.temporal_cluster_config.tls_enabled && var.temporal_cluster_config.tls_crt != null && var.temporal_cluster_config.tls_key != null ? - [ - { - "name": "WORKFLOW_TEMPORAL_TLS_CRT", - "value": var.temporal_cluster_config.tls_crt - }, - { - "name": "WORKFLOW_TEMPORAL_TLS_KEY", - "value": var.temporal_cluster_config.tls_key - } - ] : - [] + var.temporal_cluster_config.tls_enabled && var.temporal_cluster_config.tls_crt != null && var.temporal_cluster_config.tls_key != null ? + [ + { + "name" : "WORKFLOW_TEMPORAL_TLS_CRT", + "value" : var.temporal_cluster_config.tls_crt + }, + { + "name" : "WORKFLOW_TEMPORAL_TLS_KEY", + "value" : var.temporal_cluster_config.tls_key + } + ] : + [] ) } diff --git a/modules/aws_ecs/main.tf b/modules/aws_ecs/main.tf index 0c81289..786be0c 100644 --- a/modules/aws_ecs/main.tf +++ b/modules/aws_ecs/main.tf @@ -17,12 +17,12 @@ resource "aws_cloudwatch_log_group" "this" { } resource "aws_db_subnet_group" "this" { - name = "${var.deployment_name}-retool" + name = "${var.deployment_name}-retool" subnet_ids = var.subnet_ids } resource "aws_db_instance" "this" { - identifier = "${var.deployment_name}-rds-instance" + identifier = "${var.deployment_name}-rds-instance" allocated_storage = 80 instance_class = var.rds_instance_class engine = "postgres" @@ -35,9 +35,13 @@ resource "aws_db_instance" "this" { vpc_security_group_ids = [aws_security_group.rds.id] db_subnet_group_name = aws_db_subnet_group.this.id performance_insights_enabled = var.rds_performance_insights_enabled - - skip_final_snapshot = true - apply_immediately = true + kms_key_id = var.rds_kms_key_id + storage_encrypted = var.rds_kms_key_id != null + backup_window = var.rds_backup_window + backup_retention_period = var.rds_backup_retention_in_days + + skip_final_snapshot = true + apply_immediately = true } resource "aws_ecs_service" "retool" { @@ -65,7 +69,7 @@ resource "aws_ecs_service" "retool" { dynamic "network_configuration" { for_each = var.launch_type == "FARGATE" ? toset([1]) : toset([]) - content { + content { subnets = var.subnet_ids security_groups = [ aws_security_group.containers.id @@ -92,7 +96,7 @@ resource "aws_ecs_service" "jobs_runner" { for_each = var.launch_type == "FARGATE" ? toset([1]) : toset([]) - content { + content { subnets = var.subnet_ids security_groups = [ aws_security_group.containers.id @@ -108,7 +112,7 @@ resource "aws_ecs_service" "workflows_backend" { cluster = aws_ecs_cluster.this.id desired_count = 1 task_definition = aws_ecs_task_definition.retool_workflows_backend[0].arn - + # Need to explictly set this in aws_ecs_service to avoid destructive behavior: https://github.com/hashicorp/terraform-provider-aws/issues/22823 capacity_provider_strategy { base = 1 @@ -123,7 +127,7 @@ resource "aws_ecs_service" "workflows_backend" { for_each = var.launch_type == "FARGATE" ? toset([1]) : toset([]) - content { + content { subnets = var.subnet_ids security_groups = [ aws_security_group.containers.id @@ -150,7 +154,7 @@ resource "aws_ecs_service" "workflows_worker" { for_each = var.launch_type == "FARGATE" ? toset([1]) : toset([]) - content { + content { subnets = var.subnet_ids security_groups = [ aws_security_group.containers.id @@ -161,13 +165,13 @@ resource "aws_ecs_service" "workflows_worker" { } resource "aws_ecs_task_definition" "retool_jobs_runner" { - family = "retool-jobs-runner" - task_role_arn = aws_iam_role.task_role.arn - execution_role_arn = var.launch_type == "FARGATE" ? aws_iam_role.execution_role[0].arn : null + family = "retool-jobs-runner" + task_role_arn = aws_iam_role.task_role.arn + execution_role_arn = var.launch_type == "FARGATE" ? aws_iam_role.execution_role[0].arn : null requires_compatibilities = var.launch_type == "FARGATE" ? ["FARGATE"] : null - network_mode = var.launch_type == "FARGATE" ? "awsvpc" : "bridge" - cpu = var.launch_type == "FARGATE" ? var.ecs_task_resource_map["jobs_runner"]["cpu"] : null - memory = var.launch_type == "FARGATE" ? var.ecs_task_resource_map["jobs_runner"]["memory"] : null + network_mode = var.launch_type == "FARGATE" ? "awsvpc" : "bridge" + cpu = var.launch_type == "FARGATE" ? var.ecs_task_resource_map["jobs_runner"]["cpu"] : null + memory = var.launch_type == "FARGATE" ? var.ecs_task_resource_map["jobs_runner"]["memory"] : null container_definitions = jsonencode( [ { @@ -211,13 +215,13 @@ resource "aws_ecs_task_definition" "retool_jobs_runner" { ) } resource "aws_ecs_task_definition" "retool" { - family = "retool" - task_role_arn = aws_iam_role.task_role.arn - execution_role_arn = var.launch_type == "FARGATE" ? aws_iam_role.execution_role[0].arn : null + family = "retool" + task_role_arn = aws_iam_role.task_role.arn + execution_role_arn = var.launch_type == "FARGATE" ? aws_iam_role.execution_role[0].arn : null requires_compatibilities = var.launch_type == "FARGATE" ? ["FARGATE"] : null - network_mode = var.launch_type == "FARGATE" ? "awsvpc" : "bridge" - cpu = var.launch_type == "FARGATE" ? var.ecs_task_resource_map["main"]["cpu"] : null - memory = var.launch_type == "FARGATE" ? var.ecs_task_resource_map["main"]["memory"] : null + network_mode = var.launch_type == "FARGATE" ? "awsvpc" : "bridge" + cpu = var.launch_type == "FARGATE" ? var.ecs_task_resource_map["main"]["cpu"] : null + memory = var.launch_type == "FARGATE" ? var.ecs_task_resource_map["main"]["memory"] : null container_definitions = jsonencode( [ { @@ -266,14 +270,14 @@ resource "aws_ecs_task_definition" "retool" { } resource "aws_ecs_task_definition" "retool_workflows_backend" { - count = var.workflows_enabled ? 1 : 0 - family = "retool-workflows-backend" - task_role_arn = aws_iam_role.task_role.arn - execution_role_arn = var.launch_type == "FARGATE" ? aws_iam_role.execution_role[0].arn : null - requires_compatibilities = var.launch_type == "FARGATE" ? ["FARGATE"] : null - network_mode = var.launch_type == "FARGATE" ? "awsvpc" : "bridge" - cpu = var.launch_type == "FARGATE" ? var.ecs_task_resource_map["workflows_backend"]["cpu"] : null - memory = var.launch_type == "FARGATE" ? var.ecs_task_resource_map["workflows_backend"]["memory"] : null + count = var.workflows_enabled ? 1 : 0 + family = "retool-workflows-backend" + task_role_arn = aws_iam_role.task_role.arn + execution_role_arn = var.launch_type == "FARGATE" ? aws_iam_role.execution_role[0].arn : null + requires_compatibilities = var.launch_type == "FARGATE" ? ["FARGATE"] : null + network_mode = var.launch_type == "FARGATE" ? "awsvpc" : "bridge" + cpu = var.launch_type == "FARGATE" ? var.ecs_task_resource_map["workflows_backend"]["cpu"] : null + memory = var.launch_type == "FARGATE" ? var.ecs_task_resource_map["workflows_backend"]["memory"] : null container_definitions = jsonencode( [ { @@ -321,14 +325,14 @@ resource "aws_ecs_task_definition" "retool_workflows_backend" { ) } resource "aws_ecs_task_definition" "retool_workflows_worker" { - count = var.workflows_enabled ? 1 : 0 - family = "retool-workflows-worker" - task_role_arn = aws_iam_role.task_role.arn - execution_role_arn = var.launch_type == "FARGATE" ? aws_iam_role.execution_role[0].arn : null - requires_compatibilities = var.launch_type == "FARGATE" ? ["FARGATE"] : null - network_mode = var.launch_type == "FARGATE" ? "awsvpc" : "bridge" - cpu = var.launch_type == "FARGATE" ? var.ecs_task_resource_map["workflows_worker"]["cpu"] : null - memory = var.launch_type == "FARGATE" ? var.ecs_task_resource_map["workflows_worker"]["memory"] : null + count = var.workflows_enabled ? 1 : 0 + family = "retool-workflows-worker" + task_role_arn = aws_iam_role.task_role.arn + execution_role_arn = var.launch_type == "FARGATE" ? aws_iam_role.execution_role[0].arn : null + requires_compatibilities = var.launch_type == "FARGATE" ? ["FARGATE"] : null + network_mode = var.launch_type == "FARGATE" ? "awsvpc" : "bridge" + cpu = var.launch_type == "FARGATE" ? var.ecs_task_resource_map["workflows_worker"]["cpu"] : null + memory = var.launch_type == "FARGATE" ? var.ecs_task_resource_map["workflows_worker"]["memory"] : null container_definitions = jsonencode( [ { @@ -381,13 +385,13 @@ resource "aws_ecs_task_definition" "retool_workflows_worker" { } resource "aws_service_discovery_private_dns_namespace" "retoolsvc" { - count = var.workflows_enabled ? 1 : 0 + count = var.workflows_enabled ? 1 : 0 name = "retoolsvc" description = "Service Discovery namespace for Retool deployment" vpc = var.vpc_id } -resource "aws_service_discovery_service" "retool_workflow_backend_service" { +resource "aws_service_discovery_service" "retool_workflow_backend_service" { count = var.workflows_enabled ? 1 : 0 name = "workflow-backend" @@ -408,17 +412,20 @@ resource "aws_service_discovery_service" "retool_workflow_backend_service" { } module "temporal" { - count = var.workflows_enabled && !var.use_exising_temporal_cluster ? 1 : 0 + count = var.workflows_enabled && !var.use_exising_temporal_cluster ? 1 : 0 source = "./temporal" - - deployment_name = "${var.deployment_name}-temporal" - vpc_id = var.vpc_id - subnet_ids = var.subnet_ids - private_dns_namespace_id = aws_service_discovery_private_dns_namespace.retoolsvc[0].id - aws_cloudwatch_log_group_id = aws_cloudwatch_log_group.this.id - aws_region = var.aws_region - aws_ecs_cluster_id = aws_ecs_cluster.this.id - launch_type = var.launch_type - container_sg_id = aws_security_group.containers.id + + deployment_name = "${var.deployment_name}-temporal" + vpc_id = var.vpc_id + subnet_ids = var.subnet_ids + private_dns_namespace_id = aws_service_discovery_private_dns_namespace.retoolsvc[0].id + aws_cloudwatch_log_group_id = aws_cloudwatch_log_group.this.id + aws_region = var.aws_region + aws_ecs_cluster_id = aws_ecs_cluster.this.id + launch_type = var.launch_type + container_sg_id = aws_security_group.containers.id aws_ecs_capacity_provider_name = var.launch_type == "EC2" ? aws_ecs_capacity_provider.this[0].name : null + kms_key_id = var.temporal_aurora_kms_key_id + backup_window = var.temporal_aurora_backup_window + backup_retention_in_days = var.temporal_aurora_backup_retention_in_days } diff --git a/modules/aws_ecs/outputs.tf b/modules/aws_ecs/outputs.tf index 84a7018..f660ff5 100644 --- a/modules/aws_ecs/outputs.tf +++ b/modules/aws_ecs/outputs.tf @@ -42,3 +42,7 @@ output "rds_instance_name" { value = aws_db_instance.this.db_name description = "Name of RDS instance" } + +output "sg_containers_id" { + value = aws_security_group.containers.id +} diff --git a/modules/aws_ecs/secrets.tf b/modules/aws_ecs/secrets.tf index f47ca3a..8223abf 100644 --- a/modules/aws_ecs/secrets.tf +++ b/modules/aws_ecs/secrets.tf @@ -4,8 +4,8 @@ resource "random_string" "rds_password" { } resource "aws_secretsmanager_secret" "rds_password" { - name = "${var.deployment_name}-rds-password" - description = "This is the password for the Retool RDS instance" + name = "${var.deployment_name}-rds-password" + description = "This is the password for the Retool RDS instance" recovery_window_in_days = 0 } @@ -15,8 +15,8 @@ resource "aws_secretsmanager_secret_version" "rds_password" { } resource "aws_secretsmanager_secret" "rds_username" { - name = "${var.deployment_name}-rds-username" - description = "This is the username for the Retool RDS instance" + name = "${var.deployment_name}-rds-username" + description = "This is the username for the Retool RDS instance" recovery_window_in_days = 0 } @@ -31,8 +31,8 @@ resource "random_string" "jwt_secret" { } resource "aws_secretsmanager_secret" "jwt_secret" { - name = "${var.deployment_name}-jwt-secret" - description = "This is the secret for Retool JWTs" + name = "${var.deployment_name}-jwt-secret" + description = "This is the secret for Retool JWTs" recovery_window_in_days = 0 } @@ -48,8 +48,8 @@ resource "random_string" "encryption_key" { } resource "aws_secretsmanager_secret" "encryption_key" { - name = "${var.deployment_name}-encryption-key" - description = "This is the secret for encrypting credentials" + name = "${var.deployment_name}-encryption-key" + description = "This is the secret for encrypting credentials" recovery_window_in_days = 0 } diff --git a/modules/aws_ecs/security.tf b/modules/aws_ecs/security.tf index 4ea33e5..524e547 100644 --- a/modules/aws_ecs/security.tf +++ b/modules/aws_ecs/security.tf @@ -3,23 +3,31 @@ resource "aws_security_group" "rds" { description = "Retool database security group" vpc_id = var.vpc_id - ingress { - description = "Retool ECS Postgres Inbound" - from_port = "5432" - to_port = "5432" - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] + dynamic "ingress" { + for_each = var.rds_ingress_rules + content { + description = ingress.value["description"] + from_port = ingress.value["from_port"] + to_port = ingress.value["to_port"] + protocol = ingress.value["protocol"] + cidr_blocks = ingress.value["cidr_blocks"] + ipv6_cidr_blocks = ingress.value["ipv6_cidr_blocks"] + security_groups = ingress.value["security_groups"] + } } - # Allow all outbound - modify if necessary - egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = [ - "0.0.0.0/0" - ] - ipv6_cidr_blocks = ["::/0"] + dynamic "egress" { + for_each = var.rds_egress_rules + + content { + description = egress.value["description"] + from_port = egress.value["from_port"] + to_port = egress.value["to_port"] + protocol = egress.value["protocol"] + cidr_blocks = egress.value["cidr_blocks"] + ipv6_cidr_blocks = egress.value["ipv6_cidr_blocks"] + security_groups = egress.value["security_groups"] + } } } @@ -27,13 +35,33 @@ resource "aws_security_group" "temporal_aurora" { count = var.workflows_enabled ? 1 : 0 name = "${var.deployment_name}-temporal-rds-security-group" description = "Retool database security group" + vpc_id = var.vpc_id - ingress { - description = "Retool Temporal ECS Postgres Inbound" - from_port = "5432" - to_port = "5432" - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] + dynamic "ingress" { + for_each = var.temporal_aurora_ingress_rules + content { + description = ingress.value["description"] + from_port = ingress.value["from_port"] + to_port = ingress.value["to_port"] + protocol = ingress.value["protocol"] + cidr_blocks = ingress.value["cidr_blocks"] + ipv6_cidr_blocks = ingress.value["ipv6_cidr_blocks"] + security_groups = ingress.value["security_groups"] + } + } + + dynamic "egress" { + for_each = var.temporal_aurora_egress_rules + + content { + description = egress.value["description"] + from_port = egress.value["from_port"] + to_port = egress.value["to_port"] + protocol = egress.value["protocol"] + cidr_blocks = egress.value["cidr_blocks"] + ipv6_cidr_blocks = egress.value["ipv6_cidr_blocks"] + security_groups = egress.value["security_groups"] + } } } @@ -52,6 +80,7 @@ resource "aws_security_group" "alb" { protocol = ingress.value["protocol"] cidr_blocks = ingress.value["cidr_blocks"] ipv6_cidr_blocks = ingress.value["ipv6_cidr_blocks"] + security_groups = ingress.value["security_groups"] } } @@ -65,6 +94,7 @@ resource "aws_security_group" "alb" { protocol = egress.value["protocol"] cidr_blocks = egress.value["cidr_blocks"] ipv6_cidr_blocks = egress.value["ipv6_cidr_blocks"] + security_groups = egress.value["security_groups"] } } } @@ -84,6 +114,7 @@ resource "aws_security_group" "containers" { protocol = egress.value["protocol"] cidr_blocks = egress.value["cidr_blocks"] ipv6_cidr_blocks = egress.value["ipv6_cidr_blocks"] + security_groups = egress.value["security_groups"] } } } @@ -103,7 +134,7 @@ resource "aws_vpc_security_group_ingress_rule" "variable_rules" { resource "aws_vpc_security_group_ingress_rule" "containers_self_ingress" { security_group_id = aws_security_group.containers.id - description = "Allow self-ingress for inter-container communication" + description = "Allow self-ingress for inter-container communication" referenced_security_group_id = aws_security_group.containers.id - ip_protocol = -1 + ip_protocol = -1 } diff --git a/modules/aws_ecs/temporal/locals.tf b/modules/aws_ecs/temporal/locals.tf index e4ef558..94ba970 100644 --- a/modules/aws_ecs/temporal/locals.tf +++ b/modules/aws_ecs/temporal/locals.tf @@ -3,48 +3,48 @@ locals { var.additional_env_vars, # add additional environment variables [ { - "name": "LOG_LEVEL", - "value": "debug,info" + "name" : "LOG_LEVEL", + "value" : "debug,info" }, { - "name": "NUM_HISTORY_SHARDS", - "value": "128" + "name" : "NUM_HISTORY_SHARDS", + "value" : "128" }, { - "name": "DB", - "value": "postgresql" + "name" : "DB", + "value" : "postgresql" }, { - "name": "POSTGRES_HOST", - "value": module.temporal_aurora_rds.cluster_endpoint + "name" : "POSTGRES_HOST", + "value" : module.temporal_aurora_rds.cluster_endpoint }, { - "name": "POSTGRES_PORT", - "value": tostring(module.temporal_aurora_rds.cluster_port) + "name" : "POSTGRES_PORT", + "value" : tostring(module.temporal_aurora_rds.cluster_port) }, { - "name": "POSTGRES_USER", - "value": var.temporal_aurora_username + "name" : "POSTGRES_USER", + "value" : var.temporal_aurora_username }, { - "name": "POSTGRES_PASSWORD", - "value": random_string.temporal_aurora_password.result + "name" : "POSTGRES_PASSWORD", + "value" : random_string.temporal_aurora_password.result }, { - "name": "DBNAME", - "value": "temporal" + "name" : "DBNAME", + "value" : "temporal" }, { - "name": "DBNAME_VISIBILITY", - "value": "temporal_visibility" + "name" : "DBNAME_VISIBILITY", + "value" : "temporal_visibility" }, { - "name": "DYNAMIC_CONFIG_FILE_PATH", - "value": "/etc/temporal/ecs/dynamic_config/dynamicconfig-sql.yaml" + "name" : "DYNAMIC_CONFIG_FILE_PATH", + "value" : "/etc/temporal/ecs/dynamic_config/dynamicconfig-sql.yaml" }, { - "name": "ECS_DEPLOYED", - "value": "true" + "name" : "ECS_DEPLOYED", + "value" : "true" } ] ) diff --git a/modules/aws_ecs/temporal/main.tf b/modules/aws_ecs/temporal/main.tf index c982d67..ced6f6a 100644 --- a/modules/aws_ecs/temporal/main.tf +++ b/modules/aws_ecs/temporal/main.tf @@ -2,22 +2,22 @@ module "temporal_aurora_rds" { source = "terraform-aws-modules/rds-aurora/aws" version = "8.0.2" - name="${var.deployment_name}-temporal-rds-instance" + name = "${var.deployment_name}-temporal-rds-instance" engine = "aurora-postgresql" engine_mode = "provisioned" engine_version = "14.5" storage_encrypted = true - vpc_id = var.vpc_id + vpc_id = var.vpc_id monitoring_interval = 60 # Create DB Subnet group using var.subnet_ids create_db_subnet_group = true - subnets = var.subnet_ids + subnets = var.subnet_ids - master_username = aws_secretsmanager_secret_version.temporal_aurora_username.secret_string - master_password = aws_secretsmanager_secret_version.temporal_aurora_password.secret_string + master_username = aws_secretsmanager_secret_version.temporal_aurora_username.secret_string + master_password = aws_secretsmanager_secret_version.temporal_aurora_password.secret_string manage_master_user_password = false apply_immediately = true @@ -38,10 +38,14 @@ module "temporal_aurora_rds" { instances = { one = {} } + + kms_key_id = var.kms_key_id + preferred_backup_window = var.backup_window + backup_retention_period = var.backup_retention_in_days } -resource "aws_service_discovery_service" "temporal_frontend_service" { - name = "temporal" +resource "aws_service_discovery_service" "temporal_frontend_service" { + name = "temporal" dns_config { namespace_id = var.private_dns_namespace_id @@ -85,7 +89,7 @@ resource "aws_ecs_service" "retool_temporal" { dynamic "network_configuration" { for_each = var.launch_type == "FARGATE" ? toset([1]) : toset([]) - content { + content { subnets = var.subnet_ids security_groups = [ var.container_sg_id @@ -99,13 +103,13 @@ resource "aws_ecs_task_definition" "retool_temporal" { for_each = var.temporal_services_config - family = "${var.deployment_name}-${each.key}" - task_role_arn = aws_iam_role.task_role.arn - execution_role_arn = var.launch_type == "FARGATE" ? aws_iam_role.execution_role[0].arn : null + family = "${var.deployment_name}-${each.key}" + task_role_arn = aws_iam_role.task_role.arn + execution_role_arn = var.launch_type == "FARGATE" ? aws_iam_role.execution_role[0].arn : null requires_compatibilities = var.launch_type == "FARGATE" ? ["FARGATE"] : null - network_mode = var.launch_type == "FARGATE" ? "awsvpc" : "bridge" - cpu = var.launch_type == "FARGATE" ? each.value["cpu"] : null - memory = var.launch_type == "FARGATE" ? each.value["memory"] : null + network_mode = var.launch_type == "FARGATE" ? "awsvpc" : "bridge" + cpu = var.launch_type == "FARGATE" ? each.value["cpu"] : null + memory = var.launch_type == "FARGATE" ? each.value["memory"] : null container_definitions = jsonencode( [ { @@ -145,13 +149,13 @@ resource "aws_ecs_task_definition" "retool_temporal" { "value" = each.key }, ], - each.key != "frontend" ? [ { - "name": "PUBLIC_FRONTEND_ADDRESS", - "value": "${var.temporal_cluster_config.host}:${var.temporal_cluster_config.port}" + each.key != "frontend" ? [{ + "name" : "PUBLIC_FRONTEND_ADDRESS", + "value" : "${var.temporal_cluster_config.host}:${var.temporal_cluster_config.port}" } ] : [] ) } ] ) -} \ No newline at end of file +} diff --git a/modules/aws_ecs/temporal/secrets.tf b/modules/aws_ecs/temporal/secrets.tf index d249f98..1a77da3 100644 --- a/modules/aws_ecs/temporal/secrets.tf +++ b/modules/aws_ecs/temporal/secrets.tf @@ -5,8 +5,8 @@ resource "random_string" "temporal_aurora_password" { } resource "aws_secretsmanager_secret" "temporal_aurora_password" { - name = "${var.deployment_name}-temporal-rds-password" - description = "This is the password for the Retool Temporal RDS instance" + name = "${var.deployment_name}-temporal-rds-password" + description = "This is the password for the Retool Temporal RDS instance" recovery_window_in_days = 0 } @@ -16,8 +16,8 @@ resource "aws_secretsmanager_secret_version" "temporal_aurora_password" { } resource "aws_secretsmanager_secret" "temporal_aurora_username" { - name = "${var.deployment_name}-temporal-rds-username" - description = "This is the username for the Retool Temporal RDS instance" + name = "${var.deployment_name}-temporal-rds-username" + description = "This is the username for the Retool Temporal RDS instance" recovery_window_in_days = 0 } diff --git a/modules/aws_ecs/temporal/variables.tf b/modules/aws_ecs/temporal/variables.tf index e87c6de..e143fca 100644 --- a/modules/aws_ecs/temporal/variables.tf +++ b/modules/aws_ecs/temporal/variables.tf @@ -6,70 +6,70 @@ # tls_key: For mTLS only. Base64 encoded string of private tls key variable "temporal_cluster_config" { type = object({ - namespace = string - host = string - port = string - tls_enabled = bool - tls_crt = optional(string) - tls_key = optional(string) - }) - - default = { - namespace = "workflows" - host = "temporal.retoolsvc" - port = "7233" - tls_enabled = false - } + namespace = string + host = string + port = string + tls_enabled = bool + tls_crt = optional(string) + tls_key = optional(string) + }) + + default = { + namespace = "workflows" + host = "temporal.retoolsvc" + port = "7233" + tls_enabled = false + } } variable "temporal_services_config" { type = map(object({ - request_port = number - membership_port = number - cpu = number - memory = number + request_port = number + membership_port = number + cpu = number + memory = number })) default = { - frontend: { - request_port = 7233, + frontend : { + request_port = 7233, membership_port = 6933 - cpu = 512 - memory = 1024 + cpu = 512 + memory = 1024 }, - history: { - request_port = 7234, + history : { + request_port = 7234, membership_port = 6934 - cpu = 512 - memory = 2048 + cpu = 512 + memory = 2048 }, - matching: { - request_port = 7235, + matching : { + request_port = 7235, membership_port = 6935 - cpu = 512 - memory = 1024 + cpu = 512 + memory = 1024 }, - worker: { - request_port = 7239, + worker : { + request_port = 7239, membership_port = 6939 - cpu = 512 - memory = 1024 + cpu = 512 + memory = 1024 } } } variable "deployment_name" { - type = string - default = "retool-temporal" - description = "Name for Temporal Cluster deployment. Defaults to retool-temporal" + type = string + default = "retool-temporal" + description = "Name for Temporal Cluster deployment. Defaults to retool-temporal" } variable "launch_type" { - type = string - default = "FARGATE" + type = string + default = "FARGATE" validation { - condition = contains(["FARGATE", "EC2"], var.launch_type) + condition = contains(["FARGATE", "EC2"], var.launch_type) error_message = "launch_type must be either \"FARGATE\" or \"EC2\"" } } @@ -110,8 +110,8 @@ variable "private_dns_namespace_id" { } variable "temporal_image" { - type = string - default = "tryretool/one-offs:retool-temporal-1.1.2" + type = string + default = "tryretool/one-offs:retool-temporal-1.1.2" description = "Docker image to use for Temporal cluster." } @@ -132,12 +132,12 @@ variable "subnet_ids" { } variable "container_sg_id" { - type = string + type = string description = "ID for security group to use for ECS service" } variable "aws_ecs_capacity_provider_name" { - type = string + type = string description = "Name for ECS capacity provider for EC2" } @@ -148,7 +148,7 @@ variable "additional_env_vars" { } variable "aws_ecs_cluster_id" { - type = string + type = string description = "ID for ECS cluster to deploy Temporal to." } @@ -163,4 +163,20 @@ variable "aws_region" { description = "AWS region. Defaults to `us-east-1`" } +variable "kms_key_id" { + type = string + default = null + description = "KMS key ID for encrypting database. Defaults to null." +} + +variable "backup_window" { + type = string + default = null + description = "Backup window. Defaults to null." +} +variable "backup_retention_in_days" { + type = number + default = null + description = "Number of days to retain backups. Defaults to null." +} diff --git a/modules/aws_ecs/variables.tf b/modules/aws_ecs/variables.tf index 20064e8..897cb39 100644 --- a/modules/aws_ecs/variables.tf +++ b/modules/aws_ecs/variables.tf @@ -62,25 +62,25 @@ variable "ecs_retool_image" { } variable "ecs_task_resource_map" { - type = map(object({ - cpu = number + type = map(object({ + cpu = number memory = number })) - default = { + default = { main = { - cpu = 2048 + cpu = 2048 memory = 4096 }, jobs_runner = { - cpu = 1024 + cpu = 1024 memory = 2048 }, workflows_backend = { - cpu = 2048 + cpu = 2048 memory = 4096 } workflows_worker = { - cpu = 2048 + cpu = 2048 memory = 4096 } } @@ -136,17 +136,17 @@ variable "use_exising_temporal_cluster" { } variable "launch_type" { - type = string - default = "FARGATE" + type = string + default = "FARGATE" validation { - condition = contains(["FARGATE", "EC2"], var.launch_type) + condition = contains(["FARGATE", "EC2"], var.launch_type) error_message = "launch_type must be either \"FARGATE\" or \"EC2\"" } } # namescape: temporal namespace to use for Retool Workflows. We recommend this is only used by Retool. -# If use_existing_temporal_cluster == true this should be config for currently existing cluster. +# If use_existing_temporal_cluster == true this should be config for currently existing cluster. # If use_existing_temporal_cluster == false, you should use the defaults. # host: hostname for Temporal Frontend service # port: port for Temporal Frontend service @@ -155,20 +155,20 @@ variable "launch_type" { # tls_key: For mTLS only. Base64 encoded string of private tls key variable "temporal_cluster_config" { type = object({ - namespace = string - host = string - port = string - tls_enabled = bool - tls_crt = optional(string) - tls_key = optional(string) - }) + namespace = string + host = string + port = string + tls_enabled = bool + tls_crt = optional(string) + tls_key = optional(string) + }) - default = { - namespace = "workflows" - host = "temporal.retoolsvc" - port = "7233" - tls_enabled = false - } + default = { + namespace = "workflows" + host = "temporal.retoolsvc" + port = "7233" + tls_enabled = false + } } variable "temporal_aurora_username" { @@ -269,46 +269,46 @@ variable "container_ingress_rules" { ) default = { first = { - description = "Global HTTP inbound ipv4" - from_port = "80" - to_port = "3000" - protocol = "tcp" - cidr_block = "0.0.0.0/0" + description = "Global HTTP inbound ipv4" + from_port = "80" + to_port = "3000" + protocol = "tcp" + cidr_block = "0.0.0.0/0" }, second = { - description = "Global HTTP inbound ipv6" - from_port = "80" - to_port = "3000" - protocol = "tcp" - ipv6_cidr_block = "::/0" + description = "Global HTTP inbound ipv6" + from_port = "80" + to_port = "3000" + protocol = "tcp" + ipv6_cidr_block = "::/0" }, third = { - description = "Global HTTPS inbound ipv4" - from_port = "443" - to_port = "3000" - protocol = "tcp" - cidr_block = "0.0.0.0/0" + description = "Global HTTPS inbound ipv4" + from_port = "443" + to_port = "3000" + protocol = "tcp" + cidr_block = "0.0.0.0/0" }, fourth = { - description = "Global HTTPS inbound ipv4" - from_port = "443" - to_port = "3000" - protocol = "tcp" - ipv6_cidr_block = "::/0" + description = "Global HTTPS inbound ipv4" + from_port = "443" + to_port = "3000" + protocol = "tcp" + ipv6_cidr_block = "::/0" }, fifth = { - description = "SSH inbound ipv4" - from_port = "22" - to_port = "22" - protocol = "tcp" - cidr_block = "0.0.0.0/0" + description = "SSH inbound ipv4" + from_port = "22" + to_port = "22" + protocol = "tcp" + cidr_block = "0.0.0.0/0" }, sixth = { - description = "SSH inbound ipv6" - from_port = "22" - to_port = "22" - protocol = "tcp" - ipv6_cidr_block = "::/0" + description = "SSH inbound ipv6" + from_port = "22" + to_port = "22" + protocol = "tcp" + ipv6_cidr_block = "::/0" } } description = "Ingress rules for EC2 instances in autoscaling group or ECS services in Fargate" @@ -322,8 +322,9 @@ variable "container_egress_rules" { from_port = string to_port = string protocol = string - cidr_blocks = list(string) - ipv6_cidr_blocks = list(string) + cidr_blocks = optional(list(string)) + ipv6_cidr_blocks = optional(list(string)) + security_groups = optional(list(string)) }) ) default = [ @@ -343,12 +344,13 @@ variable "container_egress_rules" { variable "alb_ingress_rules" { type = list( object({ - description = string + description = optional(string) from_port = string to_port = string protocol = string - cidr_blocks = list(string) - ipv6_cidr_blocks = list(string) + cidr_blocks = optional(list(string)) + ipv6_cidr_blocks = optional(list(string)) + security_groups = optional(list(string)) }) ) default = [ @@ -368,12 +370,13 @@ variable "alb_ingress_rules" { variable "alb_egress_rules" { type = list( object({ - description = string + description = optional(string) from_port = string to_port = string protocol = string - cidr_blocks = list(string) - ipv6_cidr_blocks = list(string) + cidr_blocks = optional(list(string)) + ipv6_cidr_blocks = optional(list(string)) + security_groups = optional(list(string)) }) ) default = [ @@ -388,3 +391,155 @@ variable "alb_egress_rules" { ] description = "Egress rules for load balancer" } + +variable "alb_certificate_arn" { + type = string + default = null + description = "ARN of the ACM certificate to use for HTTPS. Defaults to null." +} + +variable "alb_subnet_ids" { + type = list(string) + default = null + description = "Subnet IDs for the load balancer. Defaults to `subnet_ids`." +} + +variable "rds_ingress_rules" { + type = list( + object({ + description = optional(string) + from_port = string + to_port = string + protocol = string + cidr_blocks = optional(list(string)) + ipv6_cidr_blocks = optional(list(string)) + security_groups = optional(list(string)) + }) + ) + default = [ + { + description = "Retool ECS Postgres Inbound" + from_port = "5432" + to_port = "5432" + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + ] + description = "Ingress rules for RDS" +} + + +variable "rds_egress_rules" { + type = list( + object({ + description = optional(string) + from_port = string + to_port = string + protocol = string + cidr_blocks = optional(list(string)) + ipv6_cidr_blocks = optional(list(string)) + security_groups = optional(list(string)) + }) + ) + default = [ + { + description = "Global outbound" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = [ + "0.0.0.0/0" + ] + ipv6_cidr_blocks = ["::/0"] + } + ] + description = "Egress rules for RDS" +} + +variable "temporal_aurora_ingress_rules" { + type = list( + object({ + description = optional(string) + from_port = string + to_port = string + protocol = string + cidr_blocks = optional(list(string)) + ipv6_cidr_blocks = optional(list(string)) + security_groups = optional(list(string)) + }) + ) + default = [ + { + description = "Retool ECS Postgres Inbound" + from_port = "5432" + to_port = "5432" + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + ] + description = "Ingress rules for temporal Aurora" +} + + +variable "temporal_aurora_egress_rules" { + type = list( + object({ + description = optional(string) + from_port = string + to_port = string + protocol = string + cidr_blocks = optional(list(string)) + ipv6_cidr_blocks = optional(list(string)) + security_groups = optional(list(string)) + }) + ) + default = [ + { + description = "Global outbound" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = [ + "0.0.0.0/0" + ] + ipv6_cidr_blocks = ["::/0"] + } + ] + description = "Egress rules for temporal Aurora" +} + +variable "rds_kms_key_id" { + type = string + default = null + description = "KMS key ID for encrypting database. Defaults to null." +} + +variable "temporal_aurora_kms_key_id" { + type = string + default = null + description = "KMS key ID for encrypting temporal Aurora. Defaults to null." +} + +variable "rds_backup_window" { + type = string + default = null + description = "Backup window for RDS. Defaults to null." +} + +variable "rds_backup_retention_in_days" { + type = number + default = null + description = "Number of days to retain backups for RDS. Defaults to null." +} + +variable "temporal_aurora_backup_window" { + type = string + default = null + description = "Backup window for temporal Aurora. Defaults to null." +} + +variable "temporal_aurora_backup_retention_in_days" { + type = number + default = null + description = "Number of days to retain backups for temporal Aurora. Defaults to null." +} diff --git a/modules/aws_ecs_ec2/main.tf b/modules/aws_ecs_ec2/main.tf index 761a68b..f43dc9f 100644 --- a/modules/aws_ecs_ec2/main.tf +++ b/modules/aws_ecs_ec2/main.tf @@ -22,7 +22,7 @@ resource "aws_ecs_cluster" "this" { data "aws_ami" "this" { most_recent = true # get the latest version - name_regex = "^amzn2-ami-ecs-hvm-\\d\\.\\d\\.\\d{8}-x86_64-ebs$" + name_regex = "^amzn2-ami-ecs-hvm-\\d\\.\\d\\.\\d{8}-x86_64-ebs$" filter { name = "virtualization-type" @@ -65,7 +65,7 @@ resource "aws_launch_configuration" "this" { # Allow the EC2 instances to access AWS resources on your behalf, using this instance profile and the permissions defined there iam_instance_profile = aws_iam_instance_profile.ec2.arn - + lifecycle { create_before_destroy = true } @@ -144,7 +144,7 @@ resource "aws_cloudwatch_log_group" "this" { } resource "aws_db_instance" "this" { - identifier = "${var.deployment_name}-rds-instance" + identifier = "${var.deployment_name}-rds-instance" allocated_storage = 80 instance_class = var.rds_instance_class engine = "postgres" @@ -156,9 +156,9 @@ resource "aws_db_instance" "this" { publicly_accessible = var.rds_publicly_accessible vpc_security_group_ids = [aws_security_group.rds.id] performance_insights_enabled = var.rds_performance_insights_enabled - - skip_final_snapshot = true - apply_immediately = true + + skip_final_snapshot = true + apply_immediately = true } resource "aws_ecs_service" "retool" { diff --git a/modules/aws_ecs_ec2/secrets.tf b/modules/aws_ecs_ec2/secrets.tf index f47ca3a..8223abf 100644 --- a/modules/aws_ecs_ec2/secrets.tf +++ b/modules/aws_ecs_ec2/secrets.tf @@ -4,8 +4,8 @@ resource "random_string" "rds_password" { } resource "aws_secretsmanager_secret" "rds_password" { - name = "${var.deployment_name}-rds-password" - description = "This is the password for the Retool RDS instance" + name = "${var.deployment_name}-rds-password" + description = "This is the password for the Retool RDS instance" recovery_window_in_days = 0 } @@ -15,8 +15,8 @@ resource "aws_secretsmanager_secret_version" "rds_password" { } resource "aws_secretsmanager_secret" "rds_username" { - name = "${var.deployment_name}-rds-username" - description = "This is the username for the Retool RDS instance" + name = "${var.deployment_name}-rds-username" + description = "This is the username for the Retool RDS instance" recovery_window_in_days = 0 } @@ -31,8 +31,8 @@ resource "random_string" "jwt_secret" { } resource "aws_secretsmanager_secret" "jwt_secret" { - name = "${var.deployment_name}-jwt-secret" - description = "This is the secret for Retool JWTs" + name = "${var.deployment_name}-jwt-secret" + description = "This is the secret for Retool JWTs" recovery_window_in_days = 0 } @@ -48,8 +48,8 @@ resource "random_string" "encryption_key" { } resource "aws_secretsmanager_secret" "encryption_key" { - name = "${var.deployment_name}-encryption-key" - description = "This is the secret for encrypting credentials" + name = "${var.deployment_name}-encryption-key" + description = "This is the secret for encrypting credentials" recovery_window_in_days = 0 }