Terraform vs Other IaC Tools
Terraform is a provisioning tool, and the most common confusion for newcomers is treating it as a rival to Ansible, Kubernetes, or shell scripts when those occupy entirely different layers. Knowing which problem each tool actually solves — and how they compose — is the difference between a clean pipeline and a tangle of tools fighting over the same job.
This topic places Terraform on the map. The map has three regions, and most "Terraform versus X" arguments dissolve once you see which region X lives in.
The Three Layers
Infrastructure tooling splits into three layers. Provisioning creates the resources: the virtual machine, the network, the load balancer, the database. Configuration management sets up what runs on those resources: installs packages, writes config files, manages services. Orchestration schedules and runs long-lived workloads across a fleet — what Kubernetes does for containers.
Terraform lives squarely in provisioning. A tool from a different layer is not a competitor; it is a neighbor that picks up where Terraform leaves off. The arguments worth having are between tools in the same layer — Terraform versus CloudFormation versus Pulumi — and those come down to scope, language, and ecosystem.
Terraform vs CloudFormation and Bicep
CloudFormation (AWS) and Bicep (Azure) are single-cloud declarative provisioning tools, built and fully supported by the cloud vendor, with no state file for you to manage — the cloud keeps it server-side. Terraform spans every cloud through one language and brings a far larger module ecosystem, at the cost of owning your own state.
The honest call: on a committed single-cloud, AWS-only shop, CloudFormation removes the entire state-management burden and is tightly integrated. Choose Terraform when you span more than one cloud, want the module ecosystem, or value a single workflow across every platform your team touches.
Terraform vs Pulumi
Pulumi provisions infrastructure too, but you write it in a general-purpose language — TypeScript, Python, Go — instead of HCL. That buys real loops, classes, unit tests, and the IDE support of a mature language. It also buys every way a real language has to be too clever: a Pulumi program can encode logic that is far harder to review than a Terraform plan diff.
HCL is deliberately constrained, and that constraint is a feature when the artifact's job is to be reviewed and reasoned about. Choose Pulumi when your infrastructure genuinely needs a programming language; choose Terraform when a declarative, reviewable description is the point.
Terraform vs Ansible
This is the comparison people get most wrong, because the two are not competitors at all. Terraform declares and tracks the existence of infrastructure — it knows what it created, so it can plan a precise diff and tear things down cleanly. Ansible configures whatever already exists and keeps no state, so it cannot tell you what it would destroy; each run pushes a desired configuration onto hosts.
Put plainly: Terraform creates the server, Ansible sets up what runs on it. Reaching for Terraform provisioners to install and configure software is recreating Ansible badly — untracked, run-once, with no drift detection. The right design hands off from one to the other.
Using Them Together
The clean pattern is a handoff. Terraform provisions the EC2 instances, the VPC, and the load balancer, and exposes what the next stage needs through outputs — instance IPs, tags, an Auto Scaling group name. A configuration tool like Ansible then reads that inventory and configures the operating system and application. Each tool stays in its layer and neither reinvents the other.
The snippet below is the Terraform side of that handoff: an output that publishes the instances' addresses for Ansible (or any downstream tool) to consume. The full pattern — dynamic inventory by tag, sequencing the two in a pipeline — gets its own topic in the advanced chapter.
output "web_private_ips" { value = aws_instance.web[*].private_ip # Ansible reads these as its inventory }
Terraform — provisioning, stateful. Owns the lifecycle of cloud resources and knows what it created, so it can plan an exact diff and destroy cleanly. Reach for it to create the server, network, and database.
Ansible — configuration management, stateless. Pushes a desired configuration onto existing hosts each run, with no record of prior state. Reach for it to install packages, edit config, and manage services on machines that already exist.
- Using a Terraform
remote-execprovisioner to install and configure software — it runs only at create time, is not tracked in state, and turns a configuration job into an untracked side effect; use a configuration tool or a baked image instead. - Trying to manage in-place OS configuration drift with Terraform — it has no model for "the package version changed"; that is configuration management's job.
- Picking Pulumi for the power of a real language, then writing imperative resource loops nobody can review as easily as a plan diff.
- Choosing Terraform over CloudFormation on a single-cloud, AWS-only shop purely out of habit, then maintaining state infrastructure CloudFormation would have handled for free.
- Arguing "Terraform versus Ansible" as if one should win, when they belong to different layers and the answer is usually both.
- Draw the provisioning / configuration line explicitly in your architecture and assign each tool to one layer.
- Hand off from Terraform to the configuration layer through outputs or a generated inventory, not through provisioners.
- Prefer baked machine images (Packer) plus minimal boot-time config over configuring instances at provision time, for faster and more reproducible launches.
- Choose CloudFormation or Bicep when you are committed to one cloud and want zero state management; choose Terraform when you span clouds or want the module ecosystem.
- Choose Pulumi only when the configuration genuinely needs a general-purpose language, accepting the loss of a constrained, easily-reviewed description.
Knowledge Check
Which layer does Terraform operate in?
- Provisioning — creating the resources like networks, machines, and databases
- Configuration management — installing packages and editing files on existing hosts
- Orchestration — scheduling containers across a cluster
- Monitoring — collecting metrics and logs from running systems
Why is using a Terraform remote-exec provisioner to configure software a poor substitute for Ansible?
- It runs only at create time, is not tracked in state, and has no drift detection
- It cannot connect to Linux hosts, only Windows
- It is slower because it runs the configuration twice
- It requires a paid HashiCorp subscription before the provisioner block will execute against any remote host
How do Terraform and Ansible typically hand off to each other?
- Terraform provisions and exposes outputs (IPs, names) that Ansible reads as its inventory to configure the hosts
- Ansible provisions the infrastructure and Terraform validates it afterward
- They share a single state file that both read and write
- Terraform detects the Ansible playbooks in the directory and calls them automatically on every apply with no configuration needed
When is CloudFormation the better choice than Terraform?
- On a committed single-cloud AWS shop that wants vendor support and no state file to manage
- Whenever you need to manage more than one cloud provider
- When you want the largest third-party module ecosystem
- When you need to configure the operating system inside instances, installing packages and editing files after boot
You got correct