AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

Ansible for Cisco: Network Automation with IOS, NX-OS, and ASA

By Luca Berton · Published 2024-01-01 · Category: installation

Automate Cisco network devices with Ansible. Configure IOS routers and switches, NX-OS data center switches, ASA firewalls.

Why Ansible for Cisco Network Automation?

Network engineers manage hundreds or thousands of Cisco devices — routers, switches, firewalls, wireless controllers. Manual CLI configuration is slow, error-prone, and doesn't scale. A single misconfigured ACL can take down production traffic.

Ansible automates Cisco devices without installing agents. It connects via SSH (network_cli) or API (httpapi for NX-OS) and executes changes idempotently. One playbook can configure VLANs across 500 switches in minutes.

See also: AAP 2.6 Network Automation: Cisco, Arista, Juniper, and Multi-Vendor Management

Collections

# Cisco IOS (routers, switches)
ansible-galaxy collection install cisco.ios

# Cisco NX-OS (Nexus data center switches) ansible-galaxy collection install cisco.nxos

# Cisco ASA (firewalls) ansible-galaxy collection install cisco.asa

# Cisco Meraki (cloud-managed) ansible-galaxy collection install cisco.meraki

# Cisco ACI (Application Centric Infrastructure) ansible-galaxy collection install cisco.aci

# Cisco ISE (Identity Services Engine) ansible-galaxy collection install cisco.ise

Inventory for Network Devices

# inventory/network.yml
all:
  children:
    ios_devices:
      hosts:
        core-sw-01:
          ansible_host: 10.0.0.1
        core-sw-02:
          ansible_host: 10.0.0.2
        dist-sw-01:
          ansible_host: 10.0.1.1
        dist-sw-02:
          ansible_host: 10.0.1.2
        edge-rtr-01:
          ansible_host: 10.0.0.254
      vars:
        ansible_network_os: cisco.ios.ios
        ansible_connection: ansible.netcommon.network_cli
        ansible_user: ansible
        ansible_password: "{{ vault_cisco_password }}"
        ansible_become: true
        ansible_become_method: enable
        ansible_become_password: "{{ vault_enable_password }}"

nxos_devices: hosts: dc-spine-01: ansible_host: 10.0.10.1 dc-spine-02: ansible_host: 10.0.10.2 dc-leaf-01: ansible_host: 10.0.10.11 vars: ansible_network_os: cisco.nxos.nxos ansible_connection: ansible.netcommon.network_cli ansible_user: admin ansible_password: "{{ vault_nxos_password }}"

asa_firewalls: hosts: fw-01: ansible_host: 10.0.0.250 fw-02: ansible_host: 10.0.0.251 vars: ansible_network_os: cisco.asa.asa ansible_connection: ansible.netcommon.network_cli ansible_user: admin ansible_password: "{{ vault_asa_password }}" ansible_become: true ansible_become_method: enable

See also: Using the AAP 2.6 Self-Service Portal for Network Automation

Configuration Backup

---
- name: Backup all network device configurations
  hosts: ios_devices
  gather_facts: false

tasks: - name: Backup running configuration cisco.ios.ios_config: backup: true backup_options: filename: "{{ inventory_hostname }}_{{ ansible_date_time.date }}.cfg" dir_path: /backups/network/ register: backup_result

- name: Save running config to startup cisco.ios.ios_config: save_when: modified

Automated Daily Backups

---
- name: Daily network backup with diff detection
  hosts: ios_devices:nxos_devices
  gather_facts: false

tasks: - name: Get running config cisco.ios.ios_command: commands: - show running-config register: running_config when: ansible_network_os == "cisco.ios.ios"

- name: Get NX-OS running config cisco.nxos.nxos_command: commands: - show running-config register: running_config when: ansible_network_os == "cisco.nxos.nxos"

- name: Save configuration to file ansible.builtin.copy: content: "{{ running_config.stdout[0] }}" dest: "/backups/{{ inventory_hostname }}/{{ ansible_date_time.date }}.cfg" delegate_to: localhost

- name: Check for config drift ansible.builtin.command: cmd: diff /backups/{{ inventory_hostname }}/baseline.cfg /backups/{{ inventory_hostname }}/{{ ansible_date_time.date }}.cfg register: config_diff delegate_to: localhost changed_when: config_diff.rc != 0 failed_when: false

