diff --git a/infra/modules/aws/_shared/service/README.md b/infra/modules/aws/_shared/service/README.md index 23890d99..9efa3943 100644 --- a/infra/modules/aws/_shared/service/README.md +++ b/infra/modules/aws/_shared/service/README.md @@ -41,8 +41,7 @@ When `connection_type = "vpc_link"`, the module can also attach a shared API Gat Bootstrap ECS services resolve the shared placeholder image from the ECR repository named by `ecr_repository_name` using the stable `bootstrap` tag. That placeholder image is expected to be a stable shared tag, so infra applies can reuse the same bootstrap task definition input instead of churning a new placeholder image reference on every release. -Bootstrap health checks use `/`. -Real task deploys use the normal app health path, such as `/health` or `//health`. +Bootstrap and real task deploys use the same app health path, such as `/health` or `//health`, so target group health checks do not need to change during the transition from bootstrap to the deployed task. ## Decision Rules diff --git a/infra/modules/aws/_shared/service/locals.tf b/infra/modules/aws/_shared/service/locals.tf index 71175172..dae3044b 100644 --- a/infra/modules/aws/_shared/service/locals.tf +++ b/infra/modules/aws/_shared/service/locals.tf @@ -19,7 +19,8 @@ locals { green_target_group_name = "tg-${substr(md5("${var.service_name}-green"), 0, 8)}-green" is_default_path = var.root_path == "" - health_check_path = var.bootstrap ? "/" : (local.is_default_path ? "/health" : "/${var.root_path}/health") + health_check_path = local.is_default_path ? "/health" : "/${var.root_path}/health" + bootstrap_health_setup = local.is_default_path ? "printf 'ok\\n' > /usr/share/nginx/html/health" : "mkdir -p /usr/share/nginx/html/${var.root_path} && printf 'ok\\n' > /usr/share/nginx/html/health && printf 'ok\\n' > /usr/share/nginx/html/${var.root_path}/health" exact_route_key = local.is_default_path ? "ANY /" : "ANY /${var.root_path}" proxy_route_key = local.is_default_path ? "ANY /{proxy+}" : "ANY /${var.root_path}/{proxy+}" target_group_arn = local.is_default_path ? var.default_target_group_arn : aws_lb_target_group.service_target_group[0].arn @@ -90,7 +91,7 @@ locals { command = [ "sh", "-c", - "printf 'ok\\n' > /usr/share/nginx/html/health && exec nginx -g 'daemon off;'", + "${local.bootstrap_health_setup} && exec nginx -g 'daemon off;'", ] portMappings = [ diff --git a/infra/modules/aws/frontend/README.md b/infra/modules/aws/frontend/README.md index 612f65b9..064619e8 100644 --- a/infra/modules/aws/frontend/README.md +++ b/infra/modules/aws/frontend/README.md @@ -50,4 +50,4 @@ If the hosted zone does not already exist, certificate validation and alias-reco Used by the frontend build and deploy workflow path. -The Terraform module uploads a bootstrap `index.html` so the distribution serves a valid page before the built frontend assets are published. Later frontend deploys replace that object with the real app bundle output. +The Terraform module uploads a bootstrap `index.html` so the distribution serves a valid page before the built frontend assets are published. Later frontend deploys replace that object with the real app bundle output, so Terraform intentionally ignores live content and metadata drift on that bootstrap object after creation. diff --git a/infra/modules/aws/frontend/main.tf b/infra/modules/aws/frontend/main.tf index 31c43756..396fc77d 100644 --- a/infra/modules/aws/frontend/main.tf +++ b/infra/modules/aws/frontend/main.tf @@ -31,6 +31,15 @@ resource "aws_s3_object" "bootstrap_index" { source = "${path.module}/bootstrap/index.html" etag = filemd5("${path.module}/bootstrap/index.html") content_type = "text/html; charset=utf-8" + + lifecycle { + ignore_changes = [ + content_type, + etag, + source, + tags_all, + ] + } } resource "aws_s3_object" "auth_config" { diff --git a/infra/modules/aws/rds_reader_tagger/main.tf b/infra/modules/aws/rds_reader_tagger/main.tf index ea68c86a..864643a3 100644 --- a/infra/modules/aws/rds_reader_tagger/main.tf +++ b/infra/modules/aws/rds_reader_tagger/main.tf @@ -45,7 +45,8 @@ resource "aws_cloudwatch_event_target" "reader_instance_created" { resource "aws_lambda_permission" "allow_eventbridge" { statement_id = "AllowEventBridgeInvoke" action = "lambda:InvokeFunction" - function_name = module.rds_reader_tagger.alias_arn + function_name = module.rds_reader_tagger.arn principal = "events.amazonaws.com" + qualifier = module.rds_reader_tagger.alias_name source_arn = aws_cloudwatch_event_rule.reader_instance_created.arn }