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 Network Automation: Configure Cisco, Arista, and Juniper at Scale

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

Automate network device configuration with Ansible. Manage Cisco IOS, Arista EOS, Juniper Junos, and multi-vendor networks using ansible.netcommon.

Introduction

Network devices — switches, routers, firewalls — are the backbone of enterprise infrastructure, yet they're often the last to get automated. Ansible's network automation capabilities let you manage multi-vendor environments with the same playbook-based approach used for servers. No agents required — Ansible connects over SSH or API to configure devices declaratively.

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

Install Network Collections

# Core networking
ansible-galaxy collection install ansible.netcommon

# Vendor-specific ansible-galaxy collection install cisco.ios ansible-galaxy collection install cisco.nxos ansible-galaxy collection install arista.eos ansible-galaxy collection install junipernetworks.junos ansible-galaxy collection install vyos.vyos

# Python dependencies pip install paramiko netaddr xmltodict ncclient

Network Inventory

# inventory/network.yml
all:
  children:
    cisco_switches:
      hosts:
        sw-core-01:
          ansible_host: 10.0.0.1
        sw-access-01:
          ansible_host: 10.0.0.10
        sw-access-02:
          ansible_host: 10.0.0.11
      vars:
        ansible_network_os: cisco.ios.ios
        ansible_connection: ansible.netcommon.network_cli
        ansible_user: admin
        ansible_password: "{{ vault_cisco_password }}"
        ansible_become: true
        ansible_become_method: enable
        ansible_become_password: "{{ vault_enable_password }}"

arista_switches: hosts: sw-spine-01: ansible_host: 10.0.1.1 sw-leaf-01: ansible_host: 10.0.1.10 vars: ansible_network_os: arista.eos.eos ansible_connection: ansible.netcommon.network_cli ansible_user: admin ansible_password: "{{ vault_arista_password }}"

juniper_routers: hosts: rtr-edge-01: ansible_host: 10.0.2.1 rtr-edge-02: ansible_host: 10.0.2.2 vars: ansible_network_os: junipernetworks.junos.junos ansible_connection: ansible.netcommon.netconf ansible_user: admin ansible_password: "{{ vault_juniper_password }}"

See also: Ansible NETCONF Connection Plugin: Network Configuration Protocol Complete Guide

VLAN Management

Cisco IOS

- name: Configure VLANs on Cisco switches
  hosts: cisco_switches
  gather_facts: false
  tasks:
    - name: Configure VLANs
      cisco.ios.ios_vlans:
        config:
          - vlan_id: 10
            name: SERVERS
            state: active
          - vlan_id: 20
            name: USERS
            state: active
          - vlan_id: 30
            name: MANAGEMENT
            state: active
          - vlan_id: 99
            name: NATIVE
            state: active
        state: merged

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

- name: Configure access ports cisco.ios.ios_l2_interfaces: config: - name: "GigabitEthernet0/{{ item.port }}" mode: access access: vlan: "{{ item.vlan }}" state: merged loop: - { port: 10, vlan: 10 } - { port: 11, vlan: 10 } - { port: 20, vlan: 20 } - { port: 21, vlan: 20 }

Arista EOS

- name: Configure VLANs on Arista
  hosts: arista_switches
  gather_facts: false
  tasks:
    - name: Configure VLANs
      arista.eos.eos_vlans:
        config:
          - vlan_id: 10
            name: SERVERS
          - vlan_id: 20
            name: USERS
        state: merged

BGP Configuration

Cisco BGP

- name: Configure BGP on Cisco routers
  hosts: cisco_routers
  gather_facts: false
  tasks:
    - name: Configure BGP
      cisco.ios.ios_bgp_global:
        config:
          as_number: 65001
          router_id: 10.0.0.1
          log_neighbor_changes: true
          neighbors:
            - neighbor_address: 10.0.0.2
              remote_as: 65002
              description: "Peer to ISP-A"
              timers:
                keepalive: 30
                holdtime: 90
            - neighbor_address: 10.0.0.3
              remote_as: 65003
              description: "Peer to ISP-B"
          networks:
            - address: 192.168.0.0
              mask: 255.255.0.0
        state: merged

Juniper BGP

- name: Configure BGP on Juniper
  hosts: juniper_routers
  gather_facts: false
  tasks:
    - name: Configure BGP
      junipernetworks.junos.junos_bgp_global:
        config:
          as_number: 65001
          router_id: 10.0.2.1
        state: merged

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

ACL and Firewall Rules

