Network Automation

Ansible vs Puppet vs Chef (and Terraform) for Network Automation

An idempotent Ansible playbook run reporting changed=0 across three routers
In: Network Automation, Fundamentals, CCNA

Once you accept that configuring devices one at a time does not scale, the next question is which tool does the configuring for you. The CCNA v1.1 blueprint names three configuration-management tools (Ansible, Puppet, Chef) and, with the move toward infrastructure as code, Terraform belongs in the conversation too. This article compares all four, then shows Ansible actually running against three live Cisco IOS XE routers, including the idempotency gotcha that bites everyone the first time. It is part of the Network Automation guide.

The Four Axes That Separate Them

The tools differ along a few dimensions, and once you know the axes, the comparison writes itself.

Agent vs agentless

Does the managed device need software installed on it? Network gear usually can't run agents, so this axis decides everything.

Push vs pull

Does a central server push changes out, or do devices pull their config from a master on a schedule?

Declarative vs procedural

Do you describe the desired end state, or the steps to get there? Declarative tools converge to a state; procedural tools run steps.

Language

YAML (Ansible), a Ruby-based DSL (Puppet, Chef), or HCL (Terraform). Affects how steep the learning curve is.

The Four Tools, Compared

Ansible

Agentless, push, mostly declarative, YAML playbooks. Connects over SSH (or API). The default choice for network automation precisely because it needs nothing on the device.

Puppet

Agent-based, pull, declarative, Ruby DSL. Nodes pull their catalog from a Puppet master on a schedule. Strong for large fleets of servers.

Chef

Agent-based, pull, procedural, Ruby DSL. You write "recipes" and "cookbooks." Very flexible, steeper learning curve, developer-oriented.

Terraform

Agentless, declarative, HCL, provider model with a state file. Built for provisioning infrastructure (cloud, and increasingly network devices) rather than in-place config.

For network devices, the agent axis is decisive. A traditional switch cannot run a Puppet or Chef agent, so those tools reach network gear only through proxy modes or API integrations. Ansible, being agentless and speaking SSH natively, connects to a router the same way you do. That is why it dominates network automation, and why it is the tool worth demonstrating on real gear.

Ansible in Practice: The Inventory

Ansible needs to know what to manage and how to reach it. That lives in an inventory file. Here is the real one used to drive three lab routers from a Linux host:

[routers]
r1 ansible_host=192.168.99.1
r2 ansible_host=10.0.12.2
r3 ansible_host=10.0.13.2

[routers:vars]
ansible_network_os=cisco.ios.ios
ansible_connection=ansible.netcommon.network_cli
ansible_user=cisco
ansible_password=Cisco@123

The [routers] group lists three hosts; the [routers:vars] block tells Ansible to treat them as Cisco IOS over the network CLI connection (SSH under the hood, using the cisco.ios collection). No agent, no software on the routers, just credentials and a connection type.

Ad-Hoc: One Command, Three Routers

Before writing a playbook, an ad-hoc command proves the connection and shows the appeal of "do this everywhere at once." This runs a show command against all three routers in parallel:

j@llmbits:~/anslab$ ansible routers -i inventory.ini -m cisco.ios.ios_command \
  -a "commands='show version | include uptime'"
r1 | SUCCESS => {
    "changed": false,
    "stdout": ["R1 uptime is 21 minutes"]
}
r2 | SUCCESS => {
    "changed": false,
    "stdout": ["R2 uptime is 21 minutes"]
}
r3 | SUCCESS => {
    "changed": false,
    "stdout": ["R3 uptime is 21 minutes"]
}

Three devices, one command, real output. Note "changed": false: a show command reads state without altering it, so Ansible correctly reports nothing changed. That changed flag is the heart of the next lesson.

The Playbook and Idempotency

A playbook describes a desired state in YAML. This one standardizes logging and SNMP contact across all three routers:

