Chapter 13: Extending Ansible & Automation at Scale
Topic 81

Where Ansible Fits

Comparison

The whole book lands here. Terraform provisions the infrastructure and remembers it in state; Ansible configures what runs inside that infrastructure and keeps no state at all. They are not competitors and never were — they are adjacent layers, and the strongest pipeline runs terraform apply to build the Larkspur fleet, reads the result through dynamic inventory, and runs ansible-playbook to configure exactly what Terraform created.

The two layers, meeting at dynamic inventory
terraform apply
dynamic inventory
ansible-playbook

This is the reciprocal of what the Terraform books say about Ansible, stated from the Ansible side and meant to settle the question for good. The distinction that runs through every chapter of this course — no state file, the machine is the record — is exactly the seam where Ansible meets Terraform, and the seam is the state file itself.

The Two Layers, Stated Plainly

Terraform owns the infrastructure lifecycle with state. It creates the VMs, the networks, and the load balancer, and its state file is what lets it know what exists and therefore what to destroy when you remove a resource. Ansible owns configuration without state. It installs nginx, templates app.env, and re-converges the live machine on every run, with the server itself as the only record of what is true. State is precisely what one tool has and the other deliberately does not — and that single difference assigns every task to a layer.

The Combined Pipeline

terraform apply builds the fleet; its output, or the cloud's own resource tags, feeds an Ansible dynamic inventory plugin; ansible-playbook then configures every host that now exists, with no IP copied by hand. The handoff is the dynamic inventory from Chapter 2, and it is what makes a rebuilt fleet reconfigure itself: tear the instances down, terraform apply again, and the new machines appear in inventory and converge with no manual edits anywhere.

The handoff — Terraform builds, dynamic inventory reads, Ansible configures
# 1. Terraform provisions the fleet and records it in state
$ terraform apply
  # aws_instance.web[0..2] created, tagged Project=larkspur Role=web

# 2. Ansible's dynamic inventory reads the same cloud Terraform just built
$ ansible-inventory -i inventory/aws_ec2.yml --graph
  # @web:
  #   |--web1.larkspur.io
  #   |--web2.larkspur.io
  #   |--web3.larkspur.io

# 3. Ansible configures exactly the hosts Terraform created — no IP typed by hand
$ ansible-playbook -i inventory/aws_ec2.yml site.yml

No IP address is written into a static inventory at any point. Terraform's tags are the contract, the aws_ec2 inventory plugin reads them live, and Ansible targets the web group that the cloud itself reports — so the two tools meet at the cloud's current state, not at a file someone remembered to update.

When Ansible Alone

You need no Terraform when there is nothing with a create-and-destroy lifecycle to track. An existing fleet you did not provision — bare metal, a cloud someone else built, network gear that was racked years ago — is pure configuration and orchestration, and Ansible handles it end to end. There is no infrastructure to bring into existence, so the provisioning layer is simply absent, and reaching for Terraform would be inventing a lifecycle to manage where none exists.

When Terraform Alone

You need no Ansible when the infrastructure's entire job is to exist and route, with nothing to configure inside a server. A managed database, a load balancer, an object-storage bucket, a serverless function — these have a lifecycle Terraform creates and destroys, but no operating system to converge, no packages to install, no config file to template. There is no host to configure, so there is no Ansible layer; the work begins and ends at provisioning.

When Both, and What to Learn Next

Anything that is a server running software wants both: Terraform builds it, Ansible configures it, and the two meet at dynamic inventory. That is the shape of nearly every real stack, because nearly every real stack has machines with an OS and software on them. From here, deepen the provisioning side with the Terraform Deep Dive books, harden the controller path with AAP, and carry the state-versus-no-state distinction with you as the map of which tool owns which problem — because the moment you can name whether something has a lifecycle to track, you know which layer you are standing in.

Ansible vs Terraform — the closing map

Terraform — provisioning. A declarative resource graph and a state file that tracks what exists and what to destroy, owning the infrastructure lifecycle. It answers "what infrastructure should exist?" and knows how to tear it down because the state remembers what it built.

