Skip to content

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)

TypeCoverage
IaC lintterraform validate passes on all module roots (modules/network, modules/data, modules/api, modules/frontend, envs/qa, envs/prod)
IaC linttflint passes with no errors
StaticBootstrap resources (S3 buckets) exist as named constants; backend uses use_lockfile = true for S3-native locking; no hardcoded account IDs
StaticEach 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.

  • AWS account accessible via OIDC-federated role in CI
  • Bootstrap script or README documented for the one-time step
  • terraform validate exits 0 on all module and env roots
  • tflint exits 0
  • Multi-env layout: envs/qa and envs/prod each have separate backend.tf with distinct S3 key prefixes
  • Bootstrap documentation reviewed by operator before first apply

IaC lint: CI (no AWS access needed). Bootstrap apply: one-time manual against the AWS account.

terraform validate, tflint, Infracost (initial cost baseline from plan output).

None. Terraform tests operate on the plan output, not a live environment.

IaC tests are structural (CI pipeline steps) rather than Given/When/Then test functions. Where shell assertions are used:

Terminal window
# Given the qa env root exists
# When terraform validate runs
terraform -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 grep assertion 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_missing semantics or equivalent).

Capability #19 acceptance: Terraform modules are reusable, per-environment roots have separate state. Directly verified by the lint and layout exit criteria above.