QA plan: Terraform remote state and bootstrap (#20)
Story: Terraform remote state & bootstrap (S3 with S3-native locking), multi-env layout (#20)
Capability: AWS infrastructure (#19)
Test scope
Section titled “Test scope”| Type | Coverage |
|---|---|
| IaC lint | terraform validate passes on all module roots (modules/network, modules/data, modules/api, modules/frontend, envs/qa, envs/prod) |
| IaC lint | tflint passes with no errors |
| Static | Bootstrap resources (S3 buckets) exist as named constants; backend uses use_lockfile = true for S3-native locking; no hardcoded account IDs |
| Static | Each env root references a unique S3 key prefix for remote state (no state collision between QA and Prod) |
| Integration (manual) | One-time bootstrap: S3 state bucket created (S3-native locking, no lock table); terraform init on each env root succeeds |
| Integration (manual) | terraform plan on envs/qa produces a non-empty plan with no errors |
| Security (B8) | OIDC role trust policy scopes to the correct GitHub repo and branch; no * principal |
Note: Terraform apply integration tests are manual (human-gated) and not part of automated CI. CI runs validate and plan only.
Entry criteria
Section titled “Entry criteria”- AWS account accessible via OIDC-federated role in CI
- Bootstrap script or README documented for the one-time step
Exit criteria
Section titled “Exit criteria”terraform validateexits 0 on all module and env rootstflintexits 0- Multi-env layout:
envs/qaandenvs/prodeach have separatebackend.tfwith distinct S3 key prefixes - Bootstrap documentation reviewed by operator before first apply
Environments
Section titled “Environments”IaC lint: CI (no AWS access needed). Bootstrap apply: one-time manual against the AWS account.
Tooling
Section titled “Tooling”terraform validate, tflint, Infracost (initial cost baseline from plan output).
Fixtures and data
Section titled “Fixtures and data”None. Terraform tests operate on the plan output, not a live environment.
How tests are written
Section titled “How tests are written”IaC tests are structural (CI pipeline steps) rather than Given/When/Then test functions. Where shell assertions are used:
# Given the qa env root exists# When terraform validate runsterraform -chdir=envs/qa validate# Then exit code is 0 (pipeline gate)- State key collision between QA and Prod would cause one env to overwrite the other’s state. Verify distinct keys with a
grepassertion in CI. - Bootstrap is a one-time operation; if run twice it must be idempotent (S3 bucket creation is idempotent by default in Terraform with
create_if_missingsemantics or equivalent).
Capability acceptance criteria link
Section titled “Capability acceptance criteria link”Capability #19 acceptance: Terraform modules are reusable, per-environment roots have separate state. Directly verified by the lint and layout exit criteria above.
© 2026 Benjamin Arunski