Quick Decision
| Need | Use |
|---|---|
| Provision cloud infrastructure (VMs, VPCs, databases) | Terraform |
| Configure servers (install packages, manage services) | Ansible |
| Deploy applications | Ansible |
| Manage Kubernetes resources | Either (Terraform for cluster, Ansible for apps) |
| Network device configuration | Ansible |
| One-time ad-hoc tasks | Ansible |
| Track infrastructure state | Terraform |
Core Differences
Approach
Terraform: Declarative — "I want 3 EC2 instances"
resource "aws_instance" "web" {
count = 3
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
}
Ansible: Procedural (with declarative modules) — "Install nginx, then start it"
- name: Configure web server
hosts: webservers
tasks:
- ansible.builtin.apt:
name: nginx
state: present
- ansible.builtin.service:
name: nginx
state: started
State Management
Terraform: Maintains a state file tracking all resources
- Knows what exists, what changed, what to destroy
- Can detect drift
- State must be stored securely (S3, Terraform Cloud)
Ansible: Stateless — no state file
- Checks current state on each run
- Idempotent modules ensure desired state
- No drift detection built-in
Language
Terraform: HCL (HashiCorp Configuration Language) Ansible: YAML + Jinja2
Agent
Terraform: Agentless (API calls to cloud providers) Ansible: Agentless (SSH/WinRM to servers)
Where Each Excels
Terraform Wins
- Cloud resource provisioning (AWS, Azure, GCP)
- Infrastructure lifecycle (create, update, destroy)
- Dependency graphing (parallel resource creation)
- State tracking (knows what it manages)
- Plan before apply (
terraform plan)
Ansible Wins
- Server configuration (packages, files, services)
- Application deployment (zero-downtime rolling updates)
- Network devices (Cisco, Juniper, Arista)
- Ad-hoc commands (quick one-liners)
- Orchestration (multi-tier deployment workflows)
- No state to manage (simpler operations)
- Agentless SSH (nothing to install on targets)
Using Them Together (Best Practice)
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ Terraform │────▶│ Ansible │────▶│ App │
│ Provision │ │ Configure │ │ Deploy │
│ │ │ │ │ │
│ • VPC/Subnet │ │ • Install │ │ • Deploy │
│ • EC2/VMs │ │ packages │ │ code │
│ • RDS/DB │ │ • Configure │ │ • Restart │
│ • S3 Buckets │ │ services │ │ services │
│ • IAM Roles │ │ • Security │ │ • Health │
│ │ │ hardening │ │ checks │
└─────────────┘ └──────────────┘ └─────────────┘
Example: Terraform creates, Ansible configures
# Terraform — create the server
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
key_name = "deploy-key"
provisioner "local-exec" {
command = "echo '${self.public_ip}' >> ../ansible/inventory/hosts"
}
}
# Ansible — configure the server
- hosts: webservers
become: true
roles:
- common
- nginx
- app-deploy
Decision Flowchart
Need to create cloud resources?
├── Yes → Terraform
│ └── Also need to configure them?
│ └── Yes → Terraform + Ansible
└── No
└── Need to configure existing servers?
├── Yes → Ansible
└── Need to deploy applications?
└── Yes → Ansible
Common Mistakes
Using Terraform for Configuration
# DON'T — Terraform is bad at ongoing config management
resource "aws_instance" "web" {
user_data = <<-EOF
#!/bin/bash
apt-get update
apt-get install -y nginx
# This only runs once at creation!
EOF
}
Using Ansible for Infrastructure
# WORKS but Terraform is better for this
- amazon.aws.ec2_instance:
name: web-server
instance_type: t3.micro
image_id: ami-0c55b159cbfafe1f0
# No state tracking — can't easily destroy/update
Conclusion
Terraform for provisioning infrastructure. Ansible for configuring it. Use both together for the most robust automation pipeline. Don't force one tool to do the other's job.
Related
For a production-focused walkthrough, see Luca Berton's guide on combining Terraform and Ansible.