Skip to content

Import home-unite-us production Cognito - #186

Merged
ale210 merged 3 commits into
mainfrom
166-home-unite-us-prod-terraform
Aug 30, 2026
Merged

Import home-unite-us production Cognito#186
ale210 merged 3 commits into
mainfrom
166-home-unite-us-prod-terraform

Conversation

@ale210

@ale210 ale210 commented Aug 30, 2026

Copy link
Copy Markdown
Member

First of three PRs for #166. This one only adopts the production Cognito stack into state — no infrastructure changes.

What this does

  • Renames cognito.tf to cognito-qa.tf. Terraform keys state on resource addresses rather than filenames, so the rename alone changes nothing.
  • Adds cognito-prod.tf, declaring the production pool Home Unite Us (us-west-2_VH24AGQ3p) and its client, domain, four user groups, Google identity provider, SMS role and inline policy, and the homeuniteus-cognito-client secret.
  • Adds the matching import blocks to terraform/import.tf, following the pattern from Bring civictechindex's AWS resources under Terraform #165.

Why import rather than rebuild

The production pool holds the live user accounts, and its pool id and client id are compiled into the running production container image. Rebuilding it would mean migrating users, which Cognito does not support without resetting passwords. The home-unite-us pool already in Terraform is a separate pool that only serves qa1.homeunite.us — both are real, and neither replaces the other.

The ECS service is the opposite case and is handled separately: it holds no state, so it gets recreated with the correct naming rather than imported.

The check that matters

No resource may be added, destroyed or replaced. That is the point of this PR — it proves the adoption is inert before anything else touches production. The provider sets default_tags, so a truly empty plan is not achievable: every imported resource gains managed-by = terraform-incubator, which is exactly the tag the coverage report reads. The bar is therefore that every change is tags or Terraform-side metadata, and nothing is created or replaced.

Current plan: 11 to import, 0 to add, 4 to change, 0 to destroy. The four changes are tags_all gaining managed-by on the pool, the IAM role and the secret; provider defaults (recovery_window_in_days, force_overwrite_replica_secret) being recorded for the secret; and the Google provider's client_id/client_secret being re-marked as sensitive, which the plan explicitly annotates as "The value is unchanged".

Two defects the first plan caught, both fixed in the second commit:

  • The user pool client planned as a replacement. generate_secret cannot be read back from the API, so Terraform saw null → true and that attribute forces replacement. Replacing the client would mint a new client id, which is compiled into the production frontend bundle — it would have broken sign-in. Fixed with ignore_changes = [generate_secret], plus prevent_destroy so any future replacement fails loudly instead.
  • Three of the four user groups planned as creates. Terraform honours only one import block per resource address, so four static blocks left three instances unimported. Fixed by collapsing them into a single for_each import block.

Three further details were needed to get the plan this clean, each commented in the file:

  • user_pool_tier = "LITE" is set explicitly. The pool predates the tier feature; the provider defaults the attribute to ESSENTIALS, so omitting it would plan a billing upgrade on the live pool.
  • lambda_config names the legacy customMessage and mergeUsers by literal ARN. Those stay unmanaged deliberately — re-pointing the pool at the already-managed home-unite-us-* pair is a live change to production sign-up and belongs in its own change.
  • Only the secret container is managed, not its version, so Terraform cannot rewrite the stored client secret.

The Google identity provider reads the same secrets as the QA one. The legacy homeuniteus-google-* pair holds byte-identical values, verified by digest, so they are redundant copies rather than separate credentials and are left for the cleanup PR.

Verification

  • terraform fmt clean, terraform validate passes on 1.16.0.
  • A local plan was not possible — the state was written by 1.16.0 and the pinned constraint is ~> 1.16.0, so CI is the first real plan.
  • After this merges, confirm sign-in still works at qa.homeunite.us, which is the production hostname the current image is built against.

Not in this PR

The production ECS service, the cutover, and deleting the legacy resources. The Lambda re-point is tracked as follow-on work.

@github-actions

github-actions Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Terraform plan in terraform
With backend config files: terraform/prod.backend.tfvars

Plan: 11 to import, 0 to add, 4 to change, 0 to destroy.
Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
!~  update in-place

Terraform will perform the following actions:

  # module.home-unite-us.aws_cognito_identity_provider.google_client_prod will be updated in-place
  # (imported from "us-west-2_VH24AGQ3p:Google")
