Terraform vs Ansible: Which One Should You Learn First?

Terraform vs Ansible on one 4-switch VLAN lab: what each tool does, what changes on day two, and which one to learn first for your exam and career track.
14min
Read Time
A network engineer typing on a laptop beside an open network rack full of blue patch cables, with a navy fade from the bottom-left corner.
Terraform vs Ansible on one 4-switch VLAN lab: what each tool does, what changes on day two, and which one to learn first for your exam and career track.

Terraform vs Ansible comes down to one split. Terraform builds infrastructure and keeps a state file of what it built. Ansible configures machines and network devices that already exist, over SSH, with no agent to install. Network engineer? Learn Ansible first. Heading into cloud? Learn Terraform first. Most teams end up running both.

That’s the short version. Below, we put both tools on the same small lab, four switches and three VLANs, and looked at what each one does on the first run, the second run, and the day someone deletes something. The difference that matters shows up on day two. And it’s not the one most comparisons spend their time on.

Terraform vs Ansible: what’s the difference?

Terraform is an infrastructure as code tool for provisioning. You describe what should exist (a VM, a subnet, a VLAN) and Terraform creates, changes or destroys real objects until they match. Ansible is an automation tool for configuration. You write tasks (install a package, push a config line, restart a service) and Ansible runs them against the hosts in your inventory.

TerraformAnsible
Main jobCreate and remove infrastructureConfigure what already exists
LanguageHCLYAML playbooks
StyleDeclarative: you describe the end stateMostly procedural: tasks run in the order you wrote them
StateA state file tracks everything it managesNo state file. It checks the target on every run
Agent on targetsNone. It talks to provider APIsNone. SSH, PowerShell remoting and other transports
Python on targetsNot neededLinux targets need it. Network devices don’t
Network devicesThrough providers, like Cisco’s IOS XE provider over NETCONFThrough collections, like cisco.ios over CLI and SSH
Runs on WindowsNative Windows buildsThe control node needs WSL
LicenceBusiness Source License (BUSL) since 1.6.0GPL-3.0
OwnerIBM, through HashiCorpIBM, through Red Hat
Latest release (2026-10-19)1.16.6ansible-core 2.21.5
CertificationHashiCorp Terraform Associate (004)Red Hat Certified Engineer in Ansible

Is Terraform declarative and Ansible procedural?

Mostly, yes. HashiCorp’s own docs say the Terraform language “is declarative, describing an intended goal rather than the steps to reach that goal.” Ansible gets called procedural because a playbook runs top to bottom, in the order you wrote it.

The line blurs inside a single task, though. Ansible’s network resource modules, like cisco.ios.ios_vlans, take a description of the VLANs you want and work out the commands themselves. So one task behaves declaratively. A whole playbook is still a list of steps.

Why does Terraform’s state file matter so much?

It’s Terraform’s memory. HashiCorp’s docs say Terraform “destroys resources that exist in the state but no longer exist in the configuration.” Ansible has nothing like that. It knows what you asked for on this run and what the device looks like right now. It doesn’t know what it did last Tuesday.

Sounds abstract? The lab below makes it concrete.

Terraform vs Ansible on the same lab: what actually happens?

Here’s the setup. Four Catalyst 9000v switches, sw1 to sw4, in EVE-NG or Cisco Modeling Labs. You want VLAN 10 (USERS), VLAN 20 (VOICE) and VLAN 30 (PRINTERS) on every switch. One Linux control node runs both tools. If you don’t have a lab yet, our home lab setup guide for network engineers gets one running.

How we checked it, so you know what’s real. Terraform 1.16.6 ran a real first terraform plan on this config with Cisco’s IOS XE provider (version 1.1.1). The later Terraform rows come from Terraform’s built-in terraform_data resource standing in for the switches, plus the provider’s source code. The Ansible side ran the cisco.ios collection’s own code (version 11.6.0) in check mode against a simulated show vlan, the same way the collection’s unit tests do. No live switch was involved, so treat every output block below as representative.

Sketch of one server sending a turquoise arrow labelled NETCONF and a navy arrow labelled SSH to a bracket around a column of four network switches.

Same control node, same four switches, two transports. Terraform’s IOS XE provider talks NETCONF over SSH on TCP 830 and needs netconf-yang on each switch. Ansible’s network_cli sends plain CLI over SSH on TCP 22.

The Terraform version

Cisco’s provider talks NETCONF over SSH, on port 830 by default. So each switch needs one line before Terraform can reach it:

netconf-yang

Then the config. setproduct pairs every switch with every VLAN, so 4 switches times 3 VLANs gives 12 resources:

terraform {
  required_providers {
    iosxe = {
      source  = "CiscoDevNet/iosxe"
      version = "1.1.1"
    }
  }
}

provider "iosxe" {
  username = "admin"
  password = var.switch_password
  devices = [
    { name = "sw1", host = "10.10.10.11" },
    { name = "sw2", host = "10.10.10.12" },
    { name = "sw3", host = "10.10.10.13" },
    { name = "sw4", host = "10.10.10.14" },
  ]
}

variable "switch_password" {
  type      = string
  sensitive = true
}

locals {
  switches = ["sw1", "sw2", "sw3", "sw4"]
  vlans    = { "10" = "USERS", "20" = "VOICE", "30" = "PRINTERS" }

  # one entry per switch and VLAN pair: 4 x 3 = 12
  pairs = {
    for p in setproduct(local.switches, keys(local.vlans)) :
    "${p[0]}-vlan${p[1]}" => { device = p[0], vlan_id = p[1] }
  }
}

resource "iosxe_vlan" "lab" {
  for_each = local.pairs
  device   = each.value.device
  vlan_id  = each.value.vlan_id
  name     = local.vlans[each.value.vlan_id]
}

On a fresh lab, terraform plan lists all 12 and sums them up:

  # iosxe_vlan.lab["sw1-vlan10"] will be created
  # iosxe_vlan.lab["sw1-vlan20"] will be created
  ...
  # iosxe_vlan.lab["sw4-vlan30"] will be created

Plan: 12 to add, 0 to change, 0 to destroy.

You read that plan before anything touches a switch. That’s the habit Terraform teaches, and honestly it’s worth learning for its own sake.

The Ansible version

The inventory tells Ansible these are Cisco IOS devices reached over CLI and SSH:

switches:
  hosts:
    sw1: { ansible_host: 10.10.10.11 }
    sw2: { ansible_host: 10.10.10.12 }
    sw3: { ansible_host: 10.10.10.13 }
    sw4: { ansible_host: 10.10.10.14 }
  vars:
    ansible_connection: ansible.netcommon.network_cli
    ansible_network_os: cisco.ios.ios
    ansible_user: admin

The playbook is one task:

- name: Lab VLANs on all four switches
  hosts: switches
  gather_facts: false
  tasks:
    - name: Make sure VLANs 10, 20 and 30 exist
      cisco.ios.ios_vlans:
        config:
          - vlan_id: 10
            name: USERS
          - vlan_id: 20
            name: VOICE
          - vlan_id: 30
            name: PRINTERS
        state: merged

On a switch with none of these VLANs, the module turns that into six CLI lines:

vlan 10
name USERS
vlan 20
name VOICE
vlan 30
name PRINTERS

And the recap at the end of the first run looks like this (representative):

PLAY RECAP *********************************************************************
sw1 : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
sw2 : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
sw3 : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
sw4 : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

One task per switch, so ok=1. A changed task counts as ok too, which looks odd the first time you see it.

Notice what the switches didn’t need. No Python, no NETCONF, no agent. Ansible’s network modules run on the control node and send plain CLI over SSH. Keep that in mind for later.

What changes on run two, after drift, and after you delete VLAN 30?

Here’s where it gets interesting.

What you doTerraformAnsible (state: merged)
Run it again, nothing changedNo changes. Your infrastructure matches the configuration.changed=0 on all 4 switches
Someone types no vlan 30 on sw2Plan shows 1 to add. VLAN 30 comes back on sw2sw2 shows changed=1 and gets vlan 30 and name PRINTERS again. The other 3 stay at changed=0
You delete VLAN 30 from your codePlan: 0 to add, 0 to change, 4 to destroy. VLAN 30 leaves all 4 switcheschanged=0. VLAN 30 stays on all 4 switches
VLANs left on the 4 switches812

The first two rows are a draw. Both tools are idempotent, and both put a hand-deleted VLAN back on the next run.

The third row is the whole debate in one line. Terraform remembers it created VLAN 30 on four switches, so taking it out of the code takes it off the network. Ansible’s merged state only adds what you list. Remove VLAN 30 from the list and Ansible just stops mentioning it. You’re left with four VLAN 30s that nobody’s code describes any more.

Is that a bug? No. It’s the design. Ansible keeps no record of what it did, so it can’t know VLAN 30 was its idea.

Terraform vs Ansible after a VLAN is deleted from code, as a two-panel sketch. Under TERRAFORM, a switch with two flags and an X where a third stood. Under ANSIBLE, a switch with three flags, the third circled in turquoise and labelled ORPHAN.

