Ansible for Network Automation
1. What Is Ansible and Why Use It for Networks?
Ansible is an open-source IT automation tool developed by Red Hat that uses a simple, human-readable language (YAML) to describe the desired state of systems and automate their configuration. In a network context, Ansible can log into routers, switches, firewalls, and wireless controllers — execute commands, push configurations, and validate state — across hundreds of devices simultaneously, without a single person typing a single CLI command.
Traditional network management is device-by-device and CLI-driven. An engineer SSHes into each device, types commands, checks the output, and moves on. This approach does not scale. Ansible replaces this with a declarative, repeatable, version-controlled approach: describe what you want, run the playbook, and Ansible ensures every device reaches the desired state — consistently and at scale.
| Traditional (Manual) Approach | Ansible Automation |
|---|---|
| SSH into each device individually | Run one playbook — Ansible connects to all devices in parallel |
| Type commands manually — prone to typos | YAML playbook defines the desired state — no manual CLI entry |
| No record of what changed or when | Playbooks stored in Git — full version control and change history |
| Config drift — devices diverge over time | Idempotent playbooks re-run regularly — enforce consistent state |
| One engineer, one device at a time | One engineer, hundreds of devices simultaneously |
Related pages: Network Automation Overview | Python for Network Automation | JSON, XML & YANG | REST API Overview | NETCONF & RESTCONF | Controller-Based Networking | Ansible IOS Configuration Lab | Postman & Ansible Lab
2. Ansible Architecture – Agentless by Design
Ansible's most important architectural characteristic is that it is agentless — the managed devices (routers, switches) do not need any special software installed. Ansible communicates with network devices over existing management protocols: SSH for Cisco IOS/NX-OS and most network operating systems, or NETCONF/REST APIs for newer platforms.
2.1 Core Components
| Component | What It Is | Where It Lives |
|---|---|---|
| Control Node | The Linux machine (or VM) where Ansible is installed and playbooks are run from; this is the "brain" — it orchestrates everything | Management server, engineer's laptop, or CI/CD pipeline runner |
| Managed Nodes | The network devices being configured — routers, switches, firewalls; no Ansible installation required here | Your network infrastructure |
| Inventory | A file (INI or YAML format) listing all managed nodes and groups; the "device database" that Ansible targets | Control node — typically hosts or inventory.yml |
| Playbook | A YAML file describing a set of tasks to perform on managed nodes; the "automation script" — what Ansible runs | Control node — typically *.yml files |
| Module | A reusable unit of automation code that performs a specific action
(e.g., configure a VLAN, push a config block); Ansible ships with
thousands and Cisco provides the cisco.ios collection |
Installed on control node — executed against managed nodes |
| Collection | A bundle of related modules, roles, and plugins; Cisco publishes
cisco.ios, cisco.nxos, cisco.asa
collections on Ansible Galaxy |
Installed on control node via ansible-galaxy |
2.2 Architecture Diagram
┌──────────────────────────────────────────────────────────────────────────┐
│ Control Node (Linux / macOS — Ansible installed here) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────────┐ │
│ │ Inventory │ │ Playbook │ │ Cisco IOS Collection │ │
│ │ hosts.yml │ │ site.yml │ │ (cisco.ios modules) │ │
│ │ [routers] │ │ - tasks: │ │ ios_config, ios_vlans, │ │
│ │ R1, R2, R3 │ │ - name: │ │ ios_hostname, ios_facts... │ │
│ └──────────────┘ └──────────────┘ └──────────────────────────────┘ │
│ │ │
│ ansible-playbook site.yml │
└───────────────────────────┼──────────────────────────────────────────────┘
│ SSH (port 22) — existing management protocol
┌───────────────┼─────────────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ R1 │ │ R2 │ ... │ SW-50 │
│ (Cisco │ │ (Cisco │ │ (Cisco │
│ IOS) │ │ IOS) │ │ IOS) │
│ │ │ │ │ │
│ No agent │ │ No agent │ │ No agent │
│ required │ │ required │ │ required │
└──────────┘ └──────────┘ └──────────┘
Ansible connects via SSH → executes tasks → collects results → reports back
3. Inventory Files
The inventory file tells Ansible which devices to target, how to connect to them, and how to organise them into logical groups. Groups allow playbooks to target specific subsets of devices — for example, running a task only against all core switches, or only against branch routers.
3.1 INI Format Inventory
# hosts (INI format — simple, widely used) [routers] R1 ansible_host=192.168.1.1 R2 ansible_host=192.168.1.2 R3 ansible_host=192.168.1.3 [switches] SW1 ansible_host=192.168.2.1 SW2 ansible_host=192.168.2.2 [core_switches] SW1 [network:children] routers switches [routers:vars] ansible_user=admin ansible_password=Cisco123 ansible_network_os=cisco.ios.ios ansible_connection=network_cli ansible_become=yes ansible_become_method=enable ansible_become_password=EnablePass
3.2 YAML Format Inventory
# inventory.yml (YAML format — more structured, preferred for complex environments)
all:
children:
routers:
hosts:
R1:
ansible_host: 192.168.1.1
R2:
ansible_host: 192.168.1.2
vars:
ansible_user: admin
ansible_password: Cisco123
ansible_network_os: cisco.ios.ios
ansible_connection: network_cli
ansible_become: yes
ansible_become_method: enable
ansible_become_password: EnablePass
switches:
hosts:
SW1:
ansible_host: 192.168.2.1
SW2:
ansible_host: 192.168.2.2
vars:
ansible_user: admin
ansible_password: Cisco123
ansible_network_os: cisco.ios.ios
ansible_connection: network_cli
3.3 Key Inventory Variables for Network Devices
| Variable | Value (Cisco IOS) | Purpose |
|---|---|---|
ansible_host |
IP address or FQDN | The management IP address Ansible connects to |
ansible_user |
admin |
SSH username for the device |
ansible_password |
Cisco123 |
SSH password (use Ansible Vault for production) |
ansible_network_os |
cisco.ios.ios |
Tells Ansible which network OS plugin to use; determines how to parse output and send commands |
ansible_connection |
network_cli |
Connection type for network devices — uses the device's CLI over SSH |
ansible_become |
yes |
Enables privilege escalation (equivalent to entering enable) |
ansible_become_method |
enable |
The method for privilege escalation on Cisco IOS devices |
ansible_become_password |
EnablePass |
The enable password for privileged EXEC mode |
4. YAML Syntax Essentials
Ansible playbooks and inventory files are written in YAML (YAML Ain't Markup Language) — a human-readable data serialisation format that uses indentation (spaces, never tabs) to represent hierarchy. Understanding YAML structure is essential for reading and writing Ansible content.
YAML Key Concepts:
# Comments start with hash
key: value # String value
number: 42 # Integer
enabled: true # Boolean (true/false, yes/no, on/off)
# List (sequence) — items preceded by dash and space
vlans:
- 10
- 20
- 30
# Dictionary (mapping) — key/value pairs
router:
hostname: R1
loopback: 1.1.1.1
# List of dictionaries (very common in Ansible tasks)
interfaces:
- name: GigabitEthernet0/1
description: Uplink to Core
enabled: true
- name: GigabitEthernet0/2
description: Server port
enabled: false
# Multi-line strings
config_block: |
interface GigabitEthernet0/1
description Uplink
ip address 10.0.0.1 255.255.255.252
no shutdown
CRITICAL YAML RULES:
✓ Indentation MUST use spaces — NEVER tabs
✓ Consistent indentation depth (2 or 4 spaces per level)
✓ Colons followed by a space: key: value (not key:value)
✓ Lists use dash-space prefix: - item (not -item)
✗ Mixing tabs and spaces breaks parsing with cryptic errors
5. Playbook Structure
A playbook is the core Ansible automation file. It defines which
devices to run against (the hosts directive), what
to do (the tasks list), and optionally variables,
handlers, and other configuration. A playbook can contain one or
more plays, each targeting a different group of hosts.
Playbook anatomy:
--- # YAML document start marker
- name: Play description # Name of the play (human-readable)
hosts: routers # Which inventory group(s) to target
gather_facts: no # For network devices: usually 'no'
connection: network_cli # Or specified in inventory
vars: # Variables for this play
ntp_server: 10.0.0.1
domain_name: corp.local
tasks: # List of tasks to execute in order
- name: Task description # Human-readable task name (shown in output)
module_name: # The Ansible module to call
parameter1: value1 # Module parameters
parameter2: value2
- name: Another task
another_module:
parameter: value
handlers: # Tasks triggered by 'notify' in other tasks
- name: Save configuration
cisco.ios.ios_command:
commands: write memory
5.1 Key Playbook Directives
| Directive | Purpose | Example |
|---|---|---|
hosts |
Specifies which inventory group or host(s) the play runs against | hosts: routers or hosts: all or
hosts: R1,SW2 |
gather_facts |
Controls whether Ansible automatically collects device facts at
play start; set to no for network devices to avoid errors |
gather_facts: no |
vars |
Defines play-level variables — reusable values referenced with
{{ variable_name }} syntax throughout the play |
vars:vlan_id: 10 |
tasks |
Ordered list of actions to perform; each task calls one module | See examples below |
when |
Conditional — task only runs when the condition is true | when: ansible_network_os == "cisco.ios.ios" |
loop |
Repeats a task for each item in a list | loop: [10, 20, 30] |
register |
Stores a task's output in a variable for use in later tasks | register: output |
notify |
Triggers a named handler when the task reports a change | notify: Save configuration |
6. Cisco IOS Modules
The cisco.ios Ansible collection provides modules
specifically designed for Cisco IOS and IOS-XE devices. Each module targets a
specific configuration area and understands the IOS data model — rather than
parsing raw CLI text, these modules interact with IOS in a structured,
idempotent way.
Install the Cisco IOS collection: ansible-galaxy collection install cisco.ios Verify installation: ansible-galaxy collection list | grep cisco
6.1 Commonly Used Cisco IOS Modules
| Module | Purpose | Key Parameters |
|---|---|---|
cisco.ios.ios_config |
Push arbitrary IOS configuration lines — the most flexible but least idempotent module; use when no specific module exists | lines, parents, before,
after, save_when |
cisco.ios.ios_command |
Run exec-mode show commands and return output; does not make configuration changes | commands, wait_for, retries |
cisco.ios.ios_facts |
Gather structured facts from a device (hostname, interfaces, version, routing table) for use in later tasks or reports | gather_subset (all, min, hardware, config) |
cisco.ios.ios_vlans |
Manage VLANs using a structured resource module — idempotent; can create, modify, and delete VLANs with VLAN name and state | config (list of VLAN dicts with name/vlan_id),
state (merged/replaced/deleted) |
cisco.ios.ios_interfaces |
Manage interface parameters — description, enabled state, speed, duplex | config (list of interface dicts), state |
cisco.ios.ios_l3_interfaces |
Configure Layer 3 IP addresses on interfaces | config with ipv4 address list |
cisco.ios.ios_banner |
Configure login banners (MOTD, login, exec) | banner (motd/login/exec), text,
state |
cisco.ios.ios_ntp_global |
Configure NTP servers and settings — see NTP Configuration for the manual CLI equivalent | config with servers list |
cisco.ios.ios_hostname |
Set the device hostname (IOS-XE 17.3+ resource module) | config.hostname, state |
ios_vlans and ios_interfaces are
idempotent — they understand the current state of the device and only
make changes when needed. ios_config is more like raw CLI
automation — it pushes lines without necessarily understanding the existing
config. Prefer resource modules when available; use ios_config
as a fallback for features that do not yet have a dedicated module.
7. Idempotency in Ansible
Idempotency is one of the most important concepts in Ansible. An idempotent operation produces the same result whether it is run once or a hundred times — it only makes a change when the current state differs from the desired state. If the desired state already exists, no change is made and no configuration is pushed.
Idempotency in Practice: Desired state (playbook): VLAN 10 named "Sales" should exist Run 1: VLAN 10 does not exist → Ansible CREATES it (changed = yes) Run 2: VLAN 10 already exists with name "Sales" → NO CHANGE (changed = no) Run 3: VLAN 10 exists but name is wrong → Ansible UPDATES name (changed = yes) Run 4: VLAN 10 exists with correct name → NO CHANGE (changed = no) Benefit: ┌──────────────────────────────────────────────────────────────────────┐ │ You can run the SAME playbook repeatedly without fear of breaking │ │ things or creating duplicate configuration. Idempotent playbooks │ │ are safe to schedule, automate, and run as compliance checks. │ └──────────────────────────────────────────────────────────────────────┘ ios_config vs resource modules: ios_config: NOT fully idempotent — pushes lines even if already present ios_vlans: FULLY idempotent — checks state before making changes ios_interfaces: FULLY idempotent — compares current vs desired state
8. Worked Example 1 – Configure Hostname and Domain Name
# configure-hostname.yml
---
- name: Set hostname and domain name on all routers
hosts: routers
gather_facts: no
vars:
domain: corp.local
tasks:
- name: Set device hostname using ios_config
cisco.ios.ios_config:
lines:
- hostname {{ inventory_hostname }}
- ip domain-name {{ domain }}
- name: Verify hostname (optional check)
cisco.ios.ios_command:
commands:
- show running-config | include hostname
register: hostname_output
- name: Display hostname output
debug:
var: hostname_output.stdout_lines
# Run with: ansible-playbook configure-hostname.yml -i hosts
# What happens:
# For each host in the [routers] group:
# 1. Ansible SSHes to the device (using credentials in inventory)
# 2. Enters privileged EXEC mode (enable)
# 3. Enters global config mode (conf t)
# 4. Pushes: hostname R1 (using inventory_hostname as the value)
# ip domain-name corp.local
# 5. Exits config mode
# 6. Runs 'show running-config | include hostname' and stores output
# 7. Displays the stored output in the playbook results
9. Worked Example 2 – Configure VLANs
The cisco.ios.ios_vlans resource module is the preferred way to
manage VLANs at scale. See also
VLAN Creation and Management
for the manual CLI equivalent.
# configure-vlans.yml
---
- name: Configure VLANs on all switches
hosts: switches
gather_facts: no
tasks:
# ── Method 1: Resource module (idempotent, preferred) ──
- name: Create VLANs using ios_vlans resource module
cisco.ios.ios_vlans:
config:
- name: Management
vlan_id: 10
state: active
- name: Sales
vlan_id: 20
state: active
- name: Engineering
vlan_id: 30
state: active
- name: Voice
vlan_id: 40
state: active
state: merged
notify: Save configuration
# ── Method 2: ios_config with loop (flexible fallback) ──
# Uncomment this block if ios_vlans is not available:
#
# - name: Create VLANs using ios_config
# cisco.ios.ios_config:
# lines:
# - name {{ item.name }}
# parents: vlan {{ item.id }}
# loop:
# - { id: 10, name: Management }
# - { id: 20, name: Sales }
# - { id: 30, name: Engineering }
# - { id: 40, name: Voice }
- name: Verify VLANs exist
cisco.ios.ios_command:
commands:
- show vlan brief
register: vlan_output
- name: Display VLAN table
debug:
var: vlan_output.stdout_lines
handlers:
- name: Save configuration
cisco.ios.ios_command:
commands:
- write memory
# Run with: ansible-playbook configure-vlans.yml -i hosts
# Expected output (abbreviated):
# TASK [Create VLANs using ios_vlans resource module]
# changed: [SW1]
# changed: [SW2]
#
# TASK [Verify VLANs exist]
# ok: [SW1]
# ok: [SW2]
#
# TASK [Display VLAN table]
# SW1: |
# VLAN Name Status Ports
# ---- ------------ --------- ------
# 10 Management active
# 20 Sales active
# 30 Engineering active
# 40 Voice active
10. Worked Example 3 – Gather and Display Device Facts
# gather-facts.yml
---
- name: Gather device facts from all network devices
hosts: network
gather_facts: no
tasks:
- name: Gather IOS facts
cisco.ios.ios_facts:
gather_subset:
- min ! hostname, version, serial number
# - all ! adds interfaces, routing, config (slower)
- name: Display device summary
debug:
msg: |
Device: {{ inventory_hostname }}
Hostname: {{ ansible_net_hostname }}
IOS Version: {{ ansible_net_version }}
Model: {{ ansible_net_model }}
Serial: {{ ansible_net_serialnum }}
- name: Save facts to a file (optional)
copy:
content: "{{ ansible_facts | to_nice_yaml }}"
dest: "./facts/{{ inventory_hostname }}_facts.yml"
delegate_to: localhost
# Useful ansible_net_* variables populated by ios_facts:
# ansible_net_hostname — configured hostname
# ansible_net_version — IOS software version
# ansible_net_model — hardware model (e.g., ISR4331)
# ansible_net_serialnum — device serial number
# ansible_net_interfaces — dict of all interfaces and their state
# ansible_net_all_ipv4_addresses — all IPv4 addresses on the device
11. Running Playbooks and Useful Commands
# Run a playbook:
ansible-playbook site.yml -i hosts
# Run with increased verbosity (useful for debugging):
ansible-playbook site.yml -i hosts -v ! 1 level verbose
ansible-playbook site.yml -i hosts -vvv ! 3 levels — connection detail
# Dry run — check mode (shows what WOULD change, makes no changes):
ansible-playbook site.yml -i hosts --check
# Limit to specific hosts or groups:
ansible-playbook site.yml -i hosts --limit R1
ansible-playbook site.yml -i hosts --limit routers
# Run specific tags only:
ansible-playbook site.yml -i hosts --tags vlans
# Prompt for SSH password at runtime (avoids plain text in inventory):
ansible-playbook site.yml -i hosts --ask-pass --ask-become-pass
# Test connectivity to all devices (ad-hoc ping):
ansible all -i hosts -m ping
# Run a single ad-hoc command (no playbook):
ansible routers -i hosts -m cisco.ios.ios_command \
-a "commands='show version'"
# List all hosts in inventory:
ansible-inventory -i hosts --list
# Encrypt sensitive data with Ansible Vault:
ansible-vault encrypt_string 'Cisco123' --name 'ansible_password'
11.1 Ansible Vault – Protecting Credentials
Storing passwords in plain text in inventory files is a serious security risk. Ansible Vault encrypts sensitive values so they can be safely stored in version control (Git) without exposing credentials. Ensure the devices themselves also use strong SSH credentials — see SSH Configuration and Login Security.
# Create an encrypted variable file:
ansible-vault create secrets.yml
# Encrypt an existing file:
ansible-vault encrypt inventory/group_vars/all.yml
# Run playbook with vault password:
ansible-playbook site.yml -i hosts --vault-password-file vault.pass
# OR
ansible-playbook site.yml -i hosts --ask-vault-pass
# Reference vault-encrypted vars in inventory:
# group_vars/routers.yml (encrypted with vault)
ansible_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
6132633362623432643637313934...
12. Ansible vs Other Automation Tools
| Feature | Ansible | Puppet | Chef | Python Scripts |
|---|---|---|---|---|
| Architecture | Agentless (SSH/API) | Agent-based (pull model) | Agent-based (pull model) | Custom — any method |
| Language | YAML (declarative) | Puppet DSL (declarative) | Ruby DSL (procedural) | Python (procedural) |
| Network device support | Excellent — cisco.ios, nxos, asa, junos, f5, palo alto | Limited — primarily server OS | Limited — primarily server OS | Excellent — Netmiko, NAPALM, RESTCONF, full control |
| Learning curve | Low — YAML is readable; no programming required | Medium — Puppet DSL requires learning | High — Ruby knowledge needed | Medium-High — requires Python knowledge |
| Idempotency | Yes (resource modules) / Partial (ios_config) | Yes — enforces desired state on every run | Yes — converges to desired state | Only if the developer codes it |
| Best for networks | Configuration management, compliance, multi-vendor | Server infrastructure; not ideal for network OSes | Server infrastructure; not ideal for network OSes | Custom scripting, complex logic, one-off tasks — see Python Netmiko Lab |
13. Key Terms Quick Reference
| Term | Definition |
|---|---|
| Ansible | An open-source, agentless IT automation platform by Red Hat; uses YAML playbooks to configure systems via SSH or APIs |
| Agentless | Ansible requires no software to be installed on managed nodes — it connects via SSH (or APIs) using existing protocols; ideal for network OSes that cannot run arbitrary software |
| Control Node | The Linux/macOS machine where Ansible is installed; where playbooks are written and executed from |
| Managed Node | The network device (router, switch) being configured by Ansible; requires no Ansible installation |
| Inventory File | A file (INI or YAML) listing all managed devices and groups; defines connection variables (IP, credentials, OS type) for each device or group |
| Playbook | A YAML file containing one or more plays; each play targets a group of hosts and defines an ordered list of tasks to execute |
| Play | A section of a playbook that maps a set of tasks to a specific group of hosts; a single playbook can contain multiple plays targeting different groups |
| Task | A single action within a play that calls one Ansible module with specific parameters; tasks are executed in order |
| Module | A reusable unit of code that performs a specific action (e.g., configure a VLAN, run a show command); Ansible provides thousands of modules including the Cisco IOS collection |
| Collection | A distributable bundle of modules, roles, and plugins; Cisco publishes cisco.ios, cisco.nxos, cisco.asa collections via Ansible Galaxy |
| YAML | YAML Ain't Markup Language — a human-readable data format using indentation to define structure; the language of Ansible playbooks and inventory; spaces only, no tabs |
| Idempotency | A property of Ansible resource modules where running the same playbook multiple times produces the same result — changes are only made when the current state differs from the desired state |
| ios_config | The most flexible Cisco IOS module — pushes raw config lines to a device; useful for features without a dedicated module but less idempotent than resource modules |
| ios_vlans | A Cisco IOS resource module for VLAN management; fully idempotent — compares desired vs current state and only makes necessary changes |
| Ansible Vault | Ansible's built-in encryption feature for protecting sensitive data (passwords, keys) in playbooks and inventory files so they can be safely stored in version control |
| network_cli | The Ansible connection type used for network devices; establishes an SSH session and interacts with the device CLI, enabling persistent connection and privilege escalation |