!~  resource "aws_cognito_identity_provider" "google_client_prod" {
        attribute_mapping = {
            "birthdate"    = "birthdays"
            "email"        = "email"
            "family_name"  = "family_name"
            "gender"       = "genders"
            "given_name"   = "given_name"
            "name"         = "names"
            "phone_number" = "phoneNumbers"
            "picture"      = "picture"
            "username"     = "sub"
        }
        id                = "us-west-2_VH24AGQ3p:Google"
        idp_identifiers   = []
!~      provider_details  = {
            "attributes_url"                = "https://people.googleapis.com/v1/people/me?personFields="
            "attributes_url_add_attributes" = "true"
            "authorize_scopes"              = "email profile openid"
            "authorize_url"                 = "https://accounts.google.com/o/oauth2/v2/auth"
          # Warning: this attribute value will be marked as sensitive and will not
          # display in UI output after applying this change. The value is unchanged.
!~          "client_id"                     = (sensitive value)
          # Warning: this attribute value will be marked as sensitive and will not
          # display in UI output after applying this change. The value is unchanged.
!~          "client_secret"                 = (sensitive value)
            "oidc_issuer"                   = "https://accounts.google.com"
            "token_request_method"          = "****"
            "token_url"                     = "******************************************"
        }
        provider_name     = "Google"
        provider_type     = "Google"
        region            = "us-west-2"
        user_pool_id      = "*******************"
    }

  # module.home-unite-us.aws_cognito_user_group.homeuniteus_prod["Admins"] will be imported
    resource "aws_cognito_user_group" "homeuniteus_prod" {
        description  = "Managed by Terraform"
        id           = "us-west-2_VH24AGQ3p/Admins"
        name         = "Admins"
        precedence   = 0
        region       = "us-west-2"
        role_arn     = null
        user_pool_id = "*******************"
    }

  # module.home-unite-us.aws_cognito_user_group.homeuniteus_prod["Coordinators"] will be imported
    resource "aws_cognito_user_group" "homeuniteus_prod" {
        description  = "Managed by Terraform"
        id           = "us-west-2_VH24AGQ3p/Coordinators"
        name         = "Coordinators"
        precedence   = 0
        region       = "us-west-2"
        role_arn     = null
        user_pool_id = "*******************"
    }

  # module.home-unite-us.aws_cognito_user_group.homeuniteus_prod["Guests"] will be imported
    resource "aws_cognito_user_group" "homeuniteus_prod" {
        description  = "Managed by Terraform"
        id           = "us-west-2_VH24AGQ3p/Guests"
        name         = "Guests"
        precedence   = 0
        region       = "us-west-2"
        role_arn     = null
        user_pool_id = "*******************"
    }

  # module.home-unite-us.aws_cognito_user_group.homeuniteus_prod["Hosts"] will be imported
    resource "aws_cognito_user_group" "homeuniteus_prod" {
        description  = "Managed by Terraform"
        id           = "us-west-2_VH24AGQ3p/Hosts"
        name         = "Hosts"
        precedence   = 0
        region       = "us-west-2"
        role_arn     = null
        user_pool_id = "*******************"
    }

  # module.home-unite-us.aws_cognito_user_pool.homeuniteus_prod will be updated in-place
  # (imported from "us-west-2_VH24AGQ3p")
