diff --git a/README.md b/README.md index 87dc49d..86be638 100644 --- a/README.md +++ b/README.md @@ -537,7 +537,7 @@ This project creates and manages resources within an AWS account for infrastruct | [infrastructure\_ecs\_cluster\_pending\_task\_metric\_lambda\_log\_retention](#input\_infrastructure\_ecs\_cluster\_pending\_task\_metric\_lambda\_log\_retention) | Log retention for the ECS cluster pending task metric Lambda | `number` | n/a | yes | | [infrastructure\_ecs\_cluster\_publicly\_avaialble](#input\_infrastructure\_ecs\_cluster\_publicly\_avaialble) | Conditionally launch the ECS cluster EC2 instances into the Public subnet | `bool` | n/a | yes | | [infrastructure\_ecs\_cluster\_service\_defaults](#input\_infrastructure\_ecs\_cluster\_service\_defaults) | Default values for ECS Cluster Services |
object({| n/a | yes | -| [infrastructure\_ecs\_cluster\_services](#input\_infrastructure\_ecs\_cluster\_services) | Map of ECS Cluster Services (The key will be the service name). Values in here will override `infrastructure_ecs_cluster_service_defaults` values if set."
github_v1_source = optional(bool, null)
github_v1_oauth_token = optional(string, null)
codestar_connection_arn = optional(string, null)
github_owner = optional(string, null)
github_repo = optional(string, null)
github_track_revision = optional(string, null)
buildspec = optional(string, null)
buildspec_from_github_repo = optional(bool, null)
codebuild_environment_variables = optional(list(object({
name = string
value = string
})), [])
ecr_scan_target_sns_topic_arn = optional(string, null)
deployment_type = optional(string, null)
enable_cloudwatch_logs = optional(bool, null)
cloudwatch_logs_retention = optional(number, null)
enable_execute_command = optional(bool, null)
deregistration_delay = optional(number, null)
custom_policies = optional(map(object({
description = string
policy = object({
Version = string
Statement = list(object({
Action = list(string)
Effect = string
Resource = list(string)
}))
})
})), {})
container_entrypoint = optional(list(string), null)
container_port = optional(number, null)
container_volumes = optional(list(map(string)), null)
container_extra_hosts = optional(list(map(string)), null)
container_count = optional(number, null)
container_heath_check_path = optional(string, null)
container_heath_grace_period = optional(number, null)
scheduled_tasks = optional(map(object({
entrypoint = optional(list(string), null)
schedule_expression = string
})), {})
domain_names = optional(list(string), null)
enable_cloudfront = optional(bool, null)
cloudfront_tls_certificate_arn = optional(string, null)
cloudfront_access_logging_enabled = optional(bool, null)
cloudfront_bypass_protection_enabled = optional(bool, null)
cloudfront_bypass_protection_excluded_domains = optional(list(string), null)
cloudfront_origin_shield_enabled = optional(bool, null)
cloudfront_managed_cache_policy = optional(string, null)
cloudfront_managed_origin_request_policy = optional(string, null)
cloudfront_managed_response_headers_policy = optional(string, null)
cloudfront_waf_association = optional(string, null)
alb_tls_certificate_arn = optional(string, null)
})
map(object({| n/a | yes | +| [infrastructure\_ecs\_cluster\_services](#input\_infrastructure\_ecs\_cluster\_services) | Map of ECS Cluster Services (The key will be the service name). Values in here will override `infrastructure_ecs_cluster_service_defaults` values if set."
github_v1_source = optional(bool, null)
github_v1_oauth_token = optional(string, null)
codestar_connection_arn = optional(string, null)
github_owner = optional(string, null)
github_repo = optional(string, null)
github_track_revision = optional(string, null)
buildspec = optional(string, null)
buildspec_from_github_repo = optional(bool, null)
codebuild_environment_variables = optional(list(object({
name = string
value = string
})), [])
ecr_scan_target_sns_topic_arn = optional(string, null)
deployment_type = optional(string, null)
enable_cloudwatch_logs = optional(bool, null)
cloudwatch_logs_retention = optional(number, null)
enable_execute_command = optional(bool, null)
deregistration_delay = optional(number, null)
custom_policies = optional(map(object({
description = string
policy = object({
Version = string
Statement = list(object({
Action = list(string)
Effect = string
Resource = list(string)
}))
})
})), {})
container_entrypoint = optional(list(string), null)
container_port = optional(number, null)
container_volumes = optional(list(map(string)), null)
container_extra_hosts = optional(list(map(string)), null)
container_count = optional(number, null)
container_heath_check_path = optional(string, null)
container_heath_grace_period = optional(number, null)
scheduled_tasks = optional(map(object({
entrypoint = list(string)
schedule_expression = string
})), null)
domain_names = optional(list(string), null)
enable_cloudfront = optional(bool, null)
cloudfront_tls_certificate_arn = optional(string, null)
cloudfront_access_logging_enabled = optional(bool, null)
cloudfront_bypass_protection_enabled = optional(bool, null)
cloudfront_bypass_protection_excluded_domains = optional(list(string), null)
cloudfront_origin_shield_enabled = optional(bool, null)
cloudfront_managed_cache_policy = optional(string, null)
cloudfront_managed_origin_request_policy = optional(string, null)
cloudfront_managed_response_headers_policy = optional(string, null)
cloudfront_waf_association = optional(string, null)
alb_tls_certificate_arn = optional(string, null)
}))
map(object({| n/a | yes | | [infrastructure\_ecs\_cluster\_services\_alb\_enable\_global\_accelerator](#input\_infrastructure\_ecs\_cluster\_services\_alb\_enable\_global\_accelerator) | Enable Global Accelerator (GA) for the infrastructure ECS cluster services ALB. If `cloudfront_bypass_protection_enabled` is set for a service, any domain pointing towards the GA must be added to the `cloudfront_bypass_protection_excluded_domains` list. It is recommended that the GA only be used for apex domains that redirect to the domain associated with CloudFront. Ideally, apex domains would use an ALIAS record pointing towards the CloudFront distribution. | `bool` | n/a | yes | | [infrastructure\_ecs\_cluster\_services\_alb\_ip\_allow\_list](#input\_infrastructure\_ecs\_cluster\_services\_alb\_ip\_allow\_list) | IP allow list for ingress traffic to the infrastructure ECS cluster services ALB | `list(string)` | n/a | yes | | [infrastructure\_ecs\_cluster\_services\_alb\_logs\_retention](#input\_infrastructure\_ecs\_cluster\_services\_alb\_logs\_retention) | Retention in days for the infrasrtucture ecs cluster ALB logs | `number` | n/a | yes | diff --git a/container-definitions/app.json.tpl b/container-definitions/app.json.tpl index f885d68..a0b0ebf 100644 --- a/container-definitions/app.json.tpl +++ b/container-definitions/app.json.tpl @@ -10,7 +10,7 @@ "tag": "${syslog_tag}" } }, - %{else} + %{ else } %{ if cloudwatch_log_group != "" } "logConfiguration": { "logDriver": "awslogs", @@ -39,6 +39,11 @@ "containerPort": ${container_port} } ], + %{ if enable_sidecar_container }, + "healthCheck": { + "command": ["CMD-SHELL", "curl -f localhost:${container_port} || exit 1] + }, + %{ endif } %{ endif } %{ if environment != "[]" } "environment": ${environment}, @@ -63,13 +68,65 @@ %{ if security_options != "[]" } "dockerSecurityOptions": ${security_options}, %{ endif } - %{if entrypoint != "[]"} + %{ if entrypoint != "[]" } "entrypoint": ${entrypoint}, %{ endif } - %{if command != "[]"} + %{ if command != "[]" } "command": ${command}, %{ endif } "memoryReservation": 16, "essential": true } + {% if enable_sidecar_container }, + { + "image": "${sidecar_image}", + "name": "${sidecar_container_name}", + %{ if syslog_address != "" } + "logConfiguration": { + "logDriver": "syslog", + "options": { + "syslog-address": "${syslog_address}", + "tag": "${syslog_tag}" + } + }, + %{ else } + %{ if cloudwatch_log_group != "" } + "logConfiguration": { + "logDriver": "awslogs", + "options": { + %{ if awslogs_stream_prefix != "" } + "awslogs-stream-prefix": "${awslogs_stream_prefix}", + %{ endif } + "awslogs-group": "${cloudwatch_log_group}", + "awslogs-region": "${region}" + } + }, + %{ else } + "logConfiguration": { + "logDriver": "json-file" + }, + %{ endif } + "portMappings": [ + { + "hostPort": 0, + "protocol": "tcp", + "containerPort": 8080 + } + ], + %{ if sidecar_environment != "[]" } + "environment": ${sidecar_environment}, + %{ endif } + %{ if sidecar_entrypoint != "[]" } + "entrypoint": ${sidecar_entrypoint}, + %{ endif } + "memoryReservation": 16, + "essential": true, + "dependsOn": [ + { + "containerName": "${container_name}", + "condition": "HEALTHY" + } + ] + } + %{ endif } ] diff --git a/ecs-cluster-infrastructure-datadog-agent-service.tf b/ecs-cluster-infrastructure-datadog-agent-service.tf index 5a5d26f..e2c4324 100644 --- a/ecs-cluster-infrastructure-datadog-agent-service.tf +++ b/ecs-cluster-infrastructure-datadog-agent-service.tf @@ -235,12 +235,17 @@ resource "aws_ecs_task_definition" "infrastructure_ecs_cluster_datadog_agent" { ] } }) - security_options = jsonencode([]) - syslog_address = "" - syslog_tag = "" - cloudwatch_log_group = aws_cloudwatch_log_group.infrastructure_ecs_cluster_datadog_agent[0].name - awslogs_stream_prefix = "" - region = local.aws_region + security_options = jsonencode([]) + syslog_address = "" + syslog_tag = "" + cloudwatch_log_group = aws_cloudwatch_log_group.infrastructure_ecs_cluster_datadog_agent[0].name + awslogs_stream_prefix = "" + region = local.aws_region + enable_sidecar_container = false + sidecar_container_name = "" + sidecar_image = "" + sidecar_environment = "[]" + sidecar_entrypoint = "[]" } ) diff --git a/ecs-cluster-infrastructure-logspout-service.tf b/ecs-cluster-infrastructure-logspout-service.tf index 6459705..8187ffe 100644 --- a/ecs-cluster-infrastructure-logspout-service.tf +++ b/ecs-cluster-infrastructure-logspout-service.tf @@ -20,13 +20,18 @@ resource "aws_ecs_task_definition" "infrastructure_ecs_cluster_logspout" { containerPath = "/var/run/docker.sock" } ]) - linux_parameters = jsonencode({}) - security_options = jsonencode([]) - syslog_address = "" - syslog_tag = "" - cloudwatch_log_group = "" - awslogs_stream_prefix = "" - region = local.aws_region + linux_parameters = jsonencode({}) + security_options = jsonencode([]) + syslog_address = "" + syslog_tag = "" + cloudwatch_log_group = "" + awslogs_stream_prefix = "" + region = local.aws_region + enable_sidecar_container = false + sidecar_container_name = "" + sidecar_image = "" + sidecar_environment = "[]" + sidecar_entrypoint = "[]" } ) diff --git a/ecs-cluster-infrastructure-service-scheduled-task.tf b/ecs-cluster-infrastructure-service-scheduled-task.tf index 31de5b5..35b49a5 100644 --- a/ecs-cluster-infrastructure-service-scheduled-task.tf +++ b/ecs-cluster-infrastructure-service-scheduled-task.tf @@ -40,12 +40,17 @@ resource "aws_ecs_task_definition" "infrastructure_ecs_cluster_service_scheduled linux_parameters = jsonencode({ initProcessEnabled = false }) - security_options = jsonencode([]) - syslog_address = !local.infrastructure_ecs_cluster_logspout_enabled ? local.infrastructure_ecs_cluster_syslog_docker_address : "" - syslog_tag = "${local.resource_prefix}-${each.key}-{{.ID}}" - cloudwatch_log_group = !local.infrastructure_ecs_cluster_logspout_enabled ? each.value["enable_cloudwatch_logs"] == true ? aws_cloudwatch_log_group.infrastructure_ecs_cluster_service[each.value["container_name"]].name : "" : "" - awslogs_stream_prefix = "" - region = local.aws_region + security_options = jsonencode([]) + syslog_address = !local.infrastructure_ecs_cluster_logspout_enabled ? local.infrastructure_ecs_cluster_syslog_docker_address : "" + syslog_tag = "${local.resource_prefix}-${each.key}-{{.ID}}" + cloudwatch_log_group = !local.infrastructure_ecs_cluster_logspout_enabled ? each.value["enable_cloudwatch_logs"] == true ? aws_cloudwatch_log_group.infrastructure_ecs_cluster_service[each.value["container_name"]].name : "" : "" + awslogs_stream_prefix = "" + region = local.aws_region + enable_sidecar_container = false + sidecar_container_name = "" + sidecar_image = "" + sidecar_environment = "[]" + sidecar_entrypoint = "[]" } ) execution_role_arn = aws_iam_role.infrastructure_ecs_cluster_service_task_execution[each.value["container_name"]].arn diff --git a/ecs-cluster-infrastructure-service.tf b/ecs-cluster-infrastructure-service.tf index 998edc5..24732c3 100644 --- a/ecs-cluster-infrastructure-service.tf +++ b/ecs-cluster-infrastructure-service.tf @@ -242,12 +242,17 @@ resource "aws_ecs_task_definition" "infrastructure_ecs_cluster_service" { linux_parameters = each.value["enable_execute_command"] == true ? jsonencode({ initProcessEnabled = true }) : "{}" - security_options = jsonencode([]) - syslog_address = !local.infrastructure_ecs_cluster_logspout_enabled ? local.infrastructure_ecs_cluster_syslog_docker_address : "" - syslog_tag = "${local.resource_prefix}-${each.key}-{{.ID}}" - cloudwatch_log_group = !local.infrastructure_ecs_cluster_logspout_enabled ? each.value["enable_cloudwatch_logs"] == true ? aws_cloudwatch_log_group.infrastructure_ecs_cluster_service[each.key].name : "" : "" - awslogs_stream_prefix = "" - region = local.aws_region + security_options = jsonencode([]) + syslog_address = !local.infrastructure_ecs_cluster_logspout_enabled ? local.infrastructure_ecs_cluster_syslog_docker_address : "" + syslog_tag = "${local.resource_prefix}-${each.key}-{{.ID}}" + cloudwatch_log_group = !local.infrastructure_ecs_cluster_logspout_enabled ? each.value["enable_cloudwatch_logs"] == true ? aws_cloudwatch_log_group.infrastructure_ecs_cluster_service[each.key].name : "" : "" + awslogs_stream_prefix = "" + region = local.aws_region + enable_sidecar_container = each.value["enable_sidecar_container"] + sidecar_container_name = "${each.key}-sidecar" + sidecar_image = each.value["sidecar_image"] + sidecar_environment = "[]" + sidecar_entrypoint = "[]" } ) execution_role_arn = aws_iam_role.infrastructure_ecs_cluster_service_task_execution[each.key].arn diff --git a/rds-infrastructure-s3-backups-task-definition.tf b/rds-infrastructure-s3-backups-task-definition.tf index 9de6eac..4a79ac3 100644 --- a/rds-infrastructure-s3-backups-task-definition.tf +++ b/rds-infrastructure-s3-backups-task-definition.tf @@ -168,12 +168,17 @@ resource "aws_ecs_task_definition" "infrastructure_rds_s3_backups_scheduled_task linux_parameters = jsonencode({ initProcessEnabled = false }) - security_options = jsonencode([]) - syslog_address = "" - syslog_tag = "" - cloudwatch_log_group = aws_cloudwatch_log_group.infrastructure_rds_s3_backups[each.key].name - awslogs_stream_prefix = "${local.resource_prefix}-rds-s3-backups-${each.key}" - region = local.aws_region + security_options = jsonencode([]) + syslog_address = "" + syslog_tag = "" + cloudwatch_log_group = aws_cloudwatch_log_group.infrastructure_rds_s3_backups[each.key].name + awslogs_stream_prefix = "${local.resource_prefix}-rds-s3-backups-${each.key}" + region = local.aws_region + enable_sidecar_container = false + sidecar_container_name = "" + sidecar_image = "" + sidecar_environment = "[]" + sidecar_entrypoint = "[]" } ) execution_role_arn = aws_iam_role.infrastructure_rds_s3_backups_task_execution[each.key].arn diff --git a/variables.tf b/variables.tf index d273983..cafcbf7 100644 --- a/variables.tf +++ b/variables.tf @@ -580,9 +580,11 @@ variable "infrastructure_ecs_cluster_services" { container_count: Number of containers to launch for the service container_heath_check_path: Destination for the health check request container_heath_grace_period: Seconds to ignore failing load balancer health checks on newly instantiated tasks to prevent premature shutdown + enable_sidecar_container: Launch a sidecar container that will act as a proxy for all incoming traffic + sidecar_image: A specific Docker tag to use for the sidecar container. Defaults to nginx:stable (eg. nginx:1.27.1) scheduled_tasks: A map of scheduled tasks that use the same image as the service defined eg. { "name" => { "entrypoint" = ["bundle", "exec", "run_jobs"], "schedule_expression" = "cron(* * * * ? *)" } } domain_names: Domain names to assign to CloudFront aliases, and the Application Load Balancer's `host_header` condition - enable_cloudfront: Enable cloadfront for the service + enable_cloudfront: Enable CloudFront for the service cloudfront_tls_certificate_arn: Certificate ARN to attach to CloudFront - must contain the names provided in `domain_names` cloudfront_access_logging_enabled: Enable access logging for the distribution to the infrastructure S3 logs bucket cloudfront_bypass_protection_enabled: This adds a secret header at the CloudFront level, which is then checked by the ALB listener rules. Requests are only forwarded if the header matches, preventing requests going directly to the ALB. @@ -633,6 +635,8 @@ variable "infrastructure_ecs_cluster_services" { container_count = optional(number, null) container_heath_check_path = optional(string, null) container_heath_grace_period = optional(number, null) + enable_sidecar_container = optional(bool, false) + sidecar_image = optional(string, "nginx:stable") scheduled_tasks = optional(map(object({ entrypoint = list(string) schedule_expression = string
github_v1_source = optional(bool, null)
github_v1_oauth_token = optional(string, null)
codestar_connection_arn = optional(string, null)
github_owner = optional(string, null)
github_repo = optional(string, null)
github_track_revision = optional(string, null)
buildspec = optional(string, null)
buildspec_from_github_repo = optional(bool, null)
codebuild_environment_variables = optional(list(object({
name = string
value = string
})), [])
ecr_scan_target_sns_topic_arn = optional(string, null)
deployment_type = optional(string, null)
enable_cloudwatch_logs = optional(bool, null)
cloudwatch_logs_retention = optional(number, null)
enable_execute_command = optional(bool, null)
deregistration_delay = optional(number, null)
custom_policies = optional(map(object({
description = string
policy = object({
Version = string
Statement = list(object({
Action = list(string)
Effect = string
Resource = list(string)
}))
})
})), {})
container_entrypoint = optional(list(string), null)
container_port = optional(number, null)
container_volumes = optional(list(map(string)), null)
container_extra_hosts = optional(list(map(string)), null)
container_count = optional(number, null)
container_heath_check_path = optional(string, null)
container_heath_grace_period = optional(number, null)
enable_sidecar_container = optional(bool, false)
sidecar_image = optional(string, "nginx:stable")
scheduled_tasks = optional(map(object({
entrypoint = list(string)
schedule_expression = string
})), null)
domain_names = optional(list(string), null)
enable_cloudfront = optional(bool, null)
cloudfront_tls_certificate_arn = optional(string, null)
cloudfront_access_logging_enabled = optional(bool, null)
cloudfront_bypass_protection_enabled = optional(bool, null)
cloudfront_bypass_protection_excluded_domains = optional(list(string), null)
cloudfront_origin_shield_enabled = optional(bool, null)
cloudfront_managed_cache_policy = optional(string, null)
cloudfront_managed_origin_request_policy = optional(string, null)
cloudfront_managed_response_headers_policy = optional(string, null)
cloudfront_waf_association = optional(string, null)
alb_tls_certificate_arn = optional(string, null)
}))