Renderizado do repositório de origem, preservando títulos, exemplos, código, tabelas, links e imagens.
Infrastructure as code
The value of IaC is that the configuration is the truth: reviewable, reproducible, and revertible. That value evaporates the moment someone changes something in the console, because now the code describes a system that does not exist.
Every change goes through the code. No exceptions for "just this once" or "it's urgent" — the urgent manual fix is precisely how state drifts and how the next change destroys something.
For Terraform specifics — state layout, plan symbols, the operations that force replacement, drift detection, and imports — read references/terraform.md.
1. Read the plan, every time
The plan is the review. Never apply without reading it, and never let a pipeline apply without a human having read it for anything stateful.
terraform plan -out=tfplan
terraform apply tfplan # apply exactly what was reviewedRead specifically for:
- Destroy and replace.
-/+means the resource is destroyed and recreated. On a database,
a load balancer, or anything with an address other systems know, that is an outage
- Changes you did not expect: usually drift, or a provider version that changed a default
- Count changes: a resource count that moved can renumber everything after it
Done when: you can account for every line of the plan.
2. Protect state
State is the most dangerous file in the repository. It maps code to real resources, it usually contains secrets, and losing it means your infrastructure exists with nothing managing it.
- Remote backend, with versioning and encryption. Never local, never committed
- Locking enabled, so two applies cannot run concurrently
- Never edit state by hand. Use the tool's import, move, and remove commands
- Treat state as sensitive: it contains generated passwords and connection strings in
plaintext
- Separate state per environment. One state file for production and staging means a mistake
in one can destroy the other
Done when: state is remote, locked, versioned, and access-controlled.
3. Separate environments properly
Environments should differ in values, not in code. Copy-pasted directories per environment diverge, and the divergence is discovered during an incident.
Use one module with per-environment variable files, or workspaces where they fit. Keep production values in a file that is reviewed more carefully than the others.
Never point a non-production run at production credentials. The classic disaster is a staging apply with production state.
Done when: the only difference between environments is a values file.
4. Guard the irreversible
- `prevent_destroy` on databases, storage buckets, and anything holding data
- Deletion protection at the provider level as well — belt and braces, since the lifecycle
rule only protects against this tool
- Backups verified before any change touching stateful resources
- Know what forces replacement. Many attributes are immutable, and changing one silently
means destroy-and-recreate
Done when: no plan can destroy data without an explicit, deliberate override.
5. Modularise, but not too early
- Start flat. A single configuration is easier to read than three layers of module
indirection
- Extract a module on the third repetition, not the first
- Pin module and provider versions. An unpinned provider means
initcan pull a new major
and change behaviour with no code change
- Keep blast radius small: separate state per system, so a mistake in one cannot cascade
Done when: each abstraction removes real duplication.
6. Detect drift before it bites
Run a plan on a schedule against every environment. An empty plan means the code and reality agree; anything else is drift to investigate.
When drift is found, decide deliberately: adopt it into code, or revert it. Leaving it is how you get an apply that unexpectedly deletes something someone needed.
Done when: drift is detected automatically rather than discovered during a change.
Report
State what changed, what the plan showed, what was replaced rather than updated, and what remains manual. Anything still created by hand is a gap in the model, and naming it is more useful than pretending the code is complete.