!~  resource "aws_cognito_user_pool" "homeuniteus_prod" {
        arn                        = "arn:aws:cognito-idp:us-west-2:035866691871:userpool/us-west-2_VH24AGQ3p"
        auto_verified_attributes   = [
            "email",
        ]
        creation_date              = "2024-10-08T09:26:17Z"
        custom_domain              = null
        deletion_protection        = "ACTIVE"
        domain                     = "homeuniteus"
        email_verification_message = null
        email_verification_subject = null
        endpoint                   = "cognito-idp.us-west-2.amazonaws.com/us-west-2_VH24AGQ3p"
        estimated_number_of_users  = 96
        id                         = "us-west-2_VH24AGQ3p"
        last_modified_date         = "2024-10-08T09:26:18Z"
        mfa_configuration          = "OPTIONAL"
        name                       = "Home Unite Us"
        region                     = "us-west-2"
        sms_authentication_message = null
        sms_verification_message   = null
        tags                       = {}
!~      tags_all                   = {
+           "managed-by" = "terraform-incubator"
        }
        user_pool_tier             = "LITE"
        username_attributes        = [
            "email",
            "phone_number",
        ]

        account_recovery_setting {
            recovery_mechanism {
                name     = "verified_email"
                priority = 1
            }
            recovery_mechanism {
                name     = "verified_phone_number"
                priority = 2
            }
        }

        admin_create_user_config {
            allow_admin_create_user_only = false
        }

        email_configuration {
            configuration_set      = null
            email_sending_account  = "COGNITO_DEFAULT"
            from_email_address     = null
            reply_to_email_address = null
            source_arn             = null
        }

        lambda_config {
            create_auth_challenge          = null
            custom_message                 = "arn:aws:lambda:us-west-2:035866691871:function:customMessage"
            define_auth_challenge          = null
            kms_key_id                     = null
            post_authentication            = null
            post_confirmation              = null
            pre_authentication             = null
            pre_sign_up                    = "arn:aws:lambda:us-west-2:035866691871:function:mergeUsers"
            pre_token_generation           = null
            user_migration                 = null
            verify_auth_challenge_response = null
        }

        password_policy {
            minimum_length                   = 8
            password_history_size            = 0
            require_lowercase                = true
            require_numbers                  = true
            require_symbols                  = true
            require_uppercase                = true
            temporary_password_validity_days = 7
        }

        schema {
            attribute_data_type      = "String"
            developer_only_attribute = false
            mutable                  = true
            name                     = "email"
            required                 = true

            string_attribute_constraints {
                max_length = "2048"
                min_length = "0"
            }
        }

        sign_in_policy {
            allowed_first_auth_factors = [
                "PASSWORD",
            ]
        }

        sms_configuration {
            external_id    = "************************************"
            sns_caller_arn = "arn:aws:iam::035866691871:role/service-role/homeuniteus-cognito-idp"
            sns_region     = "us-west-2"
        }

        user_attribute_update_settings {
            attributes_require_verification_before_update = [
                "email",
            ]
        }

        username_configuration {
            case_sensitive = false
        }

        verification_message_template {
            default_email_option  = "CONFIRM_WITH_CODE"
            email_message         = null
            email_message_by_link = null
            email_subject         = null
            email_subject_by_link = null
            sms_message           = null
        }
    }

  # module.home-unite-us.aws_cognito_user_pool_client.homeuniteus_prod will be imported
    resource "aws_cognito_user_pool_client" "homeuniteus_prod" {
        access_token_validity                         = 30
        allowed_oauth_flows                           = [
            "code",
        ]
        allowed_oauth_flows_user_pool_client          = true
        allowed_oauth_scopes                          = [
            "aws.cognito.signin.user.admin",
            "email",
            "openid",
            "phone",
            "profile",
        ]
        auth_session_validity                         = 3
        callback_urls                                 = [
            "http://localhost:4040/signin",
            "http://localhost:4040/signup",
            "http://localhost:4040/signup/coordinator",
            "http://localhost:4040/signup/host",
            "https://dev.homeunite.us/signin",
            "https://dev.homeunite.us/signup",
            "https://dev.homeunite.us/signup/coordinator",
            "https://dev.homeunite.us/signup/host",
            "https://qa.homeunite.us/signin",
            "https://qa.homeunite.us/signup",
            "https://qa.homeunite.us/signup/coordinator",
            "https://qa.homeunite.us/signup/host",
        ]
        client_secret                                 = (sensitive value)
        default_redirect_uri                          = null
        enable_propagate_additional_user_context_data = false
        enable_token_revocation                       = true
        explicit_auth_flows                           = [
            "ALLOW_ADMIN_USER_PASSWORD_AUTH",
            "ALLOW_CUSTOM_AUTH",
            "ALLOW_REFRESH_TOKEN_AUTH",
            "ALLOW_USER_PASSWORD_AUTH",
            "ALLOW_USER_SRP_AUTH",
        ]
        id                                            = "b76g3q852lb4us1rbo19he3uj"
        id_token_validity                             = 60
        logout_urls                                   = []
        name                                          = "homeuniteus"
        prevent_user_existence_errors                 = "ENABLED"
        read_attributes                               = [
            "address",
            "birthdate",
            "email",
            "email_verified",
            "family_name",
            "gender",
            "given_name",
            "locale",
            "middle_name",
            "name",
            "nickname",
            "phone_number",
            "phone_number_verified",
            "picture",
            "preferred_username",
            "profile",
            "updated_at",
            "website",
            "zoneinfo",
        ]
        refresh_token_validity                        = 30
        region                                        = "us-west-2"
        supported_identity_providers                  = [
            "COGNITO",
            "Google",
        ]
        user_pool_id                                  = "*******************"
        write_attributes                              = [
            "address",
            "birthdate",
            "email",
            "family_name",
            "gender",
            "given_name",
            "locale",
            "middle_name",
            "name",
            "nickname",
            "phone_number",
            "picture",
            "preferred_username",
            "profile",
            "updated_at",
            "website",
            "zoneinfo",
        ]

        token_validity_units {
            access_token  = "*******"
            id_token      = "*******"
            refresh_token = "****"
        }
    }

  # module.home-unite-us.aws_cognito_user_pool_domain.homeuniteus_prod will be imported
    resource "aws_cognito_user_pool_domain" "homeuniteus_prod" {
        aws_account_id                  = "************"
        certificate_arn                 = null
        cloudfront_distribution         = "dpp0gtxikpq3y.cloudfront.net"
        cloudfront_distribution_arn     = "dpp0gtxikpq3y.cloudfront.net"
        cloudfront_distribution_zone_id = "**************"
        domain                          = "homeuniteus"
        id                              = "homeuniteus"
        managed_login_version           = 1
        region                          = "us-west-2"
        s3_bucket                       = "aws-cognito-prod-pdx-assets"
        user_pool_id                    = "*******************"
        version                         = "20241010065051"
    }

  # module.home-unite-us.aws_iam_role.cognito_idp_prod will be updated in-place
  # (imported from "homeuniteus-cognito-idp")
