Skip to content
DevOps devops iac 5 min read

Ansible vs Terraform

Terraform and Ansible are two of the most popular Infrastructure as Code (IaC) tools — IaC means you describe your servers and infrastructure in text files instead of clicking around in a web console. People often ask “which one should I use?”, but that is usually the wrong question. They solve different problems, and the most common real-world setup uses both together. This page explains the difference in plain English, shows a side-by-side comparison, and gives you a clear recommendation for each situation.

The core difference: provisioning vs configuration

There are two distinct jobs when you set up infrastructure:

  1. Provisioning — creating the raw resources. This means spinning up a virtual server (a cloud computer you rent by the hour), a network, a load balancer (a server that spreads traffic across many machines), a database instance, and so on. You are bringing things into existence.
  2. Configuration management — taking a server that already exists and setting it up: installing packages with apt, writing config files, starting services with systemd, creating users. You are making an existing machine ready to do its job.

Terraform is built for provisioning. Ansible is built for configuration management. That single sentence explains 90% of the choice. Ansible can provision and Terraform can run scripts on a server, but each tool is much weaker outside its home turf.

Declarative vs procedural

A second big difference is how you write the instructions.

  • Declarative means you describe the end state you want (“I want one server of this size in this region”), and the tool figures out the steps to get there. Terraform is declarative.
  • Procedural means you describe the steps in order (“install nginx, then copy this file, then restart the service”). Ansible is mostly procedural — a playbook (a YAML file listing tasks) runs top to bottom.

This matters for a beginner because of idempotency — a fancy word that just means “running it twice does no harm”. A truly idempotent run only changes what is not already correct. Terraform is idempotent by design: it compares your desired state to reality and only changes the difference. Ansible is idempotent when you use its built-in modules (like apt, copy, service) instead of raw shell commands, because those modules check the current state before acting.

Comparison table

AspectTerraformAnsible
Primary jobProvisioning infrastructureConfiguring servers / apps
StyleDeclarative (describe end state)Mostly procedural (list of tasks)
LanguageHCL (HashiCorp Configuration Language)YAML playbooks
Tracks state?Yes — keeps a terraform.tfstate fileNo — checks live system each run
Idempotent?Yes, built-inYes, when using modules (not raw shell)
Agent needed on target?No (talks to cloud APIs)No (connects over SSH)
Best atCreating/destroying cloud resourcesInstalling packages, editing config files
Weak atDetailed in-server setupCreating cloud infra, tracking drift
Typical commandterraform applyansible-playbook site.yml

Gotcha: Terraform’s state file (terraform.tfstate) often contains secrets like database passwords in plain text. Never commit it to Git. Store it in a remote backend such as an S3 bucket with encryption and locking enabled.

The state file, explained

Terraform remembers what it built in a state file. Think of it as Terraform’s memory: a JSON record mapping “the server in my code” to “the actual server ID in the cloud”. This is what lets Terraform safely destroy or update exactly the resources it created, and detect drift (when someone changed something by hand). Ansible has no equivalent memory — every run it inspects the live machine fresh. Both approaches are valid; they just fit their jobs.

The common pattern: Terraform builds, Ansible configures

The mature, widely-used pattern is:

  1. Terraform creates the servers, networks, and firewall rules in the cloud.
  2. Terraform writes out the new server IP addresses (e.g. to an inventory file).
  3. Ansible connects over SSH to those servers and configures them — installs Nginx, sets up your app, hardens SSH, etc.

Here is a minimal Terraform file that creates one server and records its IP into an Ansible inventory:

resource "aws_instance" "web" {
  ami           = "ami-0abcdef1234567890"
  instance_type = "t3.micro"
  tags = { Name = "web-01" }
}

resource "local_file" "inventory" {
  filename = "${path.module}/inventory.ini"
  content  = "[web]\n${aws_instance.web.public_ip} ansible_user=ubuntu\n"
}

Run the workflow on your Ubuntu 22.04/24.04 control machine. First install both tools:

sudo apt update
sudo apt install -y ansible
sudo snap install terraform --classic

Output:

ansible-core 2.16.x ...
terraform installed

Then provision with Terraform and configure with Ansible:

terraform init
terraform apply -auto-approve
ansible-playbook -i inventory.ini site.yml

Output:

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

PLAY [Configure web servers] ***************************
TASK [Install nginx] ***********************************
changed: [203.0.113.10]
PLAY RECAP *********************************************
203.0.113.10  : ok=4  changed=1  unreachable=0  failed=0

That site.yml is a normal Ansible playbook:

- name: Configure web servers
  hosts: web
  become: true
  tasks:
    - name: Install nginx
      ansible.builtin.apt:
        name: nginx
        state: present
        update_cache: true
    - name: Ensure nginx is running
      ansible.builtin.service:
        name: nginx
        state: started
        enabled: true

Which should I use? Clear recommendations

Your situationUse
Spinning up cloud servers, VPCs, databases, DNSTerraform
Installing software and config on existing serversAnsible
One-off setup of a server you already rentAnsible
Repeatable, destroyable cloud environments (dev/staging/prod)Terraform
Pushing an app update or a config change to a fleetAnsible
Full lifecycle: create and configure cloud serversBoth (Terraform then Ansible)

Tip: Avoid using Terraform’s provisioner "remote-exec" to do heavy in-server setup. It is fragile, runs only at create time, and is not idempotent. Hand that work to Ansible instead — that is exactly what it is for.

Best practices

  • Use Terraform for what exists (infrastructure) and Ansible for what runs on it (software and config). Do not force one tool to do the other’s job.
  • Store Terraform state remotely with encryption and locking; never commit terraform.tfstate to Git.
  • In Ansible, prefer built-in modules (apt, copy, template, service) over the shell/command modules so your runs stay idempotent.
  • Keep secrets out of both: use a vault (ansible-vault) for Ansible and a secrets manager or environment variables for Terraform.
  • Generate your Ansible inventory from Terraform output so the two stay in sync automatically.
  • Run both in CI/CD (automated pipelines) so the same commands work on every developer’s machine and the server.
Last updated June 15, 2026
Was this helpful?