- name: Configure ACLs
  hosts: cisco_switches
  gather_facts: false
  tasks:
    - name: Configure extended ACL
      cisco.ios.ios_acls:
        config:
          - afi: ipv4
            acls:
              - name: DENY-EXTERNAL
                aces:
                  - sequence: 10
                    grant: permit
                    protocol: tcp
                    source:
                      address: 10.0.10.0
                      wildcard_bits: 0.0.0.255
                    destination:
                      address: 10.0.20.0
                      wildcard_bits: 0.0.0.255
                    destination_port:
                      eq: 443
                  - sequence: 20
                    grant: permit
                    protocol: tcp
                    source:
                      any: true
                    destination:
                      address: 10.0.10.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 DENY-EXTERNAL in parents: interface GigabitEthernet0/0

Configuration Backup

- name: Backup all network device configs
  hosts: all
  gather_facts: false
  tasks:
    - name: Backup Cisco config
      cisco.ios.ios_config:
        backup: true
        backup_options:
          dir_path: /backup/cisco/
          filename: "{{ inventory_hostname }}-{{ ansible_date_time.date }}.cfg"
      when: ansible_network_os == 'cisco.ios.ios'

- name: Backup Arista config arista.eos.eos_config: backup: true backup_options: dir_path: /backup/arista/ filename: "{{ inventory_hostname }}-{{ ansible_date_time.date }}.cfg" when: ansible_network_os == 'arista.eos.eos'

- name: Backup Juniper config junipernetworks.junos.junos_config: backup: true backup_options: dir_path: /backup/juniper/ filename: "{{ inventory_hostname }}-{{ ansible_date_time.date }}.cfg" when: ansible_network_os == 'junipernetworks.junos.junos'

Network Facts and Compliance

- name: Gather network facts and check compliance
  hosts: cisco_switches
  gather_facts: false
  tasks:
    - name: Gather facts
      cisco.ios.ios_facts:
        gather_subset:
          - hardware
          - config
          - interfaces
      register: device_facts

- name: Check firmware version ansible.builtin.assert: that: - "'17.09' in device_facts.ansible_facts.ansible_net_version" fail_msg: "{{ inventory_hostname }} running outdated firmware: {{ device_facts.ansible_facts.ansible_net_version }}" success_msg: "{{ inventory_hostname }} firmware OK"

- name: Verify NTP is configured cisco.ios.ios_command: commands: - show ntp status register: ntp_status failed_when: "'Clock is synchronized' not in ntp_status.stdout[0]"

Multi-Vendor Abstraction

# roles/configure_ntp/tasks/main.yml
- name: Include vendor-specific NTP tasks
  ansible.builtin.include_tasks: "{{ ansible_network_os | replace('.', '_') }}.yml"

# roles/configure_ntp/tasks/cisco_ios_ios.yml - name: Configure NTP on Cisco cisco.ios.ios_ntp_global: config: servers: - server: 10.0.0.100 prefer: true - server: 10.0.0.101 state: merged

# roles/configure_ntp/tasks/arista_eos_eos.yml - name: Configure NTP on Arista arista.eos.eos_ntp_global: config: servers: - server: 10.0.0.100 prefer: true - server: 10.0.0.101 state: merged

Best Practices

gather_facts: false — Network devices don't support standard fact gathering; use _facts modules explicitly Use resource modulesios_vlans, eos_interfaces etc. over raw _config commands for idempotency state: merged vs replacedmerged adds to existing config; replaced overwrites the entire resource Backup before changes — Always backup running config before applying changes Use --check --diff — Preview network changes before applying Serial execution — Run against one device at a time during initial rollout with serial: 1 Vault for credentials — Never store network device passwords in plain text NETCONF where available — More structured than CLI scraping; preferred for Juniper

FAQ

Can Ansible manage firewalls?

Yes — collections exist for Palo Alto (paloaltonetworks.panos), Fortinet (fortinet.fortios), Check Point (check_point.mgmt), and pfSense.

How to handle different OS versions?

Use ansible_net_version from facts to conditionally apply version-specific configurations.

Ansible vs dedicated network tools (Nornir, NAPALM)?

Ansible provides a broader automation platform (servers + network + cloud in one). Nornir/NAPALM are Python-native and faster for pure network tasks. Many teams use Ansible for orchestration and NAPALM for validation.

Conclusion

Ansible network automation brings the same infrastructure-as-code benefits to switches, routers, and firewalls that server teams have enjoyed for years. With vendor-specific collections providing idempotent resource modules, multi-vendor network management becomes declarative, version-controlled, and auditable.

Related Articles

Ansible Automation MeshAnsible Automation Platform RBACAnsible Compliance Automation

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home