diff --git a/README.md b/README.md
index 064a6b1..911ab3b 100644
--- a/README.md
+++ b/README.md
@@ -144,6 +144,8 @@ This project creates and manages resources within an AWS account for infrastruct
| [aws_iam_policy.infrastructure_ecs_cluster_service_scheduled_task_ecs_run_task](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_policy.infrastructure_ecs_cluster_service_scheduled_task_pass_role_execution_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_policy.infrastructure_ecs_cluster_service_task_custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
+| [aws_iam_policy.infrastructure_ecs_cluster_service_task_ecs_exec_log_kms_decrypt](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
+| [aws_iam_policy.infrastructure_ecs_cluster_service_task_ecs_exec_log_s3_write](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_policy.infrastructure_ecs_cluster_service_task_execution_cloudwatch_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_policy.infrastructure_ecs_cluster_service_task_execution_ecr_pull](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_policy.infrastructure_ecs_cluster_service_task_execution_kms_decrypt](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
@@ -197,6 +199,8 @@ This project creates and manages resources within an AWS account for infrastruct
| [aws_iam_role_policy_attachment.infrastructure_ecs_cluster_service_scheduled_task_ecs_run_task](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_iam_role_policy_attachment.infrastructure_ecs_cluster_service_scheduled_task_pass_role_execution_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_iam_role_policy_attachment.infrastructure_ecs_cluster_service_task_custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
+| [aws_iam_role_policy_attachment.infrastructure_ecs_cluster_service_task_ecs_exec_log_kms_decrypt](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
+| [aws_iam_role_policy_attachment.infrastructure_ecs_cluster_service_task_ecs_exec_log_s3_write](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_iam_role_policy_attachment.infrastructure_ecs_cluster_service_task_execution_cloudwatch_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_iam_role_policy_attachment.infrastructure_ecs_cluster_service_task_execution_ecr_pull](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_iam_role_policy_attachment.infrastructure_ecs_cluster_service_task_execution_kms_decrypt](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
@@ -410,6 +414,7 @@ This project creates and manages resources within an AWS account for infrastruct
| [infrastructure\_ecs\_cluster\_ecs\_asg\_diff\_alert\_threshold](#input\_infrastructure\_ecs\_cluster\_ecs\_asg\_diff\_alert\_threshold) | Threshold (Number of pending tasks) for the ECS cluster's Container Instance / ASG instance diff alert | `number` | n/a | yes |
| [infrastructure\_ecs\_cluster\_ecs\_asg\_diff\_metric\_lambda\_log\_retention](#input\_infrastructure\_ecs\_cluster\_ecs\_asg\_diff\_metric\_lambda\_log\_retention) | Log retention for the ECS cluster Container Instance / ASG instance diff metric Lambda | `number` | n/a | yes |
| [infrastructure\_ecs\_cluster\_enable\_debug\_mode](#input\_infrastructure\_ecs\_cluster\_enable\_debug\_mode) | Enable debug mode for ECS and Docker on the Infrastructure ECS. This should only be enabled when debugging (Can cause a lot of logs) | `bool` | n/a | yes |
+| [infrastructure\_ecs\_cluster\_enable\_execute\_command\_logging](#input\_infrastructure\_ecs\_cluster\_enable\_execute\_command\_logging) | Enable ECS Exec logging for services within the cluster. This will log to the infrastructure logs S3 bucket | `bool` | n/a | yes |
| [infrastructure\_ecs\_cluster\_instance\_type](#input\_infrastructure\_ecs\_cluster\_instance\_type) | The instance type for EC2 instances launched in the ECS cluster | `string` | n/a | yes |
| [infrastructure\_ecs\_cluster\_max\_instance\_lifetime](#input\_infrastructure\_ecs\_cluster\_max\_instance\_lifetime) | Maximum lifetime in seconds of an instance within the ECS cluster | `number` | n/a | yes |
| [infrastructure\_ecs\_cluster\_max\_size](#input\_infrastructure\_ecs\_cluster\_max\_size) | Maximum number of instances for the ECS cluster | `number` | n/a | yes |
diff --git a/ecs-cluster-infrastructure-service.tf b/ecs-cluster-infrastructure-service.tf
index a776983..46c01f7 100644
--- a/ecs-cluster-infrastructure-service.tf
+++ b/ecs-cluster-infrastructure-service.tf
@@ -122,6 +122,48 @@ resource "aws_iam_role_policy_attachment" "infrastructure_ecs_cluster_service_ta
policy_arn = aws_iam_policy.infrastructure_ecs_cluster_service_task_ssm_create_channels[each.key].arn
}
+resource "aws_iam_policy" "infrastructure_ecs_cluster_service_task_ecs_exec_log_s3_write" {
+ for_each = {
+ for k, v in local.infrastructure_ecs_cluster_services : k => v if v["enable_execute_command"] == true
+ }
+
+ name = "${local.resource_prefix}-${substr(sha512("ecs-cluster-service-task-${each.key}-ecs-exec-log-s3-write"), 0, 6)}"
+ description = "${local.resource_prefix}-ecs-cluster-service-task-${each.key}-ecs-exec-log-s3-write"
+ policy = templatefile("${path.root}/policies/s3-object-write.json.tpl", {
+ bucket_arn = aws_s3_bucket.infrastructure_logs[0].arn
+ path = "/ecs-exec/*"
+ })
+}
+
+resource "aws_iam_role_policy_attachment" "infrastructure_ecs_cluster_service_task_ecs_exec_log_s3_write" {
+ for_each = {
+ for k, v in local.infrastructure_ecs_cluster_services : k => v if v["enable_execute_command"] == true
+ }
+
+ role = aws_iam_role.infrastructure_ecs_cluster_service_task[each.key].name
+ policy_arn = aws_iam_policy.infrastructure_ecs_cluster_service_task_ecs_exec_log_s3_write[each.key].arn
+}
+
+resource "aws_iam_policy" "infrastructure_ecs_cluster_service_task_ecs_exec_log_kms_decrypt" {
+ for_each = {
+ for k, v in local.infrastructure_ecs_cluster_services : k => v if v["enable_execute_command"] == true && local.infrastructure_kms_encryption
+ }
+
+ name = "${local.resource_prefix}-${substr(sha512("ecs-cluster-service-task-${each.key}-ecs-exec-log-kms-decrypt"), 0, 6)}"
+ description = "${local.resource_prefix}-ecs-cluster-service-task-${each.key}-ecs-exec-log-kms-decrypt"
+ policy = templatefile("${path.root}/policies/kms-decrypt.json.tpl", {
+ kms_key_arn = aws_kms_key.infrastructure[0].arn
+ })
+}
+resource "aws_iam_role_policy_attachment" "infrastructure_ecs_cluster_service_task_ecs_exec_log_kms_decrypt" {
+ for_each = {
+ for k, v in local.infrastructure_ecs_cluster_services : k => v if v["enable_execute_command"] == true && local.infrastructure_kms_encryption
+ }
+
+ role = aws_iam_role.infrastructure_ecs_cluster_service_task[each.key].name
+ policy_arn = aws_iam_policy.infrastructure_ecs_cluster_service_task_ecs_exec_log_kms_decrypt[each.key].arn
+}
+
resource "aws_iam_policy" "infrastructure_ecs_cluster_service_task_custom" {
for_each = merge([
for service_name, service in local.infrastructure_ecs_cluster_services : {
diff --git a/ecs-cluster-infrastructure.tf b/ecs-cluster-infrastructure.tf
index 9f61fd9..785132d 100644
--- a/ecs-cluster-infrastructure.tf
+++ b/ecs-cluster-infrastructure.tf
@@ -7,6 +7,20 @@ resource "aws_ecs_cluster" "infrastructure" {
name = "containerInsights"
value = "enabled"
}
+
+ dynamic "configuration" {
+ for_each = local.infrastructure_ecs_cluster_enable_execute_command_logging ? [1] : []
+ execute_command_configuration {
+ kms_key_id = local.infrastructure_kms_encryption ? aws_kms_key.infrastructure[0].arn : null
+ logging = "OVERRIDE"
+
+ log_configuration {
+ s3_bucket_encryption_enabled = true
+ s3_bucket_name = aws_s3_bucket.infrastructure_logs[0].id
+ s3_key_prefix = "ecs-exec"
+ }
+ }
+ }
}
resource "aws_iam_role" "infrastructure_ecs_cluster" {
diff --git a/kms-infrastructure.tf b/kms-infrastructure.tf
index 67ddcb3..a91ec14 100644
--- a/kms-infrastructure.tf
+++ b/kms-infrastructure.tf
@@ -39,6 +39,13 @@ resource "aws_kms_key" "infrastructure" {
{
log_group_arn = length(local.infrastructure_ecs_cluster_services) > 0 && local.infrastructure_kms_encryption ? "arn:aws:logs:${local.aws_region}:${local.aws_account_id}:log-group:${local.resource_prefix}-infrastructure-ecs-cluster-service-logs-*" : ""
}
+ )}${length(local.infrastructure_ecs_cluster_services) > 0 && local.infrastructure_kms_encryption && local.infrastructure_ecs_cluster_enable_execute_command_logging ? "," : ""}
+ ${templatefile("${path.root}/policies/kms-key-policy-statements/role-allow-encrypt.json.tpl",
+ {
+ role_arns = jsonencode([
+ for k, v in local.infrastructure_ecs_cluster_services : aws_iam_role.infrastructure_ecs_cluster_service_task_execution[k].name if v["enable_execute_command"] == true
+ ])
+ }
)}${contains([for k, v in local.custom_s3_buckets : (v["cloudfront_dedicated_distribution"] == true || v["cloudfront_infrastructure_ecs_cluster_service"] != null) && v["create_dedicated_kms_key"] == false ? true : false], true) && local.infrastructure_kms_encryption ? "," : ""}
${templatefile("${path.root}/policies/kms-key-policy-statements/cloudfront-distribution-allow.json.tpl",
{
diff --git a/locals.tf b/locals.tf
index b319c8f..0b1cbe0 100644
--- a/locals.tf
+++ b/locals.tf
@@ -29,7 +29,8 @@ locals {
length(local.infrastructure_ecs_cluster_services) != 0 ||
length(local.custom_s3_buckets) != 0 ||
local.enable_cloudformatian_s3_template_store ||
- local.enable_infrastructure_vpc_transfer_s3_bucket
+ local.enable_infrastructure_vpc_transfer_s3_bucket ||
+ local.infrastructure_ecs_cluster_enable_execute_command_logging
)
logs_bucket_s3_source_arns = concat(
length(local.infrastructure_ecs_cluster_services) != 0 ? [aws_s3_bucket.infrastructure_ecs_cluster_service_build_pipeline_artifact_store[0].arn] : [],
@@ -169,6 +170,7 @@ locals {
infrastructure_ecs_cluster_ecs_asg_diff_alert_slack = var.infrastructure_ecs_cluster_ecs_asg_diff_alert_slack
infrastructure_ecs_cluster_ecs_asg_diff_alert_opsgenie = var.infrastructure_ecs_cluster_ecs_asg_diff_alert_opsgenie
infrastructure_ecs_cluster_enable_debug_mode = var.infrastructure_ecs_cluster_enable_debug_mode
+ infrastructure_ecs_cluster_enable_execute_command_logging = var.infrastructure_ecs_cluster_enable_execute_command_logging
infrastructure_ecs_cluster_wafs = var.infrastructure_ecs_cluster_wafs
infrastructure_ecs_cluster_enable_ssm_dhmc = local.enable_infrastructure_ecs_cluster ? data.external.ssm_dhmc_setting[0].result.setting_value != "$None" : false
infrastructure_ecs_cluster_user_data = base64encode(
diff --git a/policies/kms-key-policy-statements/role-allow-encrypt.json.tpl b/policies/kms-key-policy-statements/role-allow-encrypt.json.tpl
new file mode 100644
index 0000000..8c95608
--- /dev/null
+++ b/policies/kms-key-policy-statements/role-allow-encrypt.json.tpl
@@ -0,0 +1,11 @@
+%{if role_arns != "[]"}{
+ "Effect": "Allow",
+ "Principal": {
+ "AWS": ${role_arns}
+ },
+ "Action": [
+ "kms:GenerateDataKey*",
+ "kms:Decrypt"
+ ],
+ "Resource": "*"
+}%{endif}
diff --git a/policies/s3-object-write.json.tpl b/policies/s3-object-write.json.tpl
new file mode 100644
index 0000000..c8899e7
--- /dev/null
+++ b/policies/s3-object-write.json.tpl
@@ -0,0 +1,21 @@
+{
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Action": [
+ "s3:PutObject"
+ ],
+ "Resource": [
+ "${bucket_arn}${path}"
+ ]
+ },
+ {
+ "Effect": "Allow",
+ "Action": "s3:GetEncryptionConfiguration",
+ "Resource": [
+ "${bucket_arn}"
+ ]
+ }
+ ]
+}
diff --git a/variables.tf b/variables.tf
index bb2dd8f..f88926f 100644
--- a/variables.tf
+++ b/variables.tf
@@ -418,6 +418,11 @@ variable "infrastructure_ecs_cluster_enable_debug_mode" {
type = bool
}
+variable "infrastructure_ecs_cluster_enable_execute_command_logging" {
+ description = "Enable ECS Exec logging for services within the cluster. This will log to the infrastructure logs S3 bucket"
+ type = bool
+}
+
variable "infrastructure_ecs_cluster_wafs" {
description = "Map of WAF ACLs to craete, which can be used with service CloudFront distributions"
type = map(object({