Deployment¶
How to stand up a new app in 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¶
# 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_reposunion → Cloudflare Access app + OIDC trust),sets its CI vars (
PREVIEW_AWS_ROLE_ARNviapreview_repo_vars,CODEARTIFACT_*viacodeartifact_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:
[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¶
# infra/repos.hcl
prod_repos = [
"example",
"sales-pipeline",
"new-locality", # <- the live/internal/<dir> name; its env.hcl supplies app_name
]
2. Vend the AWS account (live/management)¶
# 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:
# 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"
}
}
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 <environment>-subzone-delegation
— that’s the exact path env.hcl’s derived acm_delegation_path points at.
# 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["<dir>"] map that’s derived
automatically from prod_repos + env.hcl, so you just reference it by name.
# 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:
# 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.