Delete VLAN 30 from your code. Terraform plans 4 to destroy and leaves 8 VLANs across the 4 switches. Ansible’s merged state reports changed=0 and leaves all 12, including 4 orphan copies of VLAN 30.

So how does Ansible remove a VLAN?

You ask it to, explicitly. The ios_vlans module has several states, and they don’t all do what their names suggest. Here’s what each one planned in our check-mode runs against cisco.ios 11.6.0:

  • purged, with VLAN 30 listed, sends no vlan 30. That’s the clean removal.
  • deleted, with VLAN 30 listed, doesn’t remove the VLAN at all. It sends no name PRINTERS, no state active and shutdown. VLAN 30 stays, nameless and shut down.
  • overridden, with only VLANs 10 and 20 listed, removes VLAN 30. But list only the names and it also sends shutdown under VLANs 10 and 20, because overridden resets every attribute you leave out. Add state: active and shutdown: disabled to each VLAN and that goes away. It also tries no vlan 1 and no vlan 1002 to 1005. Ansible’s own docs example shows that same no vlan 1 line, with VLAN 1 still there afterwards.
Two-panel sketch. Under PURGED, a switch with only a dashed outline where a flag stood. Under DELETED, a switch still flying a blank flag outlined in turquoise with a power-off symbol on it.

In check mode against cisco.ios 11.6.0, purged sent no vlan 30. deleted sent no name PRINTERS, no state active and shutdown, so VLAN 30 stayed on the switch.

Ansible’s documentation is blunt about overridden: “Use caution with this state as you could remove your access to the device.” So here’s the rule. Run any state other than merged with --check first, read the commands, then run it for real.

Terraform vs Ansible: which comes first on your track?

It depends on the exam and the job you’re aiming at. Here’s the rule we’d give a friend.

You areLearn firstFirst exam that tests it
Network engineer, CCNA level or heading for CCNA AutomationAnsibleCCNA Automation (200-901)
Cloud or platform engineer, AWS or AzureTerraformHashiCorp Terraform Associate (004)
Linux adminAnsibleRed Hat Certified Engineer in Ansible (EX200 + EX294)
Not sure yetAnsibleNone needed yet

Why Ansible when you’re unsure? It works against things you already have, like a Linux VM or a lab switch. Terraform needs something to create things in, like a cloud account or a platform with a provider, before it’s useful.

Sketch of a signpost with two right-pointing arms: NETWORK leads to a task list beside a switch, CLOUD leads to building blocks inside a cloud.

Network track: Ansible first, and CCNA Automation (200-901) asks you to read playbooks. Cloud track: Terraform first, with Terraform Associate (004) as the first exam. Linux admins follow the network path.

Network engineers: start with Ansible

Three reasons, and you can check every one of them.

  1. Your exam reads Ansible. The CCNA Automation (200-901) exam topics give Infrastructure and Automation 20% of the exam. Objective 5.6 asks you to describe “the capabilities of automation tools such as Ansible, Terraform, and Cisco NSO.” Objective 5.8 goes further for one of them: “Interpret the workflow being automated by an Ansible playbook.” Terraform gets a description. Ansible gets read line by line.
  2. It works on the gear in your lab today. Ansible’s network modules run on the control node, and the most common connection is CLI over SSH. The switch needs SSH and nothing else.
  3. Terraform asks more of the device. Cisco’s IOS XE provider uses NETCONF, so every switch needs netconf-yang and an IOS XE release. The provider’s own README lists Catalyst 8000v 17.15.3 and Catalyst 9000v 17.15.1 as the tested releases. If your lab runs older images, check NETCONF support before you plan around Terraform.

Once a static inventory gets painful, build a NetBox dynamic inventory for Ansible. Add the scripting side with 10 Python scripts for Cisco devices and our guide to the Python automation skills behind 200-901. Then add Terraform. Thinking about it as a career move? Our network automation engineer salary guide shows which skills pay.

Cloud and platform engineers: start with Terraform

If your work means creating cloud resources (networks, subnets, instances, managed databases), Terraform is built for exactly that job. The first exam is short and cheap. HashiCorp’s Terraform Associate (004) costs $70.50 USD plus tax, runs one hour, is multiple choice and online proctored, tests Terraform 1.12, and stays valid for two years.

Its objectives double as a study plan: providers, the init, plan, apply and destroy workflow, state and drift, modules, and HCP Terraform. Keep our Terraform commands cheat sheet open while you practise.

Linux admins: start with Ansible

