# Deployment How to stand up a new app in [`infra`](https://github.com/FSHTech/infra). ## Demo (preview) For a demo app the whole platform already exists in the `development` account, so onboarding is straightforward. ### 1. Add the repo to `repos.hcl` ```hcl # infra/repos.hcl demo_repos = [ "demo-example", "demo-newthing", # <- the list entry IS the GitHub repo name ] ``` That single line does three things via existing fan-out units: - **creates the GitHub repo** (`demo_repos` → `catalog/github/demo-repos`, open-push, no branch protection), - **makes it a preview app** (it joins the `preview_repos` union → Cloudflare Access app + OIDC trust), - **sets its CI vars** (`PREVIEW_AWS_ROLE_ARN` via `preview_repo_vars`, `CODEARTIFACT_*` via `codeartifact_repo_vars`). ### 2. Merge Make an `infra` PR, get a review, merge. On merge, everything will automatically happen. ### 3. Commit two files to the app repo `preview.toml` at the repo root: ```toml [preview] app = "demo-newthing" # DNS slug + ECR image-tag prefix worker_entrypoint = "tracker.queue.main:main" # the pgq worker's module:function db_name = "tracker" # Postgres DB name base_branches = ["main"] # branches that get a persistent trunk ``` `.github/workflows/preview.yml` — copy `demo-example`'s verbatim. --- ## Production A production app has a fully highly available stack. ### 1. Add the repo to `repos.hcl` ```hcl # infra/repos.hcl prod_repos = [ "example", "sales-pipeline", "new-locality", # <- the live/internal/ name; its env.hcl supplies app_name ] ``` ### 2. Vend the AWS account (`live/management`) ```hcl # infra/live/management/terragrunt.stack.hcl unit "account_new_locality" { source = "${get_repo_root()}/catalog/aws/account" path = "account-new-locality" values = { name = "new-locality" ou = "internal" # or "customers" for a customer account client = true } } ``` ### 3. Create the environment config (`live/internal/new-locality/`) This is "the specific environment configuration." **Two files, and the only one you edit is `env.hcl`.** `env.hcl` — the three knobs at the top are all you set; the rest are org defaults plus the few keys other files read. Purely-derived values (`github_repo`, `customer`) live in the shared `terragrunt.stack.hcl`, not here: ```hcl # infra/live/internal/new-locality/env.hcl locals { # ── Per-app knobs: the only lines you change for a new app ─────────────────── environment = "new-locality" # dir name = AWS account = customer label app_name = "new-locality" # GitHub repo under FSHTech/, ECR/ECS name prefix worker_entrypoint = "app.queue.main:main" # the pgq worker's module:function # ── Org defaults / contract keys other files read; leave as-is ─────────────── aws_region = "us-east-2" secondary_regions = ["us-west-2"] deploy_role = "OrganizationAccountAccessRole" github_owner = "FSHTech" customer_zone = "${local.environment}.app.efesaitch.com" tags = { Environment = local.environment ManagedBy = "terragrunt" } } ``` ```bash cp live/internal/example/terragrunt.stack.hcl live/internal/new-locality/terragrunt.stack.hcl ``` That will give you a starting point; `example` has a full production deployment. ### 4. Delegate the DNS subzone (`live/domain`) Gives the app's own Route 53 zone (`new-locality.app.efesaitch.com`) authority, so its ACM certs can validate. The unit name must be `-subzone-delegation` — that's the exact path `env.hcl`'s derived `acm_delegation_path` points at. ```hcl # infra/live/domain/terragrunt.stack.hcl unit "new_locality_subzone_delegation" { source = "${get_repo_root()}/catalog/aws/route53-delegation" path = "new-locality-subzone-delegation" values = { aws_region = local.env.aws_region j parent_zone_path = "../route53-zone" subzone_name = "new-locality.${local.env.delegated_zone}" child_zone_path = "${get_repo_root()}/live/internal/new-locality/.terragrunt-stack/shared/.terragrunt-stack/route53-zone" } } ``` ### 5. Register the repo + web vars (`live/source-code`) Two units: the GitHub repo itself, and the FE (S3/CloudFront) deploy vars. The `web_vars` unit reuses the `prod_web_vars[""]` map that's derived automatically from `prod_repos` + `env.hcl`, so you just reference it by name. ```hcl # infra/live/source-code/terragrunt.stack.hcl unit "new_locality" { source = "${get_repo_root()}/catalog/github/repo" path = "new-locality" values = { repo_name = "new-locality" description = "New locality app" teams = local.repo_teams push_allowed_teams = local.push_allowed_teams code_owner_teams = local.code_owner_teams merge_bypass_teams = ["engineers-fte"] depends_on = local.team_units } } unit "new_locality_web_vars" { source = "${get_repo_root()}/catalog/github/web-deploy-vars" path = "new-locality-web-vars" values = local.prod_web_vars["new-locality"] } ``` The `CODEARTIFACT_*` and (if you also want previews) `PREVIEW_AWS_ROLE_ARN` repo vars are already handled — they fan out from the derived `codeartifact_consumer_repos` / `preview_repos` sets. The BE `deploy-role` + `deploy-repo-vars` are part of the per-app stack you copied in step 3. ### 6. Grant human access (`roster.hcl`) Add the new env to each role's `aws_access` map that should reach it: ```hcl # infra/roster.hcl (inside locals.roles) engineers-fte = { aws_access = { ..., "new-locality" = "Admin" } } operations-fte = { aws_access = { ..., "new-locality" = "AppMaintainStaging" } } ``` ### 7. Merge Make an `infra` PR, get a review, merge. On merge, everything will automatically happen.