diff --git a/data.tf b/data.tf index d84924f..bb4c7cc 100644 --- a/data.tf +++ b/data.tf @@ -1,4 +1,5 @@ data aws_region current {} +data aws_caller_identity current {} data aws_lb passed_on { arn = var.aws_lb_arn diff --git a/main.tf b/main.tf index 19e7743..08aed58 100644 --- a/main.tf +++ b/main.tf @@ -2,25 +2,30 @@ # CloudWatch Log Groups # --------------------------------------------------- resource aws_cloudwatch_log_group ecs_group { - name = "${var.name_prefix}/fargate/${var.cluster_name}/${var.app_name}/" - tags = var.standard_tags + name = "${var.name_prefix}/fargate/${var.cluster_name}/${var.service_name}/" + tags = var.standard_tags + retention_in_days = var.retention_in_days } # --------------------------------------------------- # ECS Service # --------------------------------------------------- -resource aws_ecs_service aws_ecs_fargate_service { - name = "${var.name_prefix}-${var.app_name}" +resource time_sleep wait { + depends_on = [aws_ecs_service.main] + create_duration = "30s" +} + +resource aws_ecs_service main { + name = "${var.name_prefix}-${var.service_name}" cluster = var.cluster_arn - platform_version = var.platform_version propagate_tags = "SERVICE" deployment_maximum_percent = 200 deployment_minimum_healthy_percent = 100 desired_count = var.container_desired_count - task_definition = aws_ecs_task_definition.fargate_service_task_definition.arn + task_definition = aws_ecs_task_definition.main.arn health_check_grace_period_seconds = var.health_check_grace_period_seconds - tags = merge(var.standard_tags, { Name = var.app_name }) + tags = merge(var.standard_tags, { Name = var.service_name }) capacity_provider_strategy { capacity_provider = "FARGATE" @@ -37,49 +42,58 @@ resource aws_ecs_service aws_ecs_fargate_service { network_configuration { security_groups = var.security_groups subnets = var.subnets - assign_public_ip = var.public } load_balancer { - target_group_arn = aws_lb_target_group.aws_ecs_fargate_service_target_group.arn - container_name = var.app_name - container_port = var.app_port + target_group_arn = aws_lb_target_group.main.arn + container_name = var.service_name + container_port = var.service_port } + + depends_on = [data.aws_lb.passed_on] } # --------------------------------------------------- # ECS Task Definition # --------------------------------------------------- -module fargate_service_ecs_container_definition { - source = "cloudposse/ecs-container-definition/aws" - version = "0.58.1" +module main_container_definition { + source = "cloudposse/ecs-container-definition/aws" + version = "0.58.1" + command = var.command - container_name = var.app_name - container_image = var.container_image + container_name = var.service_name + container_image = var.service_image container_memory = var.container_memory container_memory_reservation = var.container_memory container_cpu = var.container_cpu mount_points = var.mount_points entrypoint = var.entrypoint - environment = setunion(var.environment, + secrets = var.secrets + + port_mappings = [ + { + containerPort = var.service_port + hostPort = var.service_port + protocol = "tcp" + } + ] + + environment = setunion(var.environment, [ { name = "PORT" - value = var.app_port + value = var.service_port }, { name = "APP_PORT" - value = var.app_port - } - ]) - - port_mappings = [ + value = var.service_port + }, { - containerPort = var.app_port - hostPort = var.app_port - protocol = "tcp" - }] + name = "SERVICE_PORT" + value = var.service_port + } + ]) log_configuration = { logDriver = "awslogs" @@ -92,23 +106,26 @@ module fargate_service_ecs_container_definition { } } -resource aws_ecs_task_definition fargate_service_task_definition { - family = "${var.name_prefix}-${var.app_name}" + +# --------------------------------------------------- +# Task Definition +# --------------------------------------------------- +resource aws_ecs_task_definition main { + family = "${var.name_prefix}-${var.zenv_name}-${var.service_name}" requires_compatibilities = [var.launch_type] network_mode = "awsvpc" execution_role_arn = var.execution_role_arn cpu = coalesce(var.task_cpu, var.container_cpu) memory = coalesce(var.task_memory, var.container_memory) task_role_arn = var.task_role_arn - container_definitions = jsonencode(concat([module.fargate_service_ecs_container_definition.json_map_object], var.additional_containers)) - tags = var.standard_tags + tags = merge(var.standard_tags, tomap({ Name = var.service_name })) - dynamic "volume" { + dynamic volume { for_each = var.volumes content { name = volume.value.name - dynamic "efs_volume_configuration" { + dynamic efs_volume_configuration { for_each = lookup(volume.value, "efs_volume_configuration", []) content { file_system_id = lookup(efs_volume_configuration.value, "file_system_id", null) @@ -122,114 +139,11 @@ resource aws_ecs_task_definition fargate_service_task_definition { # --------------------------------------------------- -# CloudWatch Alarms for ASG +# Internal Load Balancer - If NOT Public # --------------------------------------------------- -resource aws_cloudwatch_metric_alarm fargate_service_cpu_high { - alarm_name = "${var.name_prefix}-fargate-high-cpu-${var.app_name}" - comparison_operator = "GreaterThanOrEqualToThreshold" - evaluation_periods = "2" - threshold = var.container_cpu_high_threshold - datapoints_to_alarm = 1 - statistic = "Average" - period = "60" - - metric_name = "CPUUtilization" - namespace = "AWS/ECS" - dimensions = { - ClusterName = var.cluster_name - ServiceName = aws_ecs_service.aws_ecs_fargate_service.name - } - - alarm_actions = [ - aws_appautoscaling_policy.fargate_service_scale_up.arn - ] -} - -resource aws_cloudwatch_metric_alarm fargate_service_cpu_low { - alarm_name = "${var.name_prefix}-fargate-low-cpu-${var.app_name}" - comparison_operator = "LessThanThreshold" - evaluation_periods = "2" - threshold = var.container_cpu_low_threshold - datapoints_to_alarm = 1 - statistic = "Average" - period = "60" - - metric_name = "CPUUtilization" - namespace = "AWS/ECS" - dimensions = { - ClusterName = var.cluster_name - ServiceName = aws_ecs_service.aws_ecs_fargate_service.name - } - - alarm_actions = [ - aws_appautoscaling_policy.fargate_service_scale_down.arn - ] -} - - -# --------------------------------------------------- -# Autoscaling -# --------------------------------------------------- -resource time_sleep wait { - depends_on = [aws_ecs_service.aws_ecs_fargate_service] - create_duration = "30s" -} - -resource aws_appautoscaling_target fargate_service_autoscaling_target { - min_capacity = var.container_min_capacity - max_capacity = var.container_max_capacity - resource_id = "service/${var.cluster_name}/${var.name_prefix}-${var.app_name}" - role_arn = "arn:aws:iam::${var.account}:role/aws-service-role/ecs.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_ECSService" - scalable_dimension = "ecs:service:DesiredCount" - service_namespace = "ecs" - depends_on = [time_sleep.wait] -} - -resource aws_appautoscaling_policy fargate_service_scale_up { - name = "scale-up-${var.app_name}" - policy_type = "StepScaling" - resource_id = aws_appautoscaling_target.fargate_service_autoscaling_target.resource_id - scalable_dimension = aws_appautoscaling_target.fargate_service_autoscaling_target.scalable_dimension - service_namespace = aws_appautoscaling_target.fargate_service_autoscaling_target.service_namespace - - step_scaling_policy_configuration { - adjustment_type = "ChangeInCapacity" - cooldown = 60 - metric_aggregation_type = "Maximum" - - step_adjustment { - metric_interval_upper_bound = 0 - scaling_adjustment = 1 - } - } -} - -resource aws_appautoscaling_policy fargate_service_scale_down { - name = "scale-down-${var.app_name}" - policy_type = "StepScaling" - resource_id = aws_appautoscaling_target.fargate_service_autoscaling_target.resource_id - scalable_dimension = aws_appautoscaling_target.fargate_service_autoscaling_target.scalable_dimension - service_namespace = aws_appautoscaling_target.fargate_service_autoscaling_target.service_namespace - - step_scaling_policy_configuration { - adjustment_type = "ChangeInCapacity" - cooldown = 60 - metric_aggregation_type = "Maximum" - - step_adjustment { - metric_interval_lower_bound = 0 - scaling_adjustment = -1 - } - } -} - - -# --------------------------------------------------- -# Internal Load Balancer - If Private Subnet -# --------------------------------------------------- -resource aws_lb_target_group aws_ecs_fargate_service_target_group { - name = "${var.name_prefix}-${var.app_name}-tg" - port = var.app_port +resource aws_lb_target_group main { + name = "${var.name_prefix}-${var.zenv_name}-${var.service_name}-tg" + port = var.service_port protocol = "HTTP" vpc_id = var.vpc_id load_balancing_algorithm_type = "round_robin" @@ -242,32 +156,33 @@ resource aws_lb_target_group aws_ecs_fargate_service_target_group { timeout = 5 interval = 10 path = var.healthcheck_path - port = var.app_port + port = var.service_port } } -resource aws_lb_listener aws_ecs_fargate_service_aws_lb_listener { - load_balancer_arn = var.aws_lb_arn - port = var.aws_lb_out_port +resource aws_lb_listener main { + load_balancer_arn = data.aws_lb.passed_on.arn + port = var.public == true ? var.external_port : var.service_port protocol = "HTTPS" ssl_policy = "ELBSecurityPolicy-TLS-1-2-Ext-2018-06" certificate_arn = var.aws_lb_certificate_arn default_action { type = "forward" - target_group_arn = aws_lb_target_group.aws_ecs_fargate_service_target_group.arn + target_group_arn = aws_lb_target_group.main.arn } } -resource "aws_lb_listener_rule" "block_header_rule" { - listener_arn = aws_lb_listener.aws_ecs_fargate_service_aws_lb_listener.arn - priority = 100 +resource aws_lb_listener_rule block_header_rule { + count = var.public == true ? 0 : 1 + listener_arn = aws_lb_listener.main.arn + priority = 100 condition { - http_header { - http_header_name = "X-Forwarded-Host" - values = ["*"] - } + http_header { + http_header_name = "X-Forwarded-Host" + values = ["*"] + } } action { @@ -282,24 +197,24 @@ resource "aws_lb_listener_rule" "block_header_rule" { # --------------------------------------------------- -# Public Load Balancer - If Public Subnet +# Public Load Balancer - If Public # --------------------------------------------------- resource aws_lb public { count = var.public == true ? 1 : 0 - name = "${var.name_prefix}-Pub-${var.app_name}-LB" + name = "${var.name_prefix}-${var.zenv_name}-${var.service_name}-Pub-LB" load_balancer_type = "application" security_groups = var.security_groups subnets = var.subnets access_logs { bucket = var.s3_log_bucket - prefix = "${var.app_name}_lb" + prefix = "${var.service_name}_lb" enabled = true } tags = merge( var.standard_tags, - tomap({ Name = "Public-${var.app_name}" }) + tomap({ Name = "Public-${var.service_name}" }) ) } @@ -348,10 +263,23 @@ resource aws_lb_listener_rule block_header { # DNS Record (CNAME) # --------------------------------------------------- resource aws_route53_record main { - count = var.public == true ? 1 : 0 + count = var.public == true && var.domain_record != null ? 1 : 0 zone_id = data.aws_route53_zone.main.zone_id - name = "${var.name_prefix}-${var.app_name}" + name = "${var.name_prefix}-${var.zenv_name}-${var.service_name}" type = "CNAME" ttl = 300 records = [aws_lb.public[0].dns_name] } + + +# --------------------------------------------------- +# LogDNA subsciprion +# --------------------------------------------------- +resource aws_cloudwatch_log_subscription_filter lambda_logfilter { + count = var.logdna_lambda_logs_arn != null ? 1 : 0 + name = "${var.name_prefix}-${var.zenv_name}-${var.service_name}-filter" + log_group_name = "${var.name_prefix}/fargate/${var.cluster_name}/${var.service_name}/" + filter_pattern = "" + destination_arn = var.logdna_lambda_logs_arn + distribution = "ByLogStream" +} diff --git a/outputs.tf b/outputs.tf index 47b8636..cd9bf08 100644 --- a/outputs.tf +++ b/outputs.tf @@ -6,7 +6,7 @@ output load_balancer_fqdn { value = var.public == true ? aws_lb.public[0].dns_name : null } -output name { +output dns_name { value = var.public == true ? aws_route53_record.main[0].name : null } @@ -14,10 +14,10 @@ output fqdn { value = var.public == true ? aws_route53_record.main[0].fqdn : null } -output app_port { - value = var.app_port -} - output cloudwatch_log_group_name { value = aws_cloudwatch_log_group.ecs_group.name } + +output service_port { + value = var.service_port +} diff --git a/variables.tf b/variables.tf index 5b4cd9b..4baaa19 100644 --- a/variables.tf +++ b/variables.tf @@ -1,14 +1,23 @@ variable vpc_id {} -variable account {} variable subnets {} -variable environment {} -variable domain_name {} variable aws_lb_arn {} -variable aws_lb_out_port {} +variable domain_name {} +variable service_image {} variable security_groups {} -variable execution_role_arn {} variable aws_lb_certificate_arn {} +variable execution_role_arn { + type = string + default = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/ecsTaskExecutionRole" +} +variable logdna_lambda_logs_arn { + type = string + default = null +} +variable zenv_name { + type = string + default = "zbs" +} variable name_prefix { type = string } @@ -21,7 +30,7 @@ variable cluster_name { variable cluster_arn { type = string } -variable app_name { +variable service_name { type = string } variable container_memory { @@ -40,10 +49,9 @@ variable task_cpu { type = number default = null } -variable container_image {} -variable app_port { +variable service_port { type = number - default = 9999 + default = 8080 } variable entrypoint { type = list(string) @@ -77,7 +85,10 @@ variable task_role_arn { type = string default = null } - +variable retention_in_days { + type = number + default = 7 +} variable healthcheck_path { type = string default = "/health" @@ -90,34 +101,40 @@ variable launch_type { default = "FARGATE" } variable fargate_weight { + type = number default = 1 } variable fargate_base { + type = number default = 1 } variable fargate_spot_weight { + type = number default = 1 } variable fargate_spot_base { + type = number default = 0 } variable platform_version { type = string default = "1.4.0" # Before: "LATEST" } - variable additional_containers { description = "Additional containers definition" default = [] } - variable s3_log_bucket { type = string } - variable public { + type = bool default = false } +variable domain_record { + type = string + default = null +} variable mount_points { type = list(object({ @@ -140,3 +157,21 @@ variable volumes { description = "Task volume definitions as list of configuration objects" default = [] } + +variable environment { + type = list(object({ + name = any + value = any + })) + description = "List of Environment Variables" + default = [] +} + +variable secrets { + type = list(object({ + name = string + valueFrom = string + })) + description = "List of Secrets" + default = [] +}