- name: Alert on config drift ansible.builtin.debug: msg: "⚠️ Configuration drift detected on {{ inventory_hostname }}" when: config_diff.rc != 0

VLAN Management

---
- name: Configure VLANs across all switches
  hosts: ios_devices
  gather_facts: false

vars: vlans: - vlan_id: 10 name: MANAGEMENT - vlan_id: 20 name: SERVERS - vlan_id: 30 name: USERS - vlan_id: 40 name: VOIP - vlan_id: 50 name: GUEST - vlan_id: 99 name: NATIVE

tasks: - name: Configure VLANs cisco.ios.ios_vlans: config: "{{ vlans }}" state: merged

- name: Configure trunk ports cisco.ios.ios_l2_interfaces: config: - name: GigabitEthernet0/1 mode: trunk trunk: allowed_vlans: "10,20,30,40,50" native_vlan: 99 - name: GigabitEthernet0/2 mode: trunk trunk: allowed_vlans: "10,20,30,40,50" native_vlan: 99 state: merged

- name: Configure access ports cisco.ios.ios_l2_interfaces: config: - name: "GigabitEthernet1/{{ item }}" mode: access access: vlan: 30 state: merged loop: "{{ range(1, 25) | list }}"

See also: Ansible Network Automation: Configure Cisco, Arista, and Juniper at Scale

Interface Configuration

---
- name: Configure interfaces
  hosts: ios_devices
  gather_facts: false

tasks: - name: Configure L3 interfaces cisco.ios.ios_l3_interfaces: config: - name: Loopback0 ipv4: - address: "10.255.0.{{ host_id }}/32" - name: Vlan10 ipv4: - address: "10.0.10.{{ host_id }}/24" state: merged

- name: Configure interface descriptions cisco.ios.ios_interfaces: config: - name: GigabitEthernet0/0 description: "UPLINK to core-sw-01" enabled: true speed: "1000" duplex: full - name: GigabitEthernet0/1 description: "TRUNK to dist-sw-01" enabled: true state: merged

ACL Management

---
- name: Manage ACLs
  hosts: ios_devices
  gather_facts: false

tasks: - name: Configure extended ACL cisco.ios.ios_acls: config: - afi: ipv4 acls: - name: SERVERS_IN aces: - sequence: 10 grant: permit protocol: tcp source: address: 10.0.30.0 wildcard_bits: 0.0.0.255 destination: address: 10.0.20.0 wildcard_bits: 0.0.0.255 destination_port: eq: 443 log: true - sequence: 20 grant: permit protocol: tcp source: address: 10.0.30.0 wildcard_bits: 0.0.0.255 destination: address: 10.0.20.0 wildcard_bits: 0.0.0.255 destination_port: eq: 22 - sequence: 100 grant: deny protocol: ip source: any: true destination: any: true log: true state: merged

- name: Apply ACL to interface cisco.ios.ios_config: lines: - ip access-group SERVERS_IN in parents: interface Vlan30

OSPF Routing

---
- name: Configure OSPF
  hosts: ios_devices
  gather_facts: false

tasks: - name: Configure OSPF process cisco.ios.ios_ospfv2: config: processes: - process_id: 1 router_id: "10.255.0.{{ host_id }}" areas: - area_id: "0" ranges: - address: 10.0.0.0 netmask: 255.255.0.0 passive_interfaces: default: true networks: - address: 10.0.0.0 wildcard_bits: 0.0.255.255 area: "0" state: merged

- name: Enable OSPF on non-passive interfaces cisco.ios.ios_config: lines: - no passive-interface GigabitEthernet0/0 - no passive-interface GigabitEthernet0/1 parents: router ospf 1

NX-OS Data Center Configuration

---
- name: Configure Nexus switches
  hosts: nxos_devices
  gather_facts: false

tasks: - name: Enable features cisco.nxos.nxos_feature: feature: "{{ item }}" state: enabled loop: - vpc - lacp - interface-vlan - hsrp - ospf - bgp - nv overlay - vn-segment-vlan-based

- name: Configure VLANs cisco.nxos.nxos_vlans: config: - vlan_id: 100 name: WEB_SERVERS mapped_vni: 10100 - vlan_id: 200 name: APP_SERVERS mapped_vni: 10200 - vlan_id: 300 name: DB_SERVERS mapped_vni: 10300 state: merged

