Most network automation tutorials start with a playbook that configures a VLAN. That’s fine, but it skips the part where you understand why Ansible is structured the way it is and when you should reach for something else.
Here’s a practical foundation.
Why Ansible for Networks?
Ansible uses an agentless model — it connects via SSH (or NETCONF, or HTTPS depending on the platform). No agent to install. For network devices, this is almost always the right model.
# inventory.yml
all:
children:
core:
hosts:
core-01:
ansible_host: 10.0.0.1
ansible_network_os: cisco.ios.ios
ansible_connection: network_cli
ansible_user: admin
A Real Task: Collecting Interface State
- name: Gather interface state from core switches
hosts: core
gather_facts: false
tasks:
- name: Collect interface data
cisco.ios.ios_facts:
gather_subset:
- interfaces
- name: Show interfaces that are up
debug:
msg: "{{ item.key }}: {{ item.value.operstatus }}"
loop: "{{ ansible_network_resources.interfaces | dict2items }}"
when: item.value.operstatus == 'up'
Templates with Jinja2
Configuration rendering is where Ansible shines for networks. Keep your logic in inventory/vars, your structure in templates.
{# templates/bgp.j2 #}
router bgp {{ bgp_asn }}
bgp router-id {{ router_id }}
bgp log-neighbor-changes
{% for peer in bgp_peers %}
neighbor {{ peer.ip }} remote-as {{ peer.asn }}
neighbor {{ peer.ip }} description {{ peer.description }}
{% endfor %}
# host_vars/core-01.yml
bgp_asn: 65001
router_id: 10.0.0.1
bgp_peers:
- ip: 10.0.0.2
asn: 65002
description: "upstream-1"
When to Use Something Else
Ansible is great for push-based config management. It’s not great for:
- Real-time state queries — use NAPALM or direct API calls
- Event-driven response — use Ansible EDA or a proper event bus
- Complex diffs with rollback — look at Nornir + NAPALM
The Mental Model
Think of Ansible for networks as: declarative intent → rendered config → pushed to device. Keep your vars clean, your templates readable, and your playbooks idempotent.