Explain declarative vs. imperative infrastructure provisioning, and where Terraform fits.

4 minintermediateterraformdeclarativeiac

Quick Answer

Imperative tooling (a bash script, boto3 script) specifies the *steps* to reach a result — you must handle ordering, idempotency, and error recovery yourself. Declarative tooling (Terraform, Kubernetes manifests) specifies the *desired end state*, and the tool's engine diffs that against reality and figures out the steps. Terraform is declarative: it builds a dependency graph from resource references, computes create/update/destroy actions via `plan`, and applies them idempotently — running `apply` twice in a row with no config change is a no-op.

Detailed Answer

This distinction comes up constantly when comparing Terraform to shell scripts, SDK-based automation, or CloudFormation, so it's worth having a crisp mental model.

Imperative: specify the steps

An imperative script tells the system exactly how to reach a result, step by step:

aws ec2 run-instances --image-id ami-123 --count 1 --instance-type t3.micro
aws ec2 create-tags --resources i-abcdef --tags Key=Name,Value=web

Problems with this approach at scale:

  • You must handle idempotency yourself — running the script twice creates two instances, not one.
  • You must handle ordering and error recovery — what happens if step 2 fails after step 1 succeeded?
  • There's no built-in way to know what the script has already created without re-reading external state.

Declarative: specify the desired end state

resource "aws_instance" "web" {
  ami           = "ami-123"
  instance_type = "t3.micro"
  tags = { Name = "web" }
}

You describe what should exist, not the steps to get there. The engine (Terraform Core) is responsible for:

  • Comparing desired state (config) against last-known state (the state file) and real infrastructure.
  • Computing the minimal set of create/update/destroy actions needed.
  • Executing them in correct dependency order.
  • Being idempotent by construction — running apply again with no config changes is a no-op, because the diff comes back empty.

Where Terraform sits

Terraform is fully declarative at the resource level: you never write "create this, then that." Its dependency graph, built automatically from resource references, determines execution order, and its plan/apply separation means the "what will change" computation is always visible before anything is touched. This is precisely what makes terraform plan meaningful as a review step — there's a clear notion of "current state" vs. "desired state" to diff, which a purely imperative script doesn't have.

Interview framing: declarative tools answer "what do you want?"; imperative tools answer "what should I do?" — and Terraform deliberately chose the former to get safe, idempotent, reviewable infrastructure changes.

Related Resources