---
- name: Standardize logging and SNMP contact on all routers
  hosts: routers
  gather_facts: false
  tasks:
    - name: Ensure syslog host and trap level are set
      cisco.ios.ios_config:
        lines:
          - logging host 192.168.99.100
          - snmp-server contact noc@pinglabz.com

Run it once and, as expected, it applies the config to every router:

j@llmbits:~/anslab$ ansible-playbook -i inventory.ini set_logging.yml
changed: [r1]
changed: [r2]
changed: [r3]

PLAY RECAP ****************************************************************
r1  : ok=1  changed=1  unreachable=0  failed=0
r2  : ok=1  changed=1  unreachable=0  failed=0
r3  : ok=1  changed=1  unreachable=0  failed=0

Idempotency is the promise that running the same playbook again changes nothing, because the desired state is already met. It is the single most important property of a good automation tool: you can run it repeatedly and safely, and changed=0 tells you the network already matches your intent. So the second run should report changed=0 everywhere.

The Gotcha That Bites Everyone

Except, on the first attempt, it did not. The playbook originally also pushed logging trap informational, and every single run reported changed=1, forever. The reason is a genuine trap in network automation: logging trap informational is the IOS default, so it never appears in the running-config. Ansible's ios_config checks whether each line is present in the running-config, does not find it (because defaults are hidden), and dutifully re-pushes it every run. The config is correct; the tool just can never confirm it, so it never reaches a stable state.

The fix is to stop managing lines that match a hidden default. Removing the logging trap informational line and rerunning gives the idempotent result you actually want:

PLAY RECAP ****************************************************************
r1  : ok=1  changed=0  unreachable=0  failed=0
r2  : ok=1  changed=0  unreachable=0  failed=0
r3  : ok=1  changed=0  unreachable=0  failed=0

Now the playbook is truly idempotent: the desired state is met, and Ansible confirms it with changed=0. The lesson generalizes: when a playbook reports "changed" on every run, suspect that you are managing a line the device does not store because it matches a default. This is exactly the kind of behavior you only learn by running the tool against real devices, not by reading about it.

Where Terraform Fits

Ansible, Puppet, and Chef manage the configuration inside devices that already exist. Terraform's angle is different: it provisions the infrastructure itself from declarative code (infrastructure as code), tracking what it created in a state file so it knows the difference between "make this" and "already made this." Its provider model means the same HCL workflow that spins up cloud resources can, with a network provider, provision devices and services. For the CCNA v1.1 era, the point to hold onto is that Terraform represents the declarative, state-tracked, provider-driven style of automation, and it increasingly overlaps with the network. You describe the end state; Terraform computes and applies the diff.

FAQ

Which tool should I learn first for networking?

Ansible. It is agentless (nothing to install on the router), speaks SSH, uses readable YAML, and has mature Cisco collections. It is the tool you are most likely to use on the job and the one the CCNA emphasizes.

Why does agentless matter so much for network devices?

Most switches and routers can't run third-party agent software. A tool that requires an agent (Puppet, Chef) can only reach them through workarounds, while an agentless tool (Ansible, Terraform) connects over SSH or an API the device already offers.

What does idempotent mean in one sentence?

Running the operation again produces no additional change because the system is already in the desired state, so it is safe to run repeatedly.

Is Terraform a replacement for Ansible?

No, they solve different problems. Terraform provisions and tracks infrastructure from declarative code; Ansible configures and manages the software and settings on infrastructure that exists. Many shops use both.

Key Takeaways

Compare configuration-management tools on four axes: agent vs agentless, push vs pull, declarative vs procedural, and language. Ansible wins for network devices because it is agentless and speaks SSH. Idempotency (a rerun changes nothing) is the property you want, and the classic gotcha is that ios_config re-pushes lines matching hidden IOS defaults forever, reporting changed=1 until you stop managing them. Terraform brings declarative, state-tracked infrastructure as code into the picture for the v1.1 era. Try it hands-on in the Automation Labs, and see the Network Automation guide for the full domain-6 path.

Written by
More from Ping Labz
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to Ping Labz.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.