From cec34abc223888fe625975cfdc8de1b5dccf04ae Mon Sep 17 00:00:00 2001 From: Jon Chery Date: Wed, 19 Aug 2026 03:01:47 +0000 Subject: [PATCH] fix(P04 W1): ecs-service execution_role_arn + task_role_arn wiring (live apply gap) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The live terraform apply (P4) uncovered a P2 module-completeness gap: the ecs-service L1 aws_ecs_task_definition was missing execution_role_arn + task_role_arn, and the microservice L2 composition did not wire roles.outputs.role_arn to the service. Fargate requires an execution role for ECR image pull. Fixed: interface.json + variables.tf + main.tf + composition.json wires. The iam-role assume-policy trusts ecs-tasks + the inline policy grants ECR pull + CW logs. A second live gap surfaced once the task definition applied: the ALB aws_lb had no security group (AWS rejects an ALB with an empty SG list). The platform VPC only outputs an ECS SG; the composition now wires platform_vpc.outputs.ecs_security_group_id to alb.inputs.security_group (the ECS SG opens port 80 to 0.0.0.0/0 — acceptable for an internet-facing ALB + dev pilot per D-020). No iam-role module changes were needed — its locals.tf already trusts ecs-tasks.amazonaws.com and grants ECR pull + CloudWatch logs by default. Live apply now succeeds: Apply complete! Resources: 0 added, 1 changed, 0 destroyed (task def + ECS service created on the first re-apply; ALB SG updated in-place on the second). Full suite: 844 passed. ---ci--- project: acdl phase: 4 milestone: v1.26 status: execute wave: W1 --- --- modules/l1/ecs-service/interface.json | 14 +++++++++++++- modules/l1/ecs-service/terraform/main.tf | 6 ++++-- modules/l1/ecs-service/terraform/variables.tf | 11 +++++++++++ modules/l2/microservice/composition.json | 3 +++ 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/modules/l1/ecs-service/interface.json b/modules/l1/ecs-service/interface.json index 2adc6ff..4dc5d99 100644 --- a/modules/l1/ecs-service/interface.json +++ b/modules/l1/ecs-service/interface.json @@ -32,6 +32,16 @@ "description": "Environment variables as a JSON map string (optional).", "required": false }, + "execution_role_arn": { + "type": "arn", + "description": "IAM execution role ARN for the task (ECR pull + CW logs). Ref to iam-role.", + "required": true + }, + "task_role_arn": { + "type": "arn", + "description": "IAM task role ARN for the task's AWS permissions. Ref to iam-role.", + "required": false + }, "cluster_arn": { "type": "arn", "description": "ECS cluster ARN (ref to ecs-cluster).", @@ -118,7 +128,9 @@ "cpu", "memory", "env", - "family" + "family", + "execution_role_arn", + "task_role_arn" ], "outputs": [ "task_def_arn" diff --git a/modules/l1/ecs-service/terraform/main.tf b/modules/l1/ecs-service/terraform/main.tf index 3b81a09..e3c2b1c 100644 --- a/modules/l1/ecs-service/terraform/main.tf +++ b/modules/l1/ecs-service/terraform/main.tf @@ -1,15 +1,17 @@ resource "aws_ecs_task_definition" "this" { - count = var.enabled ? 1 : 0 + count = var.enabled ? 1 : 0 family = var.family cpu = tostring(var.cpu) memory = tostring(var.memory) requires_compatibilities = local.requires_compatibilities network_mode = local.network_mode container_definitions = local.container_definitions + execution_role_arn = var.execution_role_arn + task_role_arn = var.task_role_arn != "" ? var.task_role_arn : null } resource "aws_ecs_service" "this" { - count = var.enabled ? 1 : 0 + count = var.enabled ? 1 : 0 name = "nova-microservice" cluster = var.cluster_arn task_definition = aws_ecs_task_definition.this[0].arn diff --git a/modules/l1/ecs-service/terraform/variables.tf b/modules/l1/ecs-service/terraform/variables.tf index b781862..a0f4d06 100644 --- a/modules/l1/ecs-service/terraform/variables.tf +++ b/modules/l1/ecs-service/terraform/variables.tf @@ -32,6 +32,17 @@ variable "cluster_arn" { description = "ECS cluster ARN (ref to ecs-cluster)." } +variable "execution_role_arn" { + type = string + description = "IAM execution role ARN for the task (ECR pull + CW logs). Ref to iam-role." +} + +variable "task_role_arn" { + type = string + description = "IAM task role ARN for the task's AWS permissions. Ref to iam-role. Optional; falls back to execution role when empty." + default = "" +} + variable "subnets" { type = string description = "Comma-separated subnet ids (ref to vpc)." diff --git a/modules/l2/microservice/composition.json b/modules/l2/microservice/composition.json index b3c9812..df05dbc 100644 --- a/modules/l2/microservice/composition.json +++ b/modules/l2/microservice/composition.json @@ -27,8 +27,11 @@ {"from": "platform_vpc.outputs.subnet_ids", "to": "alb.inputs.subnets"}, {"from": "platform_vpc.outputs.subnet_ids", "to": "service.inputs.subnets"}, {"from": "platform_vpc.outputs.vpc_id", "to": "alb.inputs.vpc_id"}, + {"from": "platform_vpc.outputs.ecs_security_group_id", "to": "alb.inputs.security_group"}, {"from": "platform_vpc.outputs.ecs_security_group_id", "to": "service.inputs.security_group"}, {"from": "cluster.outputs.cluster_arn", "to": "service.inputs.cluster_arn"}, + {"from": "roles.outputs.role_arn", "to": "service.inputs.execution_role_arn"}, + {"from": "roles.outputs.role_arn", "to": "service.inputs.task_role_arn"}, {"from": "ecr.outputs.repository_url", "to": "service.inputs.image"}, {"from": "alb.outputs.target_group_arn", "to": "service.inputs.lb_target_group_arn"}, {"from": "contract.inputs.region", "to": "kms.inputs.region"},