Red Hat’s automation credential is now called Red Hat Certified Engineer in Ansible. It takes two exams: EX200, the RHCSA, and EX294, which Red Hat titles the “Red Hat Certified Advanced System Administrator in Ansible exam.” EX294 is hands-on. You write playbooks, and Red Hat checks the result on freshly installed systems.

That’s a bigger lift than a one-hour multiple-choice test. But if you already run Linux boxes, Ansible pays off on day one, and this path builds straight on the RHCSA you may already be studying for. When you can write a playbook without looking things up, test yourself with our 120 Ansible interview questions.

When to use Ansible vs Terraform

Here’s the one-line test. Use Terraform when something needs to exist. Use Ansible when something that exists needs to change. In practice:

  • Provisioning on AWS or Azure: Terraform. It plans the change, shows you the count, and tracks what it built.
  • Configuring servers after they boot: Ansible. Packages, users, files and services are its home turf.
  • Network automation on existing switches and routers: Ansible, for most labs and brownfield networks. Terraform when your platform has a solid provider and you want the network in the same plan-and-apply workflow as your cloud.
  • Homelab VMs, on Proxmox or anywhere else: both. Terraform creates the VMs, Ansible sets them up. Our Proxmox vs Docker comparison helps you pick the platform underneath.
  • One-off fixes across many hosts: Ansible. An ad hoc command or a short playbook beats writing infrastructure code for a five-minute job. Our Ansible ad hoc commands cheat sheet lists them.

Can each tool do the other’s job? A bit. Ansible has cloud modules that create instances, and Terraform has provisioners that run commands on a new machine. HashiCorp itself says you “should exhaust all alternatives before using provisioners.” Take the hint.

Can you use Terraform and Ansible together?

Yes, and it’s the most common pattern: Terraform builds, Ansible configures. The hard part used to be the handoff. Terraform knows the new machines’ IP addresses, and Ansible needs them in an inventory.

How does the Terraform Ansible provider work?

The Terraform Ansible provider (ansible/ansible, version 1.5.0) closes that gap. It gives Terraform four resources: ansible_host, ansible_group, ansible_playbook and ansible_vault. Register each new machine as a host:

resource "ansible_host" "web" {
  name   = aws_instance.web.public_ip
  groups = ["web"]
  variables = {
    ansible_user = "ubuntu"
  }
}

Then point Ansible at Terraform’s state with the cloud.terraform collection’s inventory plugin. Save this as terraform_provider.yml:

plugin: cloud.terraform.terraform_provider

Run ansible-playbook -i terraform_provider.yml site.yml from the Terraform project directory, and every host Terraform created shows up in the right group. No IP addresses copied by hand.

Sketch flow from left to right: a blueprint, an arrow labelled BUILD to a new server, a turquoise arrow to a checklist clipboard, and an arrow labelled CONFIGURE to the server with a wrench.

Terraform builds the machine and records it as an ansible_host resource. The cloud.terraform inventory plugin reads that from state, so Ansible configures the right hosts without a hand-written inventory.

How do Terraform and Ansible compare to CloudFormation, Kubernetes, Puppet and Chef?

Terraform vs CloudFormation

AWS CloudFormation is AWS’s own provisioning service. You write a template, and AWS creates the resources as a stack that you can update or delete as one unit. It’s built around AWS resources. Terraform does the same job through providers, so one workflow covers AWS, Azure and the Cisco switches from the lab above.

All-AWS shop with no plans to change? CloudFormation is a fair choice. Anything multi-cloud or hybrid? Terraform.

Terraform vs Kubernetes

Different layers, so it’s rarely either-or. Kubernetes runs containers. Terraform can create the cluster Kubernetes runs on. Terraform vs Helm and Terraform vs Docker work out the same way: Docker builds and runs container images, Helm installs packaged apps into a Kubernetes cluster, and Terraform builds the infrastructure under both. A typical stack uses all three.

Ansible vs Puppet vs Chef

All three handle configuration management. The big difference is the agent. With Puppet, the primary server compiles a catalog for each node and a Puppet agent on that node applies it. Chef works the same way: Chef Infra Client is an agent on every node, pulling cookbooks from Chef Infra Server. Ansible pushes from one control node over SSH and installs no agent on the target.

For a learner, that settles Ansible vs Chef and Ansible vs Puppet. You can automate a lab switch with Ansible this afternoon. Puppet and Chef assume you can run their agent on every target, which fits server fleets you fully control. Chef vs Puppet is mostly a question of which one your employer already runs.

