From ca448347327d8f7f5a1137b3a90de38c132ae0b3 Mon Sep 17 00:00:00 2001 From: Adam Moussa Date: Mon, 21 Sep 2026 15:06:03 -0400 Subject: [PATCH] fix(iam): list ECS tasks and fail closed on non-prod Paychex (PLAT-216) --- SETUP.md | 10 +++++++--- terraform/data.tf | 25 +++++++++++++++++++++++++ terraform/iam_github_deploy.tf | 1 + terraform/outputs.tf | 5 +++++ tests/infra/test_hcp_contract.py | 4 ++++ 5 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 terraform/data.tf diff --git a/SETUP.md b/SETUP.md index c982875..f95b38a 100644 --- a/SETUP.md +++ b/SETUP.md @@ -105,15 +105,19 @@ First apply uses the hcptf-bootstrap window (exact `StringEquals` trust, never GitHub Environment `prod`: reviewers, branch policy `main` and `v*`, Environment variable `DEPLOY_ROLE_ARN` = Terraform output `github_deploy_role_arn`. -GitHub Environment `dev`: same `DEPLOY_ROLE_ARN` in the matching account, -branch policy as needed for image CD. +GitHub Environment `dev`: no reviewers, `DEPLOY_ROLE_ARN` from the seahaven-dev +apply of the same output. Set `checkcomponents_queue_url` and +`checkcomponents_queue_arn` empty on the dev workspace. Function zips: Actions → Deploy on push to `main`, or `workflow_dispatch`. Image CD: Actions → Deploy API (`deploy-api.yaml`). Keep `schedules_enabled=false` and `ecs_schedules_enabled=false` until the Fargate cutover below. HCP outputs to copy: `slack_request_url`, `api_origin`, `fargate_origin`, -`holiday_scheduler_role_arn`, `github_deploy_role_arn`, `jobs_queue_arn`. +`holiday_scheduler_role_arn`, `github_deploy_role_arn`, `jobs_queue_arn`, +`ecs_task_role_arn`. Add `ecs_task_role_arn` to paychex-checkcomponents +(alongside `afterhours-shift-manager-weekly-post`) before Fargate weekly_post +runs. Dual-run keeps the Lambda principal until zip CD is retired. ## 4. Set the Slack Request URL diff --git a/terraform/data.tf b/terraform/data.tf new file mode 100644 index 0000000..8ad9897 --- /dev/null +++ b/terraform/data.tf @@ -0,0 +1,25 @@ +data "aws_caller_identity" "current" {} + +# Resource names in locals.tf embed the account ID. If the workspace is ever +# pointed at another account, fail the plan here rather than creating a parallel +# set of oddly-named resources somewhere else. +check "correct_account" { + assert { + condition = data.aws_caller_identity.current.account_id == local.account_id + error_message = "This configuration targets account ${local.account_id} (${var.environment}), but the credentials resolve to ${data.aws_caller_identity.current.account_id}." + } +} + +check "dev_has_no_paychex" { + assert { + condition = local.is_prod || var.checkcomponents_queue_url == "" + error_message = "checkcomponents_queue_url must be empty in non-prod so weekly_post cannot send to the prod Paychex queue." + } +} + +check "checkcomponents_pair" { + assert { + condition = (var.checkcomponents_queue_url == "") == (var.checkcomponents_queue_arn == "") + error_message = "checkcomponents_queue_url and checkcomponents_queue_arn must both be set or both be empty." + } +} diff --git a/terraform/iam_github_deploy.tf b/terraform/iam_github_deploy.tf index da48972..8010098 100644 --- a/terraform/iam_github_deploy.tf +++ b/terraform/iam_github_deploy.tf @@ -112,6 +112,7 @@ data "aws_iam_policy_document" "github_deploy" { "ecs:DescribeServices", "ecs:DescribeTaskDefinition", "ecs:DescribeTasks", + "ecs:ListTasks", "ecs:RegisterTaskDefinition", "ecs:UpdateService", ] diff --git a/terraform/outputs.tf b/terraform/outputs.tf index bfbe5fd..cf4f1b3 100644 --- a/terraform/outputs.tf +++ b/terraform/outputs.tf @@ -43,6 +43,11 @@ output "jobs_queue_arn" { value = aws_sqs_queue.jobs.arn } +output "ecs_task_role_arn" { + description = "ECS task role. paychex-checkcomponents queue policy must allow this ARN before Fargate weekly_post." + value = aws_iam_role.ecs_task.arn +} + output "hcptf_apply_role_arn" { description = "HCP apply role ARN. Set TFC_AWS_APPLY_ROLE_ARN after the bootstrap window." value = aws_iam_role.hcptf_apply.arn diff --git a/tests/infra/test_hcp_contract.py b/tests/infra/test_hcp_contract.py index 9a640e4..a2bc1fb 100644 --- a/tests/infra/test_hcp_contract.py +++ b/tests/infra/test_hcp_contract.py @@ -106,6 +106,9 @@ def test_checkcomponents_queue_arn_variable_matches_iam_references(): assert "var.checkcomponents_queue_arn" in LAMBDA_TF boundary = (TERRAFORM / "lambda_boundary.tf").read_text() assert "var.checkcomponents_queue_arn" in boundary + data_tf = (TERRAFORM / "data.tf").read_text() + assert "dev_has_no_paychex" in data_tf + assert "checkcomponents_pair" in data_tf def test_eight_functions_named(): @@ -134,6 +137,7 @@ def test_github_deploy_trust_covers_zip_and_image(): assert "deploy-api.yaml@refs/heads/${var.github_deploy_branch}" in iam assert "deploy-api.yaml@refs/tags/v*" in iam assert "deploy.yaml@*" not in iam + assert "ecs:ListTasks" in iam def test_plan_refresh_includes_provider6_s3_gets():