shoc-frontend-new/infra/cdk/README.md

136 lines
5.6 KiB
Markdown

# Frontend infrastructure and migration rehearsal
This CDK app describes the existing Sea Haven SHOC SPA hosting and an isolated,
production-shaped Terraform adoption rehearsal. It performs no content upload.
Content-only deployment is handled by `scripts/deploy-web.sh`.
## Existing environments
Normal synthesis remains unchanged when the adoption flag is off:
- private, versioned S3 bucket with CDK auto-delete cleanup
- CloudFront distribution and origin access control
- viewer-request function that rewrites extensionless SPA routes
- optional Route 53 A and AAAA aliases
- GitHub Actions OIDC deploy role
Dev remains the default context in `cdk.json`. Staging uses explicit context
arguments. During migration, both content workflows are manual-only and use
fixed environment configuration rather than discovering deployment targets
from CloudFormation.
## Terraform POC
The POC is isolated in account `396287094661`, region `us-east-1`, and is
created only with `-c tfPoc=true` plus an explicit `tfPocPhase`. It uses three
ownership scopes:
1. `shoc-frontend-tf-poc-shared`: a dedicated public hosted zone for
`frontend-tf-poc.seahaven.com`.
2. `shoc-frontend-tf-poc-certificate`: the DNS-validated ACM certificate.
3. `shoc-frontend-tf-poc`: the private versioned bucket, CloudFront OAC,
distribution, SPA function, A/AAAA aliases, and GitHub OIDC content deploy
role.
Fixed application values:
- bucket: `seahaven-shoc-frontend-tf-poc`
- role: `githubdeploy-shoc-frontend-new-tf-poc`
- GitHub environment: `tf-poc`
- site: `https://frontend-tf-poc.seahaven.com`
- API: `https://api.tf-poc.seahaven.com/api`
The shared stack is intentionally staged. The zone must exist and be delegated
before ACM can validate a certificate inside it:
```bash
cd infra/cdk
npm ci
npm test
npm run synth:tf-poc-zone
npm run synth:tf-poc-environment
```
After approval, deploy only `shoc-frontend-tf-poc-shared` with
`tfPocPhase=zone`. Its outputs provide the child name servers. Create the
parent NS record as a separate approved change and verify public delegation.
Only then use `tfPocPhase=environment` to deploy the separate certificate and
site stacks. The zone stack never contains the certificate, so re-running the
zone phase cannot remove a certificate created by the environment phase.
Omitting `tfPocPhase` fails closed.
The stacks output the hosted zone ID, certificate ARN, delegation evidence,
workspace tag, boundary ARN, and import IDs for the bucket, bucket policy,
distribution, OAC, SPA function, A/AAAA records, deploy role, and inline role
policy. They also emit the generated OAC name/description, deterministic origin
ID, and inline policy name required by the tf-poc Terraform configuration.
## Adoption retention
`-c retainForTerraformAdoption=true` is deliberately opt-in. Keep it enabled
from the reviewed retention deployment through CloudFormation ownership
detachment.
The emitted template applies both `DeletionPolicy: Retain` and
`UpdateReplacePolicy: Retain` to:
- site bucket and bucket policy
- distribution, OAC, and SPA rewrite function
- A and AAAA records
- GitHub deploy role and its inline policy
- `SiteBucket/AutoDeleteObjectsCustomResource`
The bucket remains configured with `autoDeleteObjects: true`. The emitted
bucket and its matching custom resource are both retained, so deleting the
stack cannot invoke that custom resource to empty the versioned bucket.
Generated provider Lambda resources, provider IAM resources, provider logs,
and CDK metadata are intentionally excluded. Template tests enforce this exact
boundary.
In adoption mode, the deploy role also receives:
- tag `HcpTerraformWorkspace=shoc-frontend-new-{env}`
- permissions boundary
`arn:aws:iam::<account>:policy/shoc-frontend-new-{env}-deploy-boundary`
- exact `StringEquals` OIDC subject matching; for dev this narrows the current
no-wildcard `StringLike` subject before Terraform import
These changes are absent when the flag is off.
## Content deployment safeguards
`scripts/deploy-web.sh` requires explicit target and expectation variables:
- `SITE_BUCKET` and matching `EXPECTED_SITE_BUCKET`
- `CLOUDFRONT_DISTRIBUTION_ID`
- `SITE_URL`
- `VITE_API_URL` and matching `EXPECTED_API_URL`
- `DEPLOY_RELEASE_ID` or `GITHUB_SHA`
- optional comma-separated `FORBIDDEN_API_URLS`
- optional `API_SMOKE_URL` and `API_CORS_ORIGIN`
The script verifies bucket versioning, builds the app, publishes immutable
assets and a no-cache index, records a release manifest, invalidates and waits,
then checks `/`, `/login`, an extensionless route, asset references, API URLs,
and cache headers. Optional API preflight checks verify CORS.
If verification fails after publishing the index, the previous index version
is restored and invalidated. Pruning starts only after successful remote
verification. Current versions needed by the latest two release manifests are
kept; unreferenced object versions are deleted.
## Manual workflows
- `.github/workflows/deploy.yml`: dev content deployment
- `.github/workflows/deploy-staging.yml`: staging content deployment
- `.github/workflows/deploy-tf-poc.yml`: isolated POC content deployment
All are `workflow_dispatch` only. Staging and tf-poc retain their GitHub
environment protection and exact environment-scoped OIDC trust. Each dev and
staging distribution ID is pinned in its workflow. The tf-poc environment
must define its generated `CLOUDFRONT_DISTRIBUTION_ID` as a protected variable.
Each workflow pins its environment's Swagger URL for API CORS/preflight checks.
Infrastructure creation, parent-zone delegation, Terraform imports, and
ownership detachment remain separate administrator actions. None of these
workflows performs them.