// In depth
What "everything in code" actually covers #
Infrastructure as code means the entire environment can be reconstructed from a Git repository — not "we have some Terraform" while the load balancer was hand-edited in 2023. On our platforms, code is the source of truth for every layer:
| Layer | Examples | Defined in |
|---|---|---|
| Network | VPCs, subnets, security groups, peering | Terraform |
| Compute | Clusters, node pools, autoscaling groups | Terraform |
| Data | Databases, replicas, backup policies, parameter groups | Terraform |
| Edge | DNS, certificates, CDN, WAF rules | Terraform |
| Identity | IAM roles, policies, service accounts | Terraform |
| Workloads | Deployments, jobs, config, scaling rules | Kubernetes manifests / Helm |
| Pipelines | Build, test, deploy automation | CI config in the repo |
NoteThe test we apply: could we rebuild this environment in a new account from the repo alone? If the answer involves "remember to also click…", it is not done.
How an infrastructure change ships #
Infrastructure changes follow the same workflow as application code — same review bar, same audit trail:
- An engineer opens a pull request changing the Terraform.
- CI posts the plan output into the PR: exactly which resources will be added, changed, or destroyed.
- A second senior engineer reviews both the code and the plan. Destructive changes (anything with "destroy" in the plan) require explicit acknowledgment.
- On merge, the pipeline applies the plan — humans never run apply against production from a laptop.
- The state file records the result; the PR records the why. Auditors get both.
This is what makes compliance evidence nearly free on our platforms: "who changed the firewall rule, when, and who approved it" is answered by a Git history instead of an archaeology project.
Drift detection #
Drift is the gap between what the code says and what actually exists — usually created by a well-meaning console change during an incident. Drift is how environments rot: the code stops being trustworthy, rebuilds stop being possible, and every future change gets scarier.
- Scheduled drift checks compare live infrastructure against the code daily and alert on any difference.
- Emergency console changes are allowed during incidents — then reconciled back into code within one business day, as a tracked action item from the postmortem.
- Read-only console access is the default for humans; mutating access is exceptional, logged, and time-boxed.
Modules & environment parity #
Environments are instances of the same modules with different parameters — staging is production at smaller scale, not a hand-built approximation of it. Our module library (v3) encodes the patterns we repeat across clients: hardened networking, databases with backups wired in, observability included by default.
- One module, many environments: dev/staging/production differ in size and replica counts, never in architecture.
- Security baselines are inside the modules — a developer cannot accidentally create a public database, because the module has no such option.
- Cost-allocation tags are applied by the module, so the bill is attributable by service and environment from day one.
- Upgrades are tested against every active client stack before rollout — module v3 shipped with migration paths, not migration surprises.
Secrets & state #
Two things never live in the repository: secrets and state. Secrets (API keys, credentials, certificates) live in a managed vault, referenced by the code and injected at deploy time — rotating a credential touches the vault, not twelve config files. Terraform state lives in encrypted remote storage with locking, in your account, because state files contain a map of your infrastructure and deserve database-grade protection.
The disaster-recovery dividend #
A reproducible environment is the most underrated disaster-recovery asset. If a region fails — or an account is compromised — the recovery procedure is: provision from code in a clean target, restore data from backups, repoint DNS. We rehearse exactly this in DR drills; clients with IaC measure regional recovery in hours. Clients without it measure in weeks, because step one is "first, remember what the environment was."
Adopting IaC on an existing estate #
Brownfield adoption does not require a freeze or a rebuild. We import what exists into Terraform incrementally — starting with the highest-risk layers (network, IAM) — and expand coverage with every sprint. The practical sequence:
- Inventory and import: bring existing resources under state management without changing them.
- Codify the riskiest layers first: networking and identity, where an undocumented change hurts most.
- Establish the PR workflow and drift detection — the discipline matters more than the coverage percentage.
- Codify the remainder opportunistically: every time something needs changing anyway, it gets imported first.
NoteTypical timeline: a mid-size estate reaches "all changes via code" in 4–6 weeks, and full coverage within a quarter — without pausing feature work.