Network Automation with Ansible: A Practical Starting Point
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.
1# inventory.yml
2all:
3 children:
4 core:
5 hosts:
6 core-01:
7 ansible_host: 10.0.0.1
8 ansible_network_os: cisco.ios.ios
9 ansible_connection: network_cli
10 ansible_user: admin
A Real Task: Collecting Interface State
1- name: Gather interface state from core switches
2 hosts: core
3 gather_facts: false
4
5 tasks:
6 - name: Collect interface data
7 cisco.ios.ios_facts:
8 gather_subset:
9 - interfaces
10
11 - name: Show interfaces that are up
12 debug:
13 msg: "{{ item.key }}: {{ item.value.operstatus }}"
14 loop: "{{ ansible_network_resources.interfaces | dict2items }}"
15 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.
1{# templates/bgp.j2 #}
2router bgp {{ bgp_asn }}
3 bgp router-id {{ router_id }}
4 bgp log-neighbor-changes
5{% for peer in bgp_peers %}
6 neighbor {{ peer.ip }} remote-as {{ peer.asn }}
7 neighbor {{ peer.ip }} description {{ peer.description }}
8{% endfor %}
1# host_vars/core-01.yml
2bgp_asn: 65001
3router_id: 10.0.0.1
4bgp_peers:
5 - ip: 10.0.0.2
6 asn: 65002
7 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.