!~  resource "aws_iam_role" "cognito_idp_prod" {
        arn                   = "arn:aws:iam::035866691871:role/service-role/homeuniteus-cognito-idp"
        assume_role_policy    = jsonencode(
            {
                Statement = [
                    {
                        Action    = "sts:AssumeRole"
                        Condition = {
                            StringEquals = {
                                "sts:ExternalId" = "f027bce1-d945-40d4-8b59-54e12015cdb7"
                            }
                        }
                        Effect    = "Allow"
                        Principal = {
                            Service = "cognito-idp.amazonaws.com"
                        }
                        Sid       = ""
                    },
                ]
                Version   = "2012-10-17"
            }
        )
        create_date           = "2024-10-08T09:25:55Z"
        description           = null
        force_detach_policies = false
        id                    = "homeuniteus-cognito-idp"
        managed_policy_arns   = []
        max_session_duration  = 3600
        name                  = "homeuniteus-cognito-idp"
        name_prefix           = null
        path                  = "/service-role/"
        permissions_boundary  = null
        tags                  = {}
!~      tags_all              = {
+           "managed-by" = "terraform-incubator"
        }
        unique_id             = "*********************"

        inline_policy {
            name   = "homeuniteus-cognito-idp"
            policy = jsonencode(
                {
                    Statement = [
                        {
                            Action   = [
                                "sns:publish",
                            ]
                            Effect   = "Allow"
                            Resource = [
                                "*",
                            ]
                        },
                    ]
                    Version   = "2012-10-17"
                }
            )
        }
    }

  # module.home-unite-us.aws_iam_role_policy.cognito_sns_prod will be imported
    resource "aws_iam_role_policy" "cognito_sns_prod" {
        id          = "homeuniteus-cognito-idp:homeuniteus-cognito-idp"
        name        = "homeuniteus-cognito-idp"
        name_prefix = null
        policy      = jsonencode(
            {
                Statement = [
                    {
                        Action   = [
                            "sns:publish",
                        ]
                        Effect   = "Allow"
                        Resource = [
                            "*",
                        ]
                    },
                ]
                Version   = "2012-10-17"
            }
        )
        role        = "homeuniteus-cognito-idp"
    }

  # module.home-unite-us.aws_secretsmanager_secret.cognito_client_prod will be updated in-place
  # (imported from "arn:aws:secretsmanager:us-west-2:035866691871:secret:homeuniteus-cognito-client-EEaiW4")
