Skip to content

What Is Infrastructure as Code

Infrastructure as Code means describing your cloud resources in a text file that lives in version control, instead of clicking through a portal — the file is the source of truth, and a tool reconciles reality to match it.

Picture an engineer opening the Azure Portal and creating a resource group, a virtual network, a virtual machine, and a network security group by hand. It works. Then three weeks later someone else opens the portal, tweaks an NSG rule to unblock a deploy, and never tells anyone. A month after that, someone deletes what looks like an orphaned storage account — it was not orphaned.

This pattern has a name, usually said with a grimace: ClickOps. The failure mode is not that any single click was wrong. It is that the sequence of clicks is:

  • Unrepeatable — nobody can stand up an identical environment from scratch, because the “instructions” were a person’s muscle memory.
  • Undocumented — the portal shows the current state, not why it looks that way or who changed what.
  • Drift-prone — the moment two people can independently change the same subscription, its real state and anyone’s mental model of it start to diverge, silently.

Nobody can answer “what is actually deployed right now, and why” just by reading anything. The portal itself is the only source of truth, and it has no memory, no review process, and no diff.

Infrastructure as Code (IaC) fixes this by making the desired infrastructure a file: checked into git, reviewed in a pull request like any other code change, with history you can git blame. But not all IaC tools work the same way, and the distinction matters:

  • Declarative IaC — you describe the end state you want, and the tool figures out how to get there. Terraform is declarative: a resource block says “there should be a resource group named this, with these properties,” and Terraform works out whether that means creating it, updating it, or leaving it alone.
  • Imperative IaC — you write an ordered script of steps: “create a resource group, then create a virtual network inside it, then launch a VM in that network.” The script describes how, and it is your job to keep it correct as things change; running it twice can misbehave unless every step is written to be safe to repeat.
# Declarative: describe the desired end state.
# Terraform decides what needs to change to get there.
resource "azurerm_resource_group" "main" {
name = "example-resources"
location = "East US"
}
Terminal window
# Imperative equivalent: an explicit, ordered command.
# Running this twice either creates a conflict or errors out —
# the script itself has no idea what already exists.
az group create --name example-resources --location eastus

Notice the declarative version never mentions create. That verb is decided by Terraform, at plan time, based on what it finds.

Terraform’s core loop is always the same three-way comparison:

  1. Read the current real-world state of the resources it manages (it keeps a record of this in a state file).
  2. Read your declared configuration — the HCL describing what you want.
  3. Compute a plan: the minimal set of creates, updates, and destroys needed to make the real world match the configuration.

Nothing gets torn down and rebuilt from scratch just because you changed one argument. If you only changed a resource group’s tags, the plan is an in-place update to that one attribute — everything else Terraform manages is left untouched.

flowchart LR
  config["HCL config (desired state)"] --> tf["Terraform"]
  real["Real Azure resources (actual state)"] --> tf
  tf --> plan["Plan: the diff"]
  plan --> apply["Apply: minimal changes"]
Terraform reconciles desired configuration against real infrastructure

Terraform is not the only IaC tool, and it helps to know what it is not:

  • ARM templates / Bicep — Azure’s own native IaC. Bicep is Microsoft’s more ergonomic DSL that compiles down to ARM JSON under the hood, and both are declarative like Terraform — but they are Azure-only, with no equivalent way to manage a GCP project or a Datadog monitor from the same file.
  • Pulumi — also provider-based and state-tracked, but you write actual programming languages (TypeScript, Python, Go) instead of a declarative HCL syntax, trading a smaller learning curve for the full expressiveness (and full complexity) of a general-purpose language.
  • Ansible — primarily a configuration management tool: it logs into existing machines and runs tasks against them (install a package, edit a config file, restart a service). Its playbooks are closer to imperative in practice — ordered tasks executed top to bottom — and it has no built-in state file tracking what it created.

Terraform’s niche is declarative, provider-based, state-tracked infrastructure provisioning: a plugin model (providers) lets the same workflow and HCL syntax target Azure, AWS, GCP, Kubernetes, Datadog, and hundreds of other systems, with a state file recording exactly what Terraform is responsible for.

One more name worth knowing: OpenTofu. After a 2023 licensing change to Terraform, a group of contributors and companies forked the last open-source release into OpenTofu, now maintained under the Linux Foundation. It is a compatible, open-source alternative — the HCL syntax, providers, and workflow you learn here apply to it directly.

What is the core problem with making infrastructure changes by clicking through the Azure Portal (ClickOps)?
What is the difference between declarative and imperative Infrastructure as Code
What does Terraform compute between your desired configuration and the real infrastructure state
Which statement about Terraform alternatives is accurate