# Section 4 — Infrastructure as Code > Companion to [`musehub-production-readiness-checklist.md`](../musehub-production-readiness-checklist.md). > Document-only pass. No Terraform/CDK/CloudFormation written yet — that's real build work, > deferred to after the full sweep per Gabriel's direction. ## Current state **No IaC exists.** All infrastructure was created by hand via one-off bash scripts (`aws-provision.sh`, `aws-provision-staging.sh`, `bootstrap-instance.sh`, `setup-ec2*.sh`) or direct console/CLI actions. These scripts are *provisioning helpers*, not IaC — they're not idempotent state managers, don't track drift, and don't represent a "current state" that can be diffed against reality. ## Recommendation (recorded, not yet actioned) **Terraform/OpenTofu** — already recorded in the main checklist's Decision Log during Section 0. Rationale unchanged: the actual footprint (2 EC2 instances, 1 SG, 1 ECR repo, 1 S3 bucket, a handful of SSM params, and now confirmed — Cloudflare DNS zone/records, see Section 6) is small and EC2-centric. Terraform has a mature `cloudflare` provider alongside `aws`, which matters here since infra now spans both providers, not just AWS. ## What the later build pass needs to capture Everything in "Manually created AWS resources" from the [Section 0 doc](00-inventory-and-decisions.md), plus (per Section 6 finding) the Cloudflare DNS zone and records — Cloudflare state has never been in code either. ## Checklist items — all deferred to the build pass Every remaining Section 4 item (module structure, environment separation, encrypted/locked remote state, provider pinning, static scanning, drift detection, import-existing-resources runbook, tagging standard, teardown guards, deletion protection, policy checks, required review) depends on IaC existing at all. None can be meaningfully started before the tool is chosen and a first module is written. Tracked as a single follow-up: "stand up Terraform/OpenTofu covering the current manually-created resource set, import don't recreate."