Where do Pulumi, OpenTofu and Ansible Tower fit?

  • Pulumi does what Terraform does, but you write it in TypeScript, JavaScript, Python, Go, .NET, Java or YAML (it accepts HCL too). Good fit if you’re a developer first.
  • OpenTofu is the fork of Terraform that started after HashiCorp’s licence switch. It’s MPL-2.0, run as a Linux Foundation project, and its latest release is 1.13.1. It reads state files from Terraform up to 1.5.x and works with Terraform providers through its own registry. Learn Terraform and you can read OpenTofu configs on day one.
  • Ansible Tower is now called automation controller, part of Red Hat Ansible Automation Platform. It runs and governs playbooks centrally. So Ansible Tower vs Terraform compares a control plane for Ansible with a provisioning tool. They don’t compete.

Is Terraform still open source after IBM bought HashiCorp?

Not by the strict definition. Since version 1.6.0, released 2023-10-04, Terraform ships under the Business Source License, and the licence file now names IBM as the licensor. IBM completed its HashiCorp purchase on 2025-02-27. You can still use Terraform free in production. The licence’s extra grant allows that, unless you offer Terraform as a hosted or embedded product that competes with IBM’s paid versions.

Ansible stays GPL-3.0. Red Hat has been part of IBM since 2019-07-09, so one company now owns both tools.

For a learner, none of this changes the answer. If your employer moved to OpenTofu, your Terraform skills come with you.

Frequently asked questions

Can Ansible replace Terraform?

For small jobs, yes. Ansible has cloud modules, like amazon.aws.ec2_instance, that create and manage instances. What it can’t do is remember them. Delete a VLAN or a VM from a playbook and it stays put. Delete it from Terraform code and Terraform removes it. For anything you’ll create and tear down more than once, use Terraform.

Is Ansible easier to learn than Terraform?

For most beginners, yes. A playbook is YAML that reads top to bottom, and you can point it at a lab switch over SSH right away. Terraform asks you to learn HCL, providers and state before your first apply means much. Neither is hard. Terraform just has more concepts on day one.

Is Terraform or Ansible better for network automation?

Ansible, for most labs and existing networks. It speaks CLI over SSH, NETCONF and HTTP APIs, and it runs on the control node, so the device needs nothing extra. Terraform shines where a good vendor provider exists, like Cisco’s IOS XE provider, and you want the network managed with the same plan-and-apply workflow as your cloud.

Do I need Python to use Ansible?

On the control node, yes. Ansible is written in Python, and ansible-core 2.21 needs Python 3.12 or newer. On Windows, you’ll run the control node inside WSL. Linux targets need Python too, but network devices don’t. And you don’t need to write any Python to write playbooks.

Should I get Terraform Associate or the Red Hat Ansible certification first?

Terraform Associate is the quicker win: one hour, multiple choice, $70.50 USD. The Red Hat path needs the RHCSA plus the hands-on EX294. Network engineers have a third option that covers both tools: CCNA Automation (200-901), where Infrastructure and Automation is 20% of the exam.

Bottom line

  • Terraform creates and destroys. Ansible configures. Terraform remembers, Ansible checks.
  • Network engineer or Linux admin? Ansible first, then Terraform. Cloud engineer? The other way round.
  • Run any Ansible state other than merged with --check before you run it for real.

Want to practise both on real labs? The DevOps Workbook covers Terraform and Ansible alongside Git, CI/CD, Docker and Kubernetes. On the network track, the CCNA Automation 200-901 workbook works through Python, APIs and Ansible against the exam blueprint. Just want the commands on one page? Grab the free Terraform commands cheat sheet and the free Ansible commands cheat sheet.

Keep Reading

Related Articles

Technician handling network switch ports in a data center.

Spanning Tree Protocol Explained: How STP Stops Switching Loops

Spanning tree protocol stops switching loops by blocking redundant links. Root bridge election, BPDUs, port states, RSTP vs MSTP, and a three-switch lab you can build tonight.
Fibre optic patch panel cabling in a data centre

NetBox Ansible: How to Build a Dynamic Inventory That Actually Stays Accurate

Set up a NetBox Ansible dynamic inventory that stays accurate: the exact nb_inventory config, the primary-IP gotcha, and fixes for the top inventory errors.
A Linux network configuration file open on a screen, showing an ethernet interface set to dhcp4 true, with SMEnode Labs cyan fading in from the bottom-left corner.

What Is APIPA? Why You’re Getting a 169.254 Address

What is APIPA? Why your PC picked a 169.254 address, the exact RFC 3927 range, how ARP probes work, a 7-step fix order and a CCNA lab to break on purpose.

Share Your Valuable Opinions