Ansible — configuration and orchestration. Ordered tasks pushed over SSH, no state because the live host is the record, owning what runs inside a machine. It answers "how should this server be set up?" and re-checks reality every run. The boundary is the state file: if it has a lifecycle to create and destroy, Terraform owns it; if it is software and settings on a host that already exists, Ansible owns it. Use both for any server — and the moment you feel yourself fighting one tool, you have crossed into the other's layer.

Common Mistakes
  • Using Terraform remote-exec or local-exec provisioners to install and configure software — they run once at create time, are not idempotent, and HashiCorp documents them as a last resort; have Terraform trigger an Ansible run instead.
  • Driving cloud-infrastructure lifecycle from Ansible's cloud modules and expecting Terraform-style cleanup — with no state, Ansible cannot know what to destroy when you remove a task; it just stops touching it.
  • Copying Terraform's output IPs into a static Ansible inventory by hand, which drifts the instant the fleet is rebuilt — let dynamic inventory read the same cloud Terraform created.
  • Arguing "Ansible or Terraform" as if one wins — the question is never which tool, it is which layer you are operating at right now, and the answer is usually "both, at different layers."
  • Treating Ansible's lack of state as a weakness to work around — it is the design: no state to lock, corrupt, or drift from reality, because the machine is checked live on every run.
Best Practices
  • Let Terraform provision and Ansible configure, wiring the handoff with dynamic inventory so a rebuilt fleet reconfigures with no manual edits.
  • Keep the boundary at the state file: a lifecycle to create and destroy is Terraform's, software and config on an existing host is Ansible's.
  • If Terraform must kick off configuration, have it trigger an Ansible run or a controller job template, never an inline remote-exec shell.
  • Run both through governed pipelines — Terraform plan and apply with state and locking, Ansible converge through the controller with RBAC and audit — so each layer is reviewed and recorded.
  • When a task feels like a fight against the tool you are holding, stop and check the layer — you have almost certainly crossed the state boundary into the other tool's job.
Comparable tools Terraform · Pulumi · CloudFormation the provisioning layer with state Packer a third adjacent layer — baking images Puppet · Chef · Salt Ansible's configuration-management peers cloud-init first-boot config below both layers

Knowledge Check

Which tool creates a server and which configures it, and why?

  • Terraform creates it because it owns the infrastructure lifecycle with state; Ansible then configures what runs inside it, without state
  • Ansible creates the server because it connects over SSH; Terraform configures what runs inside because it holds a state file
  • Both tools create and configure servers interchangeably, so the choice between them is purely a matter of team preference and existing habit
  • Terraform does both creation and configuration, and Ansible is only a fallback used when Terraform is unavailable

What does the state file give Terraform that Ansible deliberately lacks?

  • A record of what exists, so Terraform knows what to destroy — Ansible lacks this because the live host is its only state
  • Idempotency — without a stored state file, Ansible has no way to avoid repeating actions on a second run
  • The ability to connect over SSH to a remote host, which Ansible can only add by first installing a separate connection plugin
  • A way to install packages on the host, which Ansible can only do by delegating to Terraform

In the combined pipeline, what wires Terraform's output to Ansible's run?

  • A dynamic inventory plugin reads the same cloud Terraform built, so Ansible targets exactly the hosts that now exist
  • Terraform writes a static inventory file that an engineer opens and edits by hand before each playbook run
  • Ansible reads Terraform's state file directly on disk to discover the addresses of the machines that were just created
  • A remote-exec provisioner block runs the whole playbook inline at resource-create time, before the apply finishes

When is Terraform used alone, with no Ansible layer?

  • When the infrastructure's whole job is to exist and route — a managed database, load balancer, or bucket — with no OS to converge
  • Whenever the fleet grows past a few dozen hosts, since Ansible cannot scale its SSH connections beyond that point
  • When the team has no SSH access at all to any of the hosts, which Ansible strictly requires for every single run while Terraform plainly does not
  • Never — every Terraform stack requires a follow-up Ansible converge before it can be considered complete

You got correct