Infrastructure as code tools split into two jobs that get conflated constantly: provisioning, which creates and tracks cloud resources, and configuration management, which installs and configures software once a machine already exists. Terraform, OpenTofu, Pulumi, CloudFormation and AWS CDK are provisioning tools; each keeps a state file, or an equivalent record, of what it created so the next run can compute a diff against reality. Ansible is a configuration management tool; it keeps no persistent state file because it re-applies its instructions idempotently against whatever is running right now. Picking between them starts with knowing which job you actually have, not which tool has the most GitHub stars.
Most comparison posts for this keyword are written by one of the vendors involved, which makes the best tool conclusion predictable before you finish the first paragraph. This one has nothing to sell: it lays out what each tool actually does differently, how state tracking works and why it matters, where Ansible fits alongside any of them, and the point at which a two-person team is better off with a couple of well-documented scripts than with any of them. None of the tools below get ranked against each other; the goal is knowing which one matches the job in front of you.
Table of contents
- Provisioning tools vs configuration management
- Terraform and OpenTofu: HCL, a huge provider ecosystem, and a licensing fork
- Pulumi: real programming languages instead of a DSL
- CloudFormation and CDK: the AWS-native path
- How state actually works, and why it bites people
- Where Ansible fits next to any of these
- When IaC is overkill
- How this fits the rest of the stack
- FAQ
Provisioning tools vs configuration management
Terraform, OpenTofu, Pulumi, CloudFormation, and CDK all do the same fundamental job: given a description of desired infrastructure, they figure out what to create, update, or destroy in a cloud provider to match it, and they track what they have already created so the next run is a diff rather than a blind re-apply. Ansible, along with Chef and Puppet, solves a different problem: once a machine exists, install packages, write config files, start services, and keep them in that state, applied repeatedly and idempotently rather than tracked in a state file. Plenty of teams use both together: a provisioning tool creates the virtual machine, and a configuration tool installs and configures what runs on it.
Terraform and OpenTofu: HCL, a huge provider ecosystem, and a licensing fork
Terraform uses its own declarative language, HCL, and its main strength is the sheer size of its provider ecosystem; almost every cloud service, and plenty of non-cloud SaaS products, has a Terraform provider maintained by someone. That scale is also why Terraform’s 2023 shift from an open-source license to the Business Source License mattered to so many teams: it restricted certain commercial uses. OpenTofu is the response, a fork maintained under the Linux Foundation on the permissive MPL-2.0 license, aiming for close to full compatibility with existing Terraform configurations, so migrating between them is usually a smaller lift than switching to a genuinely different tool.
Pulumi: real programming languages instead of a DSL
Pulumi keeps the same state-file model underneath but replaces HCL with an actual programming language, Python, TypeScript, Go, C#, or Java, so infrastructure definitions can use loops, functions, and unit tests the way application code does. The appeal is real for teams who already think in one of those languages and want infrastructure code reviewed and tested the same way; the tradeoff is a smaller library of battle-tested community modules compared to Terraform’s much larger provider registry, and a learning curve that is really about Pulumi’s SDK conventions rather than a new syntax.
CloudFormation and CDK: the AWS-native path
CloudFormation is AWS’s own templating system, written in YAML or JSON, with no separate tool to install or license, and the deepest native coverage of AWS services since AWS builds support for its own new features into it first. Its tradeoff is that it only speaks AWS; there is no multi-cloud story. AWS CDK sits on top of it, compiling code written in TypeScript, Python, or another supported language down into CloudFormation templates, giving you a real language and better abstractions while still inheriting CloudFormation’s AWS-only scope and its underlying deployment mechanics.
How state actually works, and why it bites people
A state file is the tool’s record of what it believes exists, mapped to what you described; every plan or apply is really a three-way comparison between your configuration, the state file, and reality. Drift happens when someone changes a resource by hand outside the tool, a config tweaked directly in a cloud console, and the next apply either fights that change or silently overwrites it, depending on what was touched. For anything beyond a single person on a single machine, state needs to live somewhere shared and locked, commonly an object storage bucket paired with a lock table, or a managed backend like Terraform Cloud, because two people applying against their own local state files at the same time is how infrastructure gets duplicated or destroyed.
Pulumi and CDK carry the same category of risk under different names, since both still compute a diff against a stored record of what exists rather than reading live infrastructure fresh on every run. CloudFormation is the exception worth knowing about: AWS itself tracks stack state server-side, so there is no separate state file for you to store, lock, or lose, which removes one entire category of operational mistake at the cost of being locked to AWS’s own view of what a stack contains.
Where Ansible fits next to any of these
Ansible is agentless, connecting over SSH, and idempotent, meaning running the same playbook twice produces the same end state rather than an error on the second run. It is a strong fit for OS-level configuration, package installation, service restarts, and coordinated patch rollouts across a fleet of machines that already exist. In a stack that also uses Terraform or OpenTofu, a common pattern is straightforward: the provisioning tool creates the virtual machine and its networking, and Ansible takes over from there to configure what actually runs on it, each tool doing the job it was built for rather than one tool trying to do both.
This split also shows up in how each tool recovers from a partial failure. A provisioning tool’s plan-and-apply cycle is built to converge a whole environment toward a described end state in one pass; a configuration management playbook is usually run task by task against a smaller, more targeted set of hosts, which makes it easier to re-run just the step that failed without re-touching everything else that already succeeded.
When IaC is overkill
A handful of resources, managed by hand with good documentation and a small team who all know the setup, is often genuinely fine; the overhead of learning a tool, writing and reviewing its configuration, and managing a state file only pays for itself once you are recreating environments repeatedly, coordinating changes across a team, or need an audit trail of what changed and when. Terraform on AWS and Terraform deployment basics are worth reading once that threshold is crossed. Until then, a platform that deploys directly from a repository and keeps its own build and deploy history already gives a small team a chunk of what IaC exists to provide, reproducibility and a record of change, without a separate state file to maintain.
How this fits the rest of the stack
For a team of one or two people shipping a handful of services, the fastest path to reproducible infrastructure is often not a new tool to learn but a platform that already tracks build logs, deploy history, and rollback for you, without a state file to store, lock, or lose. The RunxBuild hosting calculator shows what that looks like for a service deployed straight from a GitHub repository, and getting started with GitHub deploys covers the setup if a state file is not what your team needs yet, and Terraform is still worth adopting later if the project grows into it.
Useful related references:
- Terraform and AWS Infrastructure as Code
- Terraform Deployment Basics
- The Ansible Copy Module
- Getting started on RunxBuild
FAQ
What is the difference between Terraform and Ansible?
Terraform provisions cloud infrastructure and tracks what it created in a state file, computing a diff on each run. Ansible configures software on machines that already exist, applying instructions idempotently over SSH with no persistent state file. Many teams use both together.
Is OpenTofu a full replacement for Terraform?
For most existing configurations, yes; OpenTofu is a fork maintained under the Linux Foundation aiming for close compatibility with Terraform’s HCL and provider ecosystem, under a permissive open-source license rather than Terraform’s Business Source License.
Do I need Pulumi if I already know Terraform?
Not necessarily. Pulumi’s advantage is using a real programming language with unit tests instead of a domain-specific language, which matters most to teams already comfortable in that language. Terraform’s ecosystem of community providers and modules is still larger.
What is Terraform state and why does it matter?
State is the tool’s record of what infrastructure it believes exists, compared against your configuration and reality on every run. Losing, corrupting, or running two applies against separate copies of state is one of the most common ways infrastructure gets duplicated or destroyed.
Is infrastructure as code worth it for a small team?
It depends on how often the infrastructure changes and how many people touch it. A small, stable setup managed by one or two people with good documentation is often fine by hand; IaC pays off once environments are recreated repeatedly or multiple people make changes.