6.7 Agent-Based and Agentless Orchestration Tools¶
Blueprint 6.7: Compare agent vs. agentless orchestration tools, such as Chef, Puppet, Ansible, and SaltStack
- Agent-based versus agentless: what runs on the managed node, who starts the connection, push versus pull
- Puppet, Chef, SaltStack and Ansible: architecture, language, transport, terminology
- Declarative versus procedural definitions, idempotence and configuration drift
- Why Ansible is the usual choice for network devices
The verb is compare. Expect drag-and-drop items that match each tool to its model, language or component names, and multiple-choice items about which tool fits a scenario. You will not be asked to write a playbook on the exam, but reading one makes the comparison concrete.
Why configuration management¶
Configuration management tools describe the desired state of many systems in text files, keep those files in version control, and apply them consistently. They replace logging in to devices one by one. The benefits are the same whichever tool you use: repeatable changes, fewer typing errors, a record of who changed what (in Git), and the ability to detect and correct configuration drift, where a device has been changed by hand away from the intended state.
A key property is idempotence: applying the same definition twice produces the same result, and the second run changes nothing because the system is already in the desired state. Well-written modules check before they change.
Agent-based versus agentless¶
graph TB
subgraph AB ["Agent-based, typically pull"]
S1["Server<br/>Puppet server or Chef Infra Server"]
A1["Agent on node 1"] -->|"polls every 30 min,<br/>fetches its config"| S1
A2["Agent on node 2"] -->|"polls"| S1
end
subgraph AL ["Agentless, push"]
CN["Ansible control node"] -->|"SSH or API, on demand"| N1["Router"]
CN -->|"SSH or API"| N2["Switch"]
end
| Agent-based | Agentless | |
|---|---|---|
| Software on the managed node | An agent (daemon) must be installed and kept updated | Nothing extra; uses SSH, NETCONF or an HTTPS API the device already has |
| Who starts the connection | Usually the agent (pull) | The control node (push) |
| When changes apply | On the agent's schedule, or when triggered | When an operator or pipeline runs the job |
| Drift correction | Automatic on every agent run | Only when the job is run again |
| Scale | Very large fleets; the agents do the work | Large, but the control node opens every connection |
| Network devices | Hard: most routers and switches cannot run a third-party agent | Natural fit: SSH and APIs are already there |
| Examples | Puppet, Chef, SaltStack (minion) | Ansible, SaltStack (salt-ssh), Puppet Bolt |
Exam trap
Push versus pull is not the same question as agent versus agentless, but the exam pairs them. Puppet and Chef: agent, pull. Ansible: agentless, push. SaltStack: agent (minion), with the master pushing commands over a message bus, plus an agentless SSH mode. If an option says Ansible "requires an agent on each switch", it is wrong.
The four tools¶
Puppet¶
| Item | Detail |
|---|---|
| Model | Agent-based, pull. The Puppet agent contacts the Puppet server (formerly "master") on TCP 8140, by default every 30 minutes |
| Language | Puppet DSL (a Ruby-based declarative language). Files are manifests (.pp), grouped into modules |
| How it works | The agent sends facts about the node (gathered by Facter); the server compiles a catalog of desired state for that node; the agent applies it and reports back |
| Style | Declarative: you describe the end state, Puppet works out the order from dependencies |
| Network devices | Through a proxy model (puppet device), where a proxy agent manages devices that cannot run an agent |
node 'ntp-client.example.com' {
package { 'chrony':
ensure => installed,
}
service { 'chronyd':
ensure => running,
enable => true,
require => Package['chrony'],
}
}
Chef¶
| Item | Detail |
|---|---|
| Model | Agent-based, pull. Chef Infra Client (chef-client) on each node pulls from the Chef Infra Server over HTTPS |
| Language | Ruby DSL. Instructions are recipes, packaged in cookbooks; resources such as package and service |
| Workstation | Engineers write and test cookbooks on a workstation and upload them with the knife tool |
| Style | Usually described as procedural (imperative): resources in a recipe run in the order written |
SaltStack (Salt)¶
| Item | Detail |
|---|---|
| Model | Agent-based. A Salt master controls minions over a ZeroMQ message bus on TCP 4505 (publish) and 4506 (return). The master pushes commands; minions can also apply states on a schedule |
| Language | State files (SLS) in YAML, templated with Jinja. Written in Python |
| Terminology | Grains: facts about a minion. Pillar: secure per-minion data from the master. Reactor: actions triggered by events on the bus |
| Agentless options | salt-ssh runs over SSH with no minion; proxy minions manage network devices that cannot run a minion |
| Style | Declarative states plus imperative remote execution (salt '*' cmd.run ...) |
Ansible¶
| Item | Detail |
|---|---|
| Model | Agentless, push. A control node (Linux with Python) connects to managed nodes when a job runs |
| Transport | SSH for servers; for network devices, connection plugins such as network_cli (SSH to the CLI), netconf, and httpapi (REST APIs) |
| Language | Playbooks in YAML. A playbook contains plays; a play targets hosts from the inventory and runs tasks; each task calls a module. Templates use Jinja2 |
| Reuse | Roles and collections (such as cisco.ios, cisco.nxos, cisco.dnac for Catalyst Center) |
| Style | Tasks run in order (procedural flow), but most modules are declarative and idempotent |
| Scale and GUI | AWX and Red Hat Ansible Automation Platform (formerly Ansible Tower) add a web UI, RBAC, scheduling and an API |
Side-by-side¶
| Puppet | Chef | SaltStack | Ansible | |
|---|---|---|---|---|
| Agent | Yes (agent) | Yes (chef-client) | Yes (minion), or salt-ssh | No |
| Push or pull | Pull | Pull | Push (master to minions) | Push |
| Central server | Puppet server | Chef Infra Server | Salt master | Control node (no daemon needed) |
| Definitions | Manifests | Recipes in cookbooks | States (SLS) | Playbooks |
| Language | Puppet DSL | Ruby DSL | YAML plus Jinja | YAML plus Jinja2 |
| Transport | HTTPS, TCP 8140 | HTTPS | ZeroMQ, TCP 4505/4506 | SSH, NETCONF, HTTPS APIs |
| Written in | Ruby (and Clojure for the server) | Ruby (and Erlang for the server) | Python | Python |
| Node facts | Facts (Facter) | Ohai attributes | Grains | Facts (gather_facts) |
graph LR
Q1{"Can the device run an agent?"} -->|"no: router, switch"| ANS["Ansible, or salt-ssh / proxy minion"]
Q1 -->|"yes: Linux servers"| Q2{"Need continuous drift correction?"}
Q2 -->|"yes"| PULL["Puppet or Chef agent runs"]
Q2 -->|"event-driven, fast remote exec"| SALT["SaltStack minions"]
Exam trap
Match the language to the tool carefully. Ruby: Chef recipes (and Puppet DSL is Ruby-based). YAML: Ansible playbooks and Salt states. A distractor that says "Ansible playbooks are written in Python" is wrong: Ansible itself is written in Python, but playbooks are YAML.
Exam trap
The term "manifest" belongs to Puppet, "cookbook" and "recipe" to Chef, "state", "grain" and "pillar" to Salt, and "playbook", "inventory" and "module" to Ansible. Drag-and-drop items often mix these.
Reading an Ansible playbook for IOS XE¶
---
- name: Configure NTP on IOS XE routers
hosts: routers
gather_facts: false
tasks:
- name: Add the NTP server
cisco.ios.ios_config:
lines:
- ntp server 192.168.100.10
- name: Collect NTP associations
cisco.ios.ios_command:
commands:
- show ntp associations
register: ntp_out
- name: Print the output
ansible.builtin.debug:
var: ntp_out.stdout_lines
How to read it: the play targets the routers group from the inventory. ansible_connection=ansible.netcommon.network_cli tells Ansible to log in over SSH to the CLI, and ansible_network_os picks the IOS command handling. ios_config compares the requested line with the running configuration and only sends it if it is missing, which is what makes the task idempotent. register stores a task's result in a variable for later tasks. In production the password would come from Ansible Vault, not plain text.
Lab: run an idempotent Ansible playbook¶
Topology: AUTO (Ubuntu, 192.168.100.50) as the Ansible control node, R1 (C8000v or IOSv) at 192.168.100.1 with SSH enabled.
graph LR
AUTO["AUTO<br/>Ansible control node"] -- "SSH TCP 22, 192.168.100.0/24" --- R1["R1<br/>192.168.100.1"]
-
Enable SSH on R1:
hostname R1 ip domain name lab.local crypto key generate rsa modulus 2048 username admin privilege 15 secret Cisco123! line vty 0 4 login local transport input sshConfirm from AUTO with
ssh [email protected]. -
On AUTO, install Ansible in a Python virtual environment (
python3 -m venv ~/ans && ~/ans/bin/pip install ansible paramiko). The fullansiblepackage includes thecisco.iosandansible.netcommoncollections. Save the two files above. -
Run the playbook:
$ ansible-playbook -i inventory.ini ntp.yml PLAY [Configure NTP on IOS XE routers] **************************************** TASK [Add the NTP server] ***************************************************** changed: [r1] TASK [Collect NTP associations] *********************************************** ok: [r1] TASK [Print the output] ******************************************************* ok: [r1] => { "ntp_out.stdout_lines": [ [ " address ref clock st when poll reach delay offset disp", " ~192.168.100.10 .INIT. 16 - 64 0 0.000 0.000 15937.", " * sys.peer, # selected, + candidate, - outlyer, x falseticker, ~ configured" ] ] } PLAY RECAP ******************************************************************** r1 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0The NTP output depends on whether 192.168.100.10 exists in your lab; an unreachable server shows
.INIT.and stratum 16. -
Run the same command again. The first task now reports
okinstead ofchanged, and the recap showschanged=0. That is idempotence: the line is already present, so nothing is sent. - Create drift: on R1,
no ntp server 192.168.100.10. Run the playbook a third time and confirm it reportschanged=1and restores the line. Note that an agentless tool corrects drift only when you run it; a Puppet agent would have done it on its next scheduled run. - Break it on purpose: change
ansible_passwordto a wrong value and run again. The recap showsunreachable=1(orfailed=1, depending on the version), and the error names an authentication failure.
Quick check¶
Which tool uses minions and grains?
SaltStack. Minions are the agents, and grains are facts about each minion.
A team must automate 400 switches that cannot run third-party software and wants to start without deploying a server. Which tool fits best?
Ansible, because it is agentless and runs from a control node over SSH or APIs with no permanent server component.
What is the default model for Puppet: push or pull, agent or agentless?
Agent-based pull. The Puppet agent contacts the Puppet server on TCP 8140, by default every 30 minutes.
References¶
- Ansible network automation documentation, including the
cisco.ioscollection - Puppet, Chef and Salt project documentation on their vendors' websites
- Cisco DevNet: network automation learning labs with Ansible
- Cisco Live On-Demand Library: search "Ansible network automation" and "configuration management"
Unlock the full question banks and cram sheets
A free account opens every question bank, the timed practice exams, the printable cram sheets, all the simulations and the lab files. Without one you see a 5-question sample of each.
No card needed. Study tips by email are optional and off unless you tick the box. Your address is never sold or shared. Privacy policy ยท Terms
Signed in as . Your account
You're on the free account, so lessons after the first module, question banks, exams and cram sheets show a sample. See Track Pass and All-Access
Also from NetManager Systems: NetManager, network monitoring for multi-vendor networks. Try the NetManager, NetDiag, PCAP Analyzer and OpsDash demos: every new account gets a free 7-day read-only login to each.