From b0d4d3836f312911408adaf0542b25d085a86794 Mon Sep 17 00:00:00 2001 From: "Keith A. Taylor" <120050018+fractal360@users.noreply.github.com> Date: Wed, 20 May 2026 19:06:01 +0100 Subject: [PATCH] Clarify Terraform bootstrap image tag ownership --- docs/aws_terraform_deployment_sequence.md | 42 ++++++++++++++++++++--- infra/terraform/ecs_task_definition.tf | 2 +- infra/terraform/variables.tf | 8 ++++- 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/docs/aws_terraform_deployment_sequence.md b/docs/aws_terraform_deployment_sequence.md index 7055507..78928da 100644 --- a/docs/aws_terraform_deployment_sequence.md +++ b/docs/aws_terraform_deployment_sequence.md @@ -752,7 +752,6 @@ Deferred production hardening: - production credential registry/admin process - migration version table - CI-before-deploy safety clarification and deployment guardrails -- Terraform image tag handling alignment with SHA-based CD --- @@ -804,7 +803,42 @@ Important distinction: --- -## 20. Implemented AWS runtime mapping +## 20. Clarified Terraform bootstrap image tag ownership + +Files: + +- `infra/terraform/ecs_task_definition.tf` +- `infra/terraform/variables.tf` +- `infra/terraform/ecs_service.tf` + +Why: + +- Terraform needs an image reference so it can create the initial ECS task definition. +- GitHub Actions CD owns real application releases after infrastructure exists. +- The CD workflow deploys immutable Git commit SHA image tags by registering new ECS task definition revisions. +- Terraform should not roll back the ECS service to its original bootstrap task definition during later infrastructure applies. + +Implemented changes: + +- Added `var.bootstrap_image_tag`, defaulting to `latest`. +- Updated the Terraform ECS task definition image reference to use `var.bootstrap_image_tag`. +- Kept the ECS service lifecycle rule that ignores `task_definition` and `desired_count`. + +Important distinction: + +- Terraform owns the ECS task definition shape and bootstrap image reference. +- GitHub Actions CD owns the currently deployed application image revision. +- Manual operations may temporarily own `desired_count`, for example when pausing the service at zero. +- Terraform should not treat SHA-based CD deployments as infrastructure drift. + +Verified result: + +- `terraform fmt` completed. +- `terraform validate` passed. +- `terraform plan` reported no changes because `var.bootstrap_image_tag` still defaults to `latest`. + + +## 21. Implemented AWS runtime mapping Local Docker Compose mapping: @@ -838,7 +872,7 @@ The application settings code continues reading the same variable names. The dep --- -## 21. Local-to-AWS environment mapping +## 22. Local-to-AWS environment mapping Local app configuration: @@ -899,7 +933,7 @@ Example smoke checks: --- -## 22. Why so many explicit resources are required +## 23. Why so many explicit resources are required AWS does not infer the runtime wiring automatically. diff --git a/infra/terraform/ecs_task_definition.tf b/infra/terraform/ecs_task_definition.tf index eb97f2a..78fc3c4 100644 --- a/infra/terraform/ecs_task_definition.tf +++ b/infra/terraform/ecs_task_definition.tf @@ -16,7 +16,7 @@ resource "aws_ecs_task_definition" "app_task_definition" { container_definitions = jsonencode([ { name = "app" - image = "${aws_ecr_repository.app.repository_url}:latest" + image = "${aws_ecr_repository.app.repository_url}:${var.bootstrap_image_tag}" essential = true portMappings = [ diff --git a/infra/terraform/variables.tf b/infra/terraform/variables.tf index 7df641f..312a657 100644 --- a/infra/terraform/variables.tf +++ b/infra/terraform/variables.tf @@ -19,4 +19,10 @@ variable "environment" { variable "agent_credential_hash_secret_arn" { description = "ARN of the existing Secrets Manager secret containing AGENT_CREDENTIAL_HASH_SECRET." type = string -} \ No newline at end of file +} + +variable "bootstrap_image_tag" { + description = "Initial image tag used by Terraform when creating the bootstrap ECS task definition. GitHub Actions CD deploys immutable Git SHA image tags after infrastructure exists." + type = string + default = "latest" +}