mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-04 17:12:06 +00:00
367 lines
16 KiB
Markdown
367 lines
16 KiB
Markdown
# Frontend Terraform adoption runbook
|
|
|
|
This tree adopts the existing Sea Haven SHOC frontend hosting resources without
|
|
recreating them. It implements the local configuration and plan-safety tooling
|
|
only. Creating these files, formatting them, initializing with
|
|
`-backend=false`, and validating them does not authorize an AWS, HCP Terraform,
|
|
GitHub, CloudFormation, DNS, or deployment mutation.
|
|
|
|
The rollout order is `tf-poc`, dev, then staging. Production and tf-poc teardown
|
|
are separate follow-up changes.
|
|
|
|
## Fixed targets
|
|
|
|
- AWS account: `396287094661`
|
|
- AWS region: `us-east-1`
|
|
- HCP organization: `seahaven`
|
|
- HCP project: `seahaven-external-dev`
|
|
- Workspaces:
|
|
- `shoc-frontend-new-tf-poc`
|
|
- `shoc-frontend-new-dev`
|
|
- `shoc-frontend-new-staging`
|
|
- HCP auto-apply: off for all three workspaces
|
|
- tf-poc site: `frontend-tf-poc.seahaven.com`
|
|
- tf-poc API build value: `https://api.tf-poc.seahaven.com/api`
|
|
- tf-poc bucket: `seahaven-shoc-frontend-tf-poc`
|
|
- tf-poc deploy role: `githubdeploy-shoc-frontend-new-tf-poc`
|
|
- tf-poc GitHub environment: `tf-poc`
|
|
|
|
The `cloud` blocks identify the organization, project, and workspace. Auto-apply
|
|
is an HCP workspace setting and must be verified operationally before connecting
|
|
VCS or starting a run.
|
|
|
|
## Ownership boundary
|
|
|
|
`live/modules/environment-owned` owns exactly these 13 addresses:
|
|
|
|
1. `module.environment_owned.aws_s3_bucket.site`
|
|
2. `module.environment_owned.aws_s3_bucket_public_access_block.site`
|
|
3. `module.environment_owned.aws_s3_bucket_ownership_controls.site`
|
|
4. `module.environment_owned.aws_s3_bucket_server_side_encryption_configuration.site`
|
|
5. `module.environment_owned.aws_s3_bucket_versioning.site`
|
|
6. `module.environment_owned.aws_s3_bucket_policy.site`
|
|
7. `module.environment_owned.aws_cloudfront_distribution.site`
|
|
8. `module.environment_owned.aws_cloudfront_origin_access_control.site`
|
|
9. `module.environment_owned.aws_cloudfront_function.spa_rewrite`
|
|
10. `module.environment_owned.aws_route53_record.site_a`
|
|
11. `module.environment_owned.aws_route53_record.site_aaaa`
|
|
12. `module.environment_owned.aws_iam_role.github_deploy`
|
|
13. `module.environment_owned.aws_iam_role_policy.github_deploy`
|
|
|
|
Every managed resource has `prevent_destroy = true`.
|
|
|
|
`live/modules/environment-inventory` is data-only. It resolves and checks the
|
|
caller account, provider region, public hosted zone, ACM certificate, account
|
|
GitHub OIDC provider, and AWS managed `Managed-CachingOptimized` CloudFront
|
|
cache policy.
|
|
|
|
The following remain outside state:
|
|
|
|
- public hosted zones and ACM certificates
|
|
- the account-global GitHub OIDC provider
|
|
- the AWS managed CloudFront cache policy
|
|
- `CDKToolkit` resources and CDK metadata
|
|
- S3 auto-delete custom resources, provider Lambda, provider role, and log group
|
|
- hosted-zone and ACM validation internals
|
|
- CloudFront service-generated resources
|
|
|
|
## Exact live inventory
|
|
|
|
### Dev
|
|
|
|
- Bucket and all bucket subresources:
|
|
`seahaven-shoc-frontend-dev`
|
|
- Distribution: `E2CWLM1AFB964P`
|
|
- OAC: `E30VSIK87N8H64`
|
|
- OAC name:
|
|
`shocfrontenddevDistributionOrigin1S3OriginAccessControlDFC82620`
|
|
- OAC description: the API empty value, modeled as `""`
|
|
- Distribution origin ID:
|
|
`shocfrontenddevDistributionOrigin10CCD0EE1`
|
|
- Function: `us-east-1shocfrontenddevSpaRewrite58674DB8`
|
|
- A import ID: `Z07671212N75U4YLPWZR8_dev.seahaven.com_A`
|
|
- AAAA import ID: `Z07671212N75U4YLPWZR8_dev.seahaven.com_AAAA`
|
|
- Deploy role: `githubdeploy-shoc-frontend-new-dev`
|
|
- Inline policy import ID:
|
|
`githubdeploy-shoc-frontend-new-dev:GithubDeployRoleDefaultPolicyE8F540D1`
|
|
- Hosted zone: `Z07671212N75U4YLPWZR8`
|
|
- Stack: `shoc-frontend-dev`
|
|
|
|
### Staging
|
|
|
|
- Bucket and all bucket subresources:
|
|
`seahaven-shoc-frontend-staging`
|
|
- Distribution: `E2JDVEZ6EGD49J`
|
|
- OAC: `E1PF5R6QQNBZAI`
|
|
- OAC name:
|
|
`shocfrontendstagingDistributOrigin1S3OriginAccessControl82B1C17D`
|
|
- OAC description: the API empty value, modeled as `""`
|
|
- Distribution origin ID:
|
|
`shocfrontendstagingDistributionOrigin16E4628FC`
|
|
- Function: `us-east-1shocfrontendstagingSpaRewriteE9C0CBDA`
|
|
- A import ID:
|
|
`Z02602739VQWBWCAGXP4_staging.seahaven.com_A`
|
|
- AAAA import ID:
|
|
`Z02602739VQWBWCAGXP4_staging.seahaven.com_AAAA`
|
|
- Deploy role: `githubdeploy-shoc-frontend-new-staging`
|
|
- Inline policy import ID:
|
|
`githubdeploy-shoc-frontend-new-staging:GithubDeployRoleDefaultPolicyE8F540D1`
|
|
- Hosted zone: `Z02602739VQWBWCAGXP4`
|
|
- Stack: `shoc-frontend-staging`
|
|
|
|
Both live roots inventory the shared certificate:
|
|
|
|
`arn:aws:acm:us-east-1:396287094661:certificate/2b78e74f-7b65-4b82-a413-7a498b102f00`
|
|
|
|
The live roots preserve the observed pre-adoption configuration:
|
|
|
|
- `adoption_complete = false`
|
|
- `Environment`, `ManagedBy=cdk`, and `Project=shoc-frontend` tags
|
|
- the S3-only `aws-cdk:auto-delete-objects=true` tag
|
|
- the deploy-role-only
|
|
`HcpTerraformWorkspace=shoc-frontend-new-<environment>` manager tag
|
|
- current OAC names, empty descriptions, origin IDs, comments, protocols, cache
|
|
policy, certificate, trust subjects, role descriptions, and legacy deploy
|
|
policy
|
|
- the exact legacy bucket-policy grant for the S3 auto-delete helper
|
|
- the mandatory deterministic permissions boundary
|
|
`arn:aws:iam::396287094661:policy/shoc-frontend-new-<environment>-deploy-boundary`
|
|
|
|
The approved legacy-owner prerequisite narrows the dev role's exact subject
|
|
from `StringLike` to `StringEquals` before import. All roots therefore use
|
|
`StringEquals` in both modes. The HCP workspace manager tag remains on the role
|
|
in both modes and is never added to S3 or CloudFront resources.
|
|
|
|
If a prerequisite changes any live metadata before import, update the matching
|
|
root to the newly observed exact value and prove a zero-change import plan. Do
|
|
not approve that drift through the controlled-update checker.
|
|
|
|
## Prerequisites
|
|
|
|
Before any remote plan:
|
|
|
|
1. Confirm the deployment workflow for the target environment is paused while
|
|
PR validation remains active.
|
|
2. Confirm no CloudFormation/CDK update or content deployment can race the
|
|
adoption.
|
|
3. Confirm the HCP workspace is in the `seahaven-external-dev` project with
|
|
auto-apply off.
|
|
4. Confirm the org-baseline plan/apply roles and deploy-role permissions
|
|
boundary exist with exact workspace trust.
|
|
5. Attach the root's deterministic boundary through the approved legacy-owner
|
|
procedure. It is mandatory before the import plan.
|
|
6. Verify account `396287094661`, region `us-east-1`, all import IDs, current
|
|
tags, the dev `StringEquals` trust prerequisite, role description, boundary,
|
|
policies, distribution configuration, OAC configuration, function code, DNS
|
|
targets, and bucket settings using read-only queries.
|
|
7. Confirm the creator stack has retention semantics for all 13 transferred
|
|
resources and the S3 auto-delete custom resource. A synthesized template and
|
|
reviewed change set are required before mutation.
|
|
8. Capture a complete object-version inventory and smoke-test baseline.
|
|
|
|
Do not remove or replace the S3 auto-delete custom resource casually. Deleting
|
|
it while its handler is active can empty the versioned bucket. Retain it during
|
|
ownership transfer and prove the tf-poc path before touching dev.
|
|
|
|
## HCP variables
|
|
|
|
Configure dynamic AWS credentials in each workspace. Use the exact
|
|
org-baseline role ARNs for that workspace:
|
|
|
|
- environment variable `TFC_AWS_PROVIDER_AUTH=true`
|
|
- environment variable `TFC_AWS_PLAN_ROLE_ARN`
|
|
- environment variable `TFC_AWS_APPLY_ROLE_ARN`
|
|
- Terraform variable `adoption_complete=false`
|
|
|
|
Do not store AWS access keys. Mark sensitive values sensitive even when they are
|
|
not credentials. VCS working directories are:
|
|
|
|
- `terraform/live/tf-poc`
|
|
- `terraform/live/dev`
|
|
- `terraform/live/staging`
|
|
|
|
tf-poc also requires every variable in `terraform.tfvars.example`. Populate
|
|
them only from creator outputs and read-only verification. The check in the
|
|
tf-poc root blocks planning while a value is empty or starts with
|
|
`REPLACE_WITH_`.
|
|
|
|
## Local validation
|
|
|
|
From the repository root:
|
|
|
|
```powershell
|
|
terraform fmt -check -recursive terraform
|
|
python scripts/test-terraform-import-plan-check.py
|
|
```
|
|
|
|
For every root:
|
|
|
|
```powershell
|
|
terraform -chdir=terraform/live/tf-poc init -backend=false
|
|
terraform -chdir=terraform/live/tf-poc 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
|
|
```
|
|
|
|
Initialization without the backend may download providers and write lockfiles,
|
|
but it must not contact HCP state or plan against AWS.
|
|
|
|
## tf-poc flow
|
|
|
|
1. Deploy only the temporary shared stack with `tfPocPhase=zone` and record its
|
|
name servers.
|
|
2. Apply the separately approved parent-zone NS delegation and verify it
|
|
publicly.
|
|
3. Use `tfPocPhase=environment` to add the certificate and environment stack.
|
|
Do not attempt certificate creation before delegation.
|
|
4. Record creator outputs for the distribution, OAC ID/name, function, zone,
|
|
certificate, origin ID, role, inline policy, bucket auto-delete helper role,
|
|
and DNS import IDs.
|
|
5. With the frontend tf-poc HCP role gate still false, set the five org-baseline
|
|
tf-poc identifiers from those outputs and deploy the reviewed boundary
|
|
update. Confirm the creator role's existing boundary now permits only its
|
|
bucket operations and exact distribution invalidation.
|
|
6. Deploy the real SPA through GitHub environment `tf-poc`, built with
|
|
`VITE_API_URL=https://api.tf-poc.seahaven.com/api`.
|
|
7. Pass HTTPS page load, `/login`, extensionless SPA fallback, asset-reference
|
|
integrity, expected/forbidden API URL scan, cache headers, invalidation
|
|
completion, API CORS/preflight connectivity, and index rollback.
|
|
8. Populate HCP variables. Re-run read-only inventory and compare all declared
|
|
metadata.
|
|
9. Produce the import plan, export JSON, and pass the zero-change import gate.
|
|
10. Review and apply only the imports. Require an immediate second no-op plan.
|
|
11. Prepare and inspect retention for all transferred resources and the
|
|
auto-delete custom resource. Do not detach yet.
|
|
12. Set only tf-poc `adoption_complete=true`. Run the controlled-update gate
|
|
with the exact addresses below, apply after review, and require a no-op
|
|
plan. This removes the bucket policy grant while the CDK auto-delete helper
|
|
role still exists.
|
|
13. Detach the creator stack with the reviewed retention template. Verify
|
|
identifiers, every object version, DNS, HTTPS/API smoke checks, deploy-role
|
|
assumption, and a final no-op plan.
|
|
|
|
Stop on a missing output, placeholder, nonzero import action, unexpected
|
|
address, replacement, inventory mismatch, retention mismatch, or smoke failure.
|
|
|
|
## Import plan safety
|
|
|
|
Create a saved plan using the approved remote workflow, then export its JSON:
|
|
|
|
```powershell
|
|
terraform show -json path\to\saved.plan > path\to\plan.json
|
|
python scripts/check-terraform-import-plan.py path\to\plan.json --environment tf-poc
|
|
```
|
|
|
|
Use `dev` or `staging` for the corresponding root. Import mode requires:
|
|
|
|
- exactly the canonical 13 addresses and AWS types
|
|
- valid import metadata for every resource
|
|
- exact known import IDs for dev and staging
|
|
- populated, non-placeholder creator IDs for tf-poc
|
|
- zero create, update, delete, or replace actions
|
|
|
|
After import apply, export the immediate refresh plan and use the distinct
|
|
post-import mode. It requires all 13 resources to be no-op and rejects any
|
|
remaining import metadata:
|
|
|
|
```powershell
|
|
python scripts/check-terraform-import-plan.py path\to\post-import-plan.json `
|
|
--environment tf-poc `
|
|
--post-import-no-op
|
|
```
|
|
|
|
The exact controlled-adoption addresses are:
|
|
|
|
- `module.environment_owned.aws_s3_bucket.site`
|
|
- `module.environment_owned.aws_s3_bucket_policy.site`
|
|
- `module.environment_owned.aws_cloudfront_distribution.site`
|
|
- `module.environment_owned.aws_cloudfront_function.spa_rewrite`
|
|
- `module.environment_owned.aws_iam_role.github_deploy`
|
|
- `module.environment_owned.aws_iam_role_policy.github_deploy`
|
|
|
|
The bucket-policy update removes only the retained auto-delete helper grant.
|
|
The IAM role update changes ownership tags while preserving the exact
|
|
`StringEquals` subject, boundary, and HCP manager tag.
|
|
|
|
Run controlled mode by repeating the exact option:
|
|
|
|
```powershell
|
|
python scripts/check-terraform-import-plan.py path\to\plan.json `
|
|
--environment tf-poc `
|
|
--allow-update-address module.environment_owned.aws_s3_bucket.site `
|
|
--allow-update-address module.environment_owned.aws_s3_bucket_policy.site `
|
|
--allow-update-address module.environment_owned.aws_cloudfront_distribution.site `
|
|
--allow-update-address module.environment_owned.aws_cloudfront_function.spa_rewrite `
|
|
--allow-update-address module.environment_owned.aws_iam_role.github_deploy `
|
|
--allow-update-address module.environment_owned.aws_iam_role_policy.github_deploy
|
|
```
|
|
|
|
Controlled mode permits only in-place updates to the addresses explicitly
|
|
listed on that invocation. It rejects create, delete, replace, import metadata,
|
|
unapproved addresses, and unused allowlist entries. OAC and Route 53 must remain
|
|
unchanged.
|
|
|
|
If an ownership-tagged resource does not actually update because its final tags
|
|
are already present, omit that address from both the plan expectation and the
|
|
command. Never leave an unused allowlist entry.
|
|
|
|
## Dev and staging flow
|
|
|
|
Run one live environment at a time.
|
|
|
|
For dev:
|
|
|
|
1. Keep releases paused.
|
|
2. Complete and verify boundary, retention, and exact-metadata prerequisites.
|
|
3. Run and review the zero-change import plan.
|
|
4. Apply imports and require a second no-op plan.
|
|
5. Prepare and verify the retention template only after tf-poc evidence is
|
|
accepted. Do not detach yet.
|
|
6. Set `adoption_complete=true`, allow only the exact updating addresses from
|
|
the controlled list, apply after review, and require another no-op plan.
|
|
7. Detach CloudFormation with the reviewed retention template.
|
|
8. Verify IDs, object versions, DNS, TLS, API connectivity, content deployment,
|
|
invalidation, rollback, deploy identity, and a final no-op plan.
|
|
9. Re-enable dev release only after explicit approval.
|
|
|
|
Observe dev for the agreed window. Then repeat the full sequence for staging.
|
|
Staging termination protection requires a separately reviewed disable
|
|
immediately before retained stack deletion. Do not carry approval from dev into
|
|
staging.
|
|
|
|
## Evidence
|
|
|
|
Retain for each phase:
|
|
|
|
- HCP run URL and workspace settings showing auto-apply off
|
|
- saved plan JSON and checker output
|
|
- state list containing exactly the 13 managed addresses
|
|
- read-only inventory before and after each mutation
|
|
- synthesized CloudFormation template, reviewed change set, and stack events
|
|
- object-version inventory
|
|
- exact DNS, certificate, distribution, OAC, function, role, and policy IDs
|
|
- deployment, invalidation, smoke, and rollback output
|
|
- post-action no-op plan
|
|
- phase close-out with completed work, validation, risks, deviations, and
|
|
remaining work
|
|
|
|
## Rollback
|
|
|
|
- Before import apply: discard the run and correct configuration.
|
|
- After import but before the controlled ownership update: remove only the
|
|
imported Terraform state addresses under a separately reviewed state
|
|
operation. CloudFormation remains authoritative.
|
|
- After the controlled ownership update but before detachment: do not simply
|
|
remove Terraform state or redeploy CloudFormation. Either complete the
|
|
reviewed retained detachment or explicitly restore the exact pre-adoption
|
|
policy and tags under a separate rollback approval.
|
|
- After detachment: Terraform remains authoritative. Restore content from the
|
|
versioned bucket and release manifests. Do not recreate the legacy stack over
|
|
retained resources.
|
|
- Re-establishing CloudFormation ownership requires a reviewed CloudFormation
|
|
`IMPORT` change set. An ordinary create/update is not a rollback.
|
|
|
|
Any replacement, destroy, cross-environment ID, missing import, broad policy
|
|
change, or failed smoke check is a hard stop.
|