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 modules — ios_vlans, eos_interfaces etc. over raw _config commands for idempotency
state: merged vs replaced — merged 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 Mesh • Ansible Automation Platform RBAC • Ansible Compliance AutomationCategory: installation