!~  resource "aws_secretsmanager_secret" "cognito_client_prod" {
        arn                            = "arn:aws:secretsmanager:us-west-2:035866691871:secret:homeuniteus-cognito-client-EEaiW4"
        description                    = null
+       force_overwrite_replica_secret = false
        id                             = "arn:aws:secretsmanager:us-west-2:035866691871:secret:homeuniteus-cognito-client-EEaiW4"
        kms_key_id                     = null
        name                           = "homeuniteus-cognito-client"
        name_prefix                    = null
        policy                         = jsonencode(
            {
                Statement = [
                    {
                        Action    = "secretsmanager:GetSecretValue"
                        Effect    = "Allow"
                        Principal = {
                            AWS = "arn:aws:iam::035866691871:user/tyler.thome"
                        }
                        Resource  = "*"
                        Sid       = "EnableAnotherAWSAccountToReadTheSecret"
                    },
                ]
                Version   = "2012-10-17"
            }
        )
+       recovery_window_in_days        = 30
        region                         = "us-west-2"
        tags                           = {}
!~      tags_all                       = {
+           "managed-by" = "terraform-incubator"
        }
        type                           = null
    }

Plan: 11 to import, 0 to add, 4 to change, 0 to destroy.

✅ Plan applied in Terraform apply (OIDC) #68

@ale210
ale210 merged commit 373d9e6 into main Aug 30, 2026
2 checks passed
ale210 added a commit that referenced this pull request Aug 30, 2026
Second of three PRs for #166, following #186 which
imported the production Cognito pool.

## What this does

- Adds `environment-prod.tf`, standing up the production stack through
`modules/container` with the correct naming
(`home-unite-us-fullstack-prod`).
- Adopts the existing `homeuniteus` ECR repository as a plain
`aws_ecr_repository` resource in `ecr.tf`, with an import block. It is
not routed through `modules/ecr` because that module derives the
repository name from the project name, and this repository's name is
referenced by the running task definition and by the project's own CI.

## Nothing here takes production traffic

The new listener rule is at **priority 17**; the live unmanaged rule is
at **16**, and the load balancer evaluates the lower number first. So
this stack comes up alongside production, serving nothing, and can be
verified before anything moves. Removing rule 16 is the cutover and
belongs to the next PR, where the rollback is simply putting it back.

## Choices worth reviewing

**`launch_type = "ec2"`**, not Fargate. The unmanaged service runs
Fargate; the module's own guidance is to use EC2 unless there is a
reason not to. The cluster has room — two container instances with
roughly 6 GB free each, against a 512 MB task.

**The image is the tag production already serves**,
`2dc864ae50.20250220-172003`, pulled from the adopted repository. It is
date-and-SHA stamped rather than a moving tag, so it cannot drift the
way the mutable `:dev`/`:qa` tags elsewhere in this account have.
Building a fresh production image is being handled separately with the
HomeUniteUs team.

**Every environment variable is declared explicitly.** The image carries
a baked-in `.env` with the same keys, and environment variables take
precedence over it — declaring the full set is what ends production's
dependence on a file that exists only inside a container. These point at
the production Cognito pool from #186, not the QA pool.

Two of those values look wrong and are not:

- `HUU_ENVIRONMENT = "qa"`. The image is built with `HUU_TARGET_ENV=qa`,
which bakes `/var/www/qa.homeunite.us` and `/var/log/qa.homeunite.us`
into it. It has to stay `qa` while this tag is in use.
- `hostname = "qa.homeunite.us"` with `www.homeunite.us` as an
additional host. The frontend bundle is compiled with
`VITE_HUU_API_BASE_URL=https://qa.homeunite.us/api`, and the backend's
`ROOT_URL` has to agree or the OAuth redirect breaks. This mirrors the
live rule at priority 16, which already serves both names. Moving the
application onto `www` properly needs a rebuilt image.

**`COGNITO_ACCESS_ID` and `COGNITO_ACCESS_KEY` are set to empty
strings** so boto3 falls back to the task role, rather than the static
access key of the `homeuniteus-app` IAM user that the baked `.env`
supplies. Retiring that user and its key is part of the cleanup PR.

**`DATABASE_URL`** is assembled into an SSM parameter, matching how QA
passes it. The production database already exists inside the shared
instance and is not created here; its password is read from the
`homeuniteus-rds-password` secret left behind by the Terragrunt stack.

## What to check in the plan

One import (the ECR repository, which should be tag-only changes), and
creates for the new service, task definition, target group, listener
rule, log group, security group, task role and policy, and the SSM
parameter. **No existing resource should be modified or destroyed**
beyond the ECR tag.

## Verification

- `terraform validate` passes on 1.16.0. `terraform fmt` is clean on the
added blocks; the pre-existing `ecr_fullstack` block is left unformatted
rather than mixing unrelated churn into this PR.
- **After this merges**, confirm the new service reaches a steady 1/1
and its target group shows a healthy target, and that `www.homeunite.us`
and `qa.homeunite.us` are still served by the *old* stack — nothing
should change for users in this PR.

## Not in this PR

The cutover, and deleting the legacy service, task definition family,
target group, security group, log group, `homeuniteus-app` IAM user and
key, the two IAM policies, and the four redundant secrets.

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
ale210 added a commit that referenced this pull request Aug 30, 2026
…hecks (#188)

Third of four PRs for #166, after #186 (Cognito
import) and #187 (production service). It corrects how the new service
is configured. It still takes no traffic — the cutover is the next PR.

## Production does not use RDS, and never has

#187 set `DATABASE_URL` from the `homeuniteus-rds-password` secret,
following the abandoned Terragrunt state, which describes a Postgres
database on the shared instance. The service came up and its API died
immediately:

```
FATAL: password authentication failed for user "homeuniteus"
ERROR: Application startup failed. Exiting.
```

The credential is not stale — the premise was wrong. The live production
container's `.env` points `DATABASE_URL` at **`/opt/huu/homeuniteus.db`,
a SQLite file on the container's own filesystem**. Nothing has connected
to that Postgres database since January 2025, which is why the password
no longer matches anything.

This PR sets `DATABASE_URL` to the same SQLite path so the new service
matches production, and removes the Postgres wiring: the
`homeuniteus-rds-password` data source, the `aws_db_instance` lookup,
and the `db_url_prod` SSM parameter, which was created holding a value
that never worked.

## What that means, recorded here deliberately

The production task definition declares no volume and no mount point. So
that SQLite file is created empty when a task starts and is destroyed
when the task is replaced — by a deploy, a host drain, or an AZ
rebalance. **The log group holds 743 task generations.** Application
data has never survived any of them.

It goes unnoticed because Cognito holds the user accounts and Cognito is
durable — 96 of them. The site loads, sign-in works, and only the
application's own tables reset. It fails in the one way that looks fine
from outside.

The current file was inspected before writing this: 53,248 bytes,
containing nothing beyond what the application recreates for itself on
startup via `create_all` and the `after_create` role seed. So there is
nothing to migrate, and nothing was lost in arriving at this PR.

**This PR reproduces that behaviour rather than fixing it**, so that the
cutover changes nothing about how the application runs. Making
production durable is a real change to a live service and belongs in its
own piece of work, not inside a migration. The comment in
`environment-prod.tf` says so at the point where the next reader will
meet it.

## Health checks now test the API

The target group checked `/`, which nginx serves whether or not the API
is alive — the container runs nginx in the foreground and the API as a
background process, so the API dying does not even stop the task. When
#187's service came up with a dead API, **the target group reported
healthy the entire time**.

It now checks `/api/health/`, which is served by the API process and
returns `"UP"`. Had this been in place, #187 would have failed visibly
instead of looking green.

## `LOG_LEVEL` restored to `DEBUG`

#187 set `INFO`; production runs `DEBUG`. Reverted to match, keeping
this migration behaviour-preserving. Whether `DEBUG` is appropriate for
production is a separate question and not settled here.

## What to check in the plan

A new task definition revision and a service update to use it, an
in-place update to the target group's health check, and the destruction
of the unused `db_url_prod` SSM parameter and its `random_password`.
Nothing else should change, and no other resource should be replaced.

## Verification

- `terraform validate` and `terraform fmt` pass on 1.16.0.
- **After this merges**, confirm from the task's CloudWatch logs that
the API reaches startup without the SQLAlchemy error — not from the
target group, which is what misled us last time. With the new health
check path a failed start should also surface as an unhealthy target.
- Production is unaffected either way: rule 16 still routes all traffic
to the old service.

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant