Cloud

Infrastructure as code: getting the fundamentals right with Terraform

Infrastructure as code delivers reproducibility only if state, modules and environments are handled deliberately from the start.

Key takeaways

  • Remote, locked, per-environment state is non-negotiable.
  • Extract modules when a pattern repeats, not in anticipation.
  • Treat plan output as a review artefact requiring human reading.
  • Detect drift on a schedule and reconcile it deliberately.

State is the part that goes wrong

Terraform's state file records what it believes exists. Keep it in remote storage with locking enabled, never in a repository, and never shared informally between engineers.

Separate state per environment. A single state file spanning development and production means a mistake in one can affect the other, which is exactly the failure mode infrastructure as code is supposed to prevent.

Structure for change, not for elegance

Write modules for genuinely reusable pieces — a standard network, a standard service — and keep environment configuration thin. Over-abstracting early produces modules with dozens of variables that nobody can reason about.

Start concrete, extract a module when the same pattern appears a third time.

Plan output is a review artefact

Run plan in the pipeline on every pull request and require a human to read it. The plan is the clearest possible statement of what a change will do, and reviewing it catches destructive changes before they run.

  • Flag any plan that destroys or replaces a stateful resource.
  • Require approval before apply on production.
  • Keep apply automated so manual runs from laptops are unnecessary.

Drift and the manual change problem

Someone will eventually change something in the console during an incident. That is a reasonable thing to do under pressure and a problem if it stays undocumented.

Detect drift on a schedule and reconcile deliberately — either bring the change into code or revert it. Estates where drift accumulates unchecked lose the reproducibility that justified the effort.

Managing existing resources

Few teams start with an empty account. Bringing existing infrastructure under management is done by importing resources into state and writing configuration that matches them exactly — a process that is tedious and worth doing.

Work in small batches, verifying that a plan against imported resources reports no changes before moving on. A plan that wants to modify something you just imported means the configuration does not match reality, and applying it would be destructive.

Accept that some resources should stay outside management, at least initially. A clear boundary between managed and unmanaged is better than a partial import nobody trusts.


Frequently asked questions

Terraform is a reasonable default, especially across providers or where the team already knows it. Native tools integrate more tightly with their platform and are a fine choice for single-provider estates.

Not in Terraform variables committed to a repository. Use a secrets manager and reference values at runtime, and be aware that some secrets can appear in state, which is another reason to protect it.

Generally no. Terraform suits infrastructure that changes infrequently. Application releases move faster and are better handled by a deployment pipeline.

Working on this?

A conversation with our team, no charge and no obligation.

Book a conversation