- name: Configure vPC cisco.nxos.nxos_vpc: domain: 1 role_priority: "{{ vpc_role_priority }}" system_priority: 2000 peer_gw: true auto_recovery: true pkl_dest: "{{ vpc_peer_ip }}" pkl_src: "{{ vpc_local_ip }}" state: present

ASA Firewall Configuration

---
- name: Configure ASA firewall
  hosts: asa_firewalls
  gather_facts: false

tasks: - name: Configure ACLs cisco.asa.asa_acls: config: - acls: - name: OUTSIDE_IN aces: - grant: permit protocol: tcp source: any: true destination: host: 10.0.20.10 destination_port: eq: 443 - grant: permit protocol: tcp source: any: true destination: host: 10.0.20.10 destination_port: eq: 80 - grant: deny protocol: ip source: any: true destination: any: true log: default state: merged

- name: Configure NAT cisco.asa.asa_config: lines: - object network WEB_SERVER - host 10.0.20.10 - nat (inside,outside) static 203.0.113.10

Compliance Checking

---
- name: Network compliance audit
  hosts: ios_devices
  gather_facts: false

tasks: - name: Check NTP configuration cisco.ios.ios_command: commands: - show ntp status register: ntp_status

- name: Verify NTP is synchronized ansible.builtin.assert: that: - "'synchronized' in ntp_status.stdout[0]" fail_msg: "NTP not synchronized on {{ inventory_hostname }}" success_msg: "NTP OK"

- name: Check SSH version cisco.ios.ios_command: commands: - show ip ssh register: ssh_info

- name: Verify SSH v2 ansible.builtin.assert: that: - "'SSH Enabled - version 2.0' in ssh_info.stdout[0]" fail_msg: "SSH v2 not enabled on {{ inventory_hostname }}"

- name: Check for insecure protocols cisco.ios.ios_command: commands: - show running-config | include telnet|http server register: insecure_check

- name: Flag insecure protocols ansible.builtin.debug: msg: "⚠️ Insecure protocol found: {{ insecure_check.stdout_lines }}" when: insecure_check.stdout[0] | length > 0

- name: Verify banner exists cisco.ios.ios_command: commands: - show running-config | section banner register: banner_check

- name: Generate compliance report ansible.builtin.template: src: compliance-report.j2 dest: "/reports/{{ inventory_hostname }}_compliance_{{ ansible_date_time.date }}.txt" delegate_to: localhost

FAQ

How does Ansible connect to Cisco devices?

Ansible uses ansible.netcommon.network_cli (SSH) for IOS, NX-OS, and ASA devices. NX-OS also supports ansible.netcommon.httpapi for REST API access. No agent is installed on the network device — Ansible sends CLI commands over SSH just like a human would.

Can Ansible roll back network changes?

Use cisco.ios.ios_config with backup: true before changes, then restore from backup if needed. For NX-OS, use checkpoints: cisco.nxos.nxos_config with checkpoint_file. Best practice: always save a backup before applying changes with state: replaced or state: overridden.

Is Ansible safe for production network changes?

Use --check --diff mode to preview changes without applying them. Use serial: 1 for rolling updates across switches. Implement a "canary" pattern: apply to one device, verify, then roll out to the rest. Back up configs before every change.

What about Cisco DNA Center and Meraki?

The cisco.dnac collection automates Cisco DNA Center (now Catalyst Center) — provisions, monitors, and manages campus networks. The cisco.meraki collection manages cloud-managed Meraki devices via their API. Both work well with Ansible for hybrid environments.

Conclusion

Ansible automates every layer of Cisco networking — IOS routers and switches, NX-OS data center fabrics, ASA firewalls, and cloud-managed Meraki devices. Start with configuration backups and compliance checks (lowest risk), then move to VLAN management, interface configuration, and routing protocols. The resource module approach (ios_vlans, ios_acls, ios_l3_interfaces) provides idempotent, structured configuration management that's safer and more maintainable than raw CLI commands.

Related Articles

Ansible for IoT and Edge ComputingUFW Allow Port with AnsibleAnsible SIEM SOC Security OperationsAAP 2.6 Automation MeshAnsible Dynamic Inventory Complete Guide

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home