Why infrastructure as code, specifically
A cloud environment built through console clicks has no history, no review process and no reliable way to reproduce it elsewhere. When the person who built it leaves, or a disaster recovery scenario requires rebuilding it from scratch, the environment effectively becomes undocumented. Terraform (and tools like it) solve this by describing infrastructure as declarative configuration files: the desired end state is written down, checked into version control, reviewed like application code, and applied through a consistent, repeatable process.
How Terraform actually works
- Configuration files (written in HCL) declare the desired resources — a virtual machine, a network, a database — without specifying the exact steps to create them.
- Providers translate that declarative configuration into calls against a specific platform (AWS, Azure, GCP, Kubernetes, and hundreds of others).
- State is Terraform record of what it believes currently exists, used to calculate the difference between the desired configuration and reality on the next run.
- A plan shows exactly what will change before anything is applied — additions, modifications and deletions — which is the main safety mechanism against unintended changes.
- Apply executes that plan, creating, updating or destroying resources to match the declared configuration.
State management is the part that actually matters
Terraform state is the single most important operational concern once more than one person works on the same infrastructure. State stored on a local laptop cannot be shared, is not locked against concurrent changes, and is one accidental deletion away from Terraform losing track of everything it manages.
- Store state remotely (S3 with DynamoDB locking, Azure Storage, Google Cloud Storage, or Terraform Cloud) rather than on a local machine, from the very first environment.
- Enable state locking so two people running Terraform at the same time cannot corrupt each other, work.
- Never edit state files by hand; use Terraform commands (import, state mv, state rm) for any state surgery that is genuinely required.
- Restrict who can read state directly — it frequently contains sensitive values (database passwords, connection strings) in plain text.
Modules keep configuration maintainable as it grows
Copy-pasted configuration across environments (development, staging, production) is the most common way Terraform projects become unmaintainable. A module packages a reusable piece of infrastructure — a standard VPC, a standard database setup, a standard Kubernetes cluster — parameterised so the same module produces different, appropriately sized environments from different input values. Well-designed modules mean a change to the standard pattern (adding a required tag, tightening a default security group) is made once, in the module, rather than repeated across every environment that uses it.
Separating environments correctly
Terraform workspaces allow multiple, separately tracked states from the same configuration, which suits lightweight variation (feature branches, short-lived test environments). For genuinely separate environments with materially different configurations and access controls — most notably production versus everything else — a separate state file and directory structure per environment is generally the safer, clearer pattern, since it makes accidental cross-environment changes structurally harder rather than relying on remembering which workspace is currently selected.
Running Terraform through CI/CD, not from a laptop
- Run terraform plan automatically on every pull request so reviewers see the actual infrastructure change alongside the code change.
- Require manual approval before terraform apply runs against production, even when the pipeline is otherwise automated.
- Use a dedicated, least-privilege service identity for the pipeline to run Terraform, rather than a personal credential.
- Pin provider and module versions explicitly so a routine run does not unexpectedly pull in a breaking upstream change.
- Scan proposed changes with a policy tool (such as Sentinel, OPA or Checkov) to catch security misconfigurations before they are applied, not after.
Mistakes that undermine infrastructure as code
- Manually changing a resource in the console after it is under Terraform management — the next apply either reverts the manual change or, worse, Terraform and reality drift apart silently.
- One enormous configuration covering the entire estate, instead of sensibly sized modules and state files that limit the blast radius of a mistake.
- No plan review step before apply, turning what should be a reviewable change into a black box.
- Storing secrets directly in configuration files instead of a secrets manager referenced at apply time.
- Treating the initial Terraform write-up as finished rather than as code that needs the same ongoing maintenance, review and refactoring as application code.
Frequently asked questions
No. Terraform focuses on provisioning infrastructure itself — creating servers, networks and managed services. Ansible and Chef focus on configuring what runs on a server after it exists. Many platforms use both together: Terraform to provision the infrastructure, and a configuration tool for what runs on top of it.
On the next plan, Terraform detects the difference between the declared configuration and the actual resource state and will typically propose reverting the manual change back to match the configuration, unless the configuration itself is updated to reflect the change intentionally.
Self-managed remote state (an S3 bucket with DynamoDB locking, for example) is a fully viable and common approach for many teams. Terraform Cloud or similar platforms add convenience — a shared UI, policy enforcement, run history — that becomes more valuable as team size and the number of environments grow.
Using the terraform import command, which brings an existing resource under Terraform management without recreating it, followed by writing configuration that matches its actual current settings. This is done incrementally, resource by resource, rather than attempting to import an entire estate at once.
The same module with different input values (instance sizes, replica counts, feature flags) is the goal — this is what keeps environments consistent in structure while appropriately sized for their purpose. Forking a module per environment reintroduces the copy-paste maintenance problem modules exist to solve.
Terraform providers exist for every major cloud, so the same tool and workflow can manage infrastructure across AWS, Azure and GCP. The configuration itself is not automatically portable between providers, since each has different resource types and concepts, but the operational pattern (plan, review, apply) is consistent across all of them.
How we can help
Target environments delivered as Terraform, not hand-clicked console changes.
Solution Design & ArchitectureArchitecture and infrastructure-as-code standards agreed before the build starts.
