# Terraform deployment infrastructure The root module remains the isolated `tf-poc` Elastic Beanstalk and SQL Server stack in `seahaven-external-dev` (`396287094661`). It must never be reused for dev or staging state. Live adoption is deliberately smaller. [`live/`](live/) imports only the existing GitHub Actions deploy roles and inline policies. All application, environment, database, certificate, DNS, network, runtime IAM, and secret resources remain external and are read only as inventory. The Terraform roots are: - `./`: isolated POC workload in `shoc-backend-tf-poc`. - `bootstrap/`: consolidated POC/dev/staging HCP role bootstrap in `shoc-backend-bootstrap`. - `live/dev/`: import-only dev deploy-role ownership in `shoc-backend-dev`. - `live/staging/`: import-only staging deploy-role ownership in `shoc-backend-staging`. IAM lives in this repo, not org-baseline. App CD never runs a bootstrap root. Auto-apply stays off for every workspace. The stabilized POC bootstrap deliberately leaves both POC HCP roles read only and removes `ViewOnlyAccess` plus the broad workload-mutation inline policy. The POC GitHub deploy role remains unchanged until a separately approved narrowing or teardown. Future POC teardown runs directly under the approved SSO administrator session, not the locked HCP apply role. ## Locked names - Hostname: `tf-poc.api.dev.seahaven.com` (zone `Z07671212N75U4YLPWZR8`) - EB application / environment: `shoc-backend-tf-poc` - RDS identifier: `shoc-backend-tf-poc` - Catalog: `shoc_tf_poc` (create this database once after RDS is available) - Deploy role: `arn:aws:iam::396287094661:role/tf-managed/githubdeploy-shoc-backend-tf-poc` - GitHub Environment: `tf-poc` (OIDC `environment:tf-poc`) - HCP org `seahaven`, project `seahaven-external-dev` ## HCP layout All SHOC backend environments live in AWS account `396287094661` and HCP The POC workspaces remain isolated until teardown. The live bootstrap owns `hcptf-shoc-backend-{dev,staging}` and matching `-plan` roles. The live environment workspaces own only their existing GitHub deploy role and inline policy. See [`live/README.md`](live/README.md). ## Console setup (once) 1. In HCP Terraform, create project `seahaven-external-dev` if it does not exist. 2. Create workspace `shoc-backend-bootstrap`: - VCS later, or CLI-driven until the branch is pushed - Working directory: `terraform/bootstrap` - Execution mode: **Local** - Auto-apply: off 3. Create workspace `shoc-backend-tf-poc`: - Same branch - Working directory: `terraform` - Execution mode: **Remote** - Auto-apply: off - Speculative plans: on 4. Do **not** use HCP "Quick setup AWS dynamic credentials". ## Discovery (before first workload apply) ```bash export AWS_PROFILE=seahaven-external-dev aws elasticbeanstalk describe-environments \ --environment-names shoc-backend-dev \ --region us-east-1 \ --query 'Environments[0].{Vpc:EndpointURL}' aws elasticbeanstalk describe-configuration-settings \ --application-name shoc-backend \ --environment-name shoc-backend-dev \ --region us-east-1 \ --query "ConfigurationSettings[0].OptionSettings[?Namespace=='aws:ec2:vpc']" ``` Copy `vpc-REPLACE_ME` and subnet lists into a local `terraform/terraform.tfvars` (gitignored). Confirm: - `app.terraform.io` OIDC exists (`create_tfc_oidc_provider=false` in bootstrap). If the data source fails, set `create_tfc_oidc_provider=true`. - `external-dev-execution-boundary` exists. - `aws-elasticbeanstalk-service-role` exists. - GitHub OIDC provider `token.actions.githubusercontent.com` exists. ## Bootstrap apply (Adam) SSO AdministratorAccess in this account is subject to the external-dev SCP, so both `hcptf-*` roles set `permissions_boundary` to `external-dev-execution-boundary`. ```bash export AWS_PROFILE=seahaven-external-dev cd terraform/bootstrap terraform login terraform init terraform apply ``` Then on workspace `shoc-backend-tf-poc`, set workspace-scoped env vars: - `TFC_AWS_PROVIDER_AUTH=true` - `TFC_AWS_PLAN_ROLE_ARN` = output `hcp_plan_role_arn` - `TFC_AWS_APPLY_ROLE_ARN` = output `hcp_apply_role_arn` Never put those in a project variable set. Set `sendgrid_api_key` as a sensitive Terraform variable on that workspace when you want mail to work. ## Workload apply HCP Manual apply on `shoc-backend-tf-poc`. Confirm `api.dev.seahaven.com` still serves live before and after. After RDS is available, create the catalog once (SQL Server Express does not accept `db_name` on `aws_db_instance`): ```sql CREATE DATABASE [shoc_tf_poc]; ``` Then first app deploy can run migrations into that catalog. ## App deploy GitHub Actions is the real CD path. `.github/workflows/deploy.yml` maps: - `dev` → `dev` - `staging` → `staging` - `main` → `prod` The live roots import the roles already referenced by the `dev` and `staging` GitHub Environment secrets. The role ARNs do not change during adoption. The POC local fallback remains available until teardown: ```bash export AWS_PROFILE=seahaven-external-dev bash scripts/deploy-api-tf.sh ``` The script refuses live names (`shoc-backend-dev`, `api.dev.seahaven.com`). ## After POC confirmation 1. Create `shoc-backend-dev` and `shoc-backend-staging` workspaces. Do not reuse POC state. 2. Apply `bootstrap/` only after approval to create the four narrowly scoped live HCP roles. 3. Follow the no-op import and controlled-update sequence in [`live/README.md`](live/README.md). 4. Retire CDK deploy-role ownership only after both role imports are proven. 5. Destroy the POC workload last. Bootstrap state remains. Do not apply this module as `environment=dev` without imports. ## Local CI equivalent ```bash terraform -chdir=terraform fmt -check -recursive terraform -chdir=terraform init -backend=false && terraform -chdir=terraform validate terraform -chdir=terraform/bootstrap init -backend=false && terraform -chdir=terraform/bootstrap validate terraform -chdir=terraform/live/dev init -backend=false && terraform -chdir=terraform/live/dev validate terraform -chdir=terraform/live/staging init -backend=false && terraform -chdir=terraform/live/staging validate ```