AAP 2.6 Network Automation: Cisco, Arista, Juniper, and Multi-Vendor Management
By Luca Berton · Published 2024-01-01 · Category: troubleshooting
Automate network infrastructure with AAP 2.6. Configure Cisco IOS/NX-OS, Arista EOS, Juniper Junos, and multi-vendor environments.
Network Automation with AAP 2.6
AAP 2.6 provides enterprise-grade network automation for multi-vendor environments. Unlike server automation (SSH-based), network automation uses specialized connection plugins and collections designed for network device APIs and CLIs.
See also: Ansible Network Automation: Configure Cisco, Arista, and Juniper at Scale
Connection Methods
| Connection | Protocol | Use Case |
|-----------|----------|----------|
| ansible.netcommon.network_cli | SSH CLI | Traditional CLI-based devices |
| ansible.netcommon.netconf | NETCONF (SSH) | Juniper, Nokia, structured config |
| ansible.netcommon.httpapi | REST API | Arista eAPI, Cisco NX-API |
| ansible.netcommon.libssh | SSH (libssh) | High-performance SSH |
Execution Environment for Network
---
version: 3
images:
base_image:
name: registry.redhat.io/ansible-automation-platform-26/ee-minimal-rhel9:latest
dependencies:
galaxy:
collections:
- name: ansible.netcommon
version: ">=7.0.0"
- name: ansible.utils
version: ">=5.0.0"
- name: cisco.ios
version: ">=9.0.0"
- name: cisco.nxos
version: ">=9.0.0"
- name: cisco.asa
- name: arista.eos
version: ">=10.0.0"
- name: junipernetworks.junos
version: ">=9.0.0"
- name: vyos.vyos
- name: community.network
python:
- paramiko>=3.0
- netmiko>=4.0
- ncclient>=0.6
- jmespath>=1.0
- netaddr>=1.0
- textfsm>=1.1
- ntc-templates>=6.0
- xmltodict>=0.13
system:
- openssh-clients [platform:rpm]
- libxml2 [platform:rpm]
- libxslt [platform:rpm]
See also: Using the AAP 2.6 Self-Service Portal for Network Automation
Inventory for Network Devices
Static Network Inventory
- name: Create network inventory
ansible.platform.inventory:
controller_host: "{{ gateway_url }}"
controller_username: "{{ controller_user }}"
controller_password: "{{ controller_pass }}"
name: "Network Infrastructure"
organization: "Network Operations"
state: present
# Cisco IOS group
- name: Create Cisco IOS group
ansible.platform.group:
name: "cisco_ios"
inventory: "Network Infrastructure"
variables:
ansible_network_os: cisco.ios.ios
ansible_connection: ansible.netcommon.network_cli
ansible_become: true
ansible_become_method: enable
state: present
# Arista EOS group
- name: Create Arista EOS group
ansible.platform.group:
name: "arista_eos"
inventory: "Network Infrastructure"
variables:
ansible_network_os: arista.eos.eos
ansible_connection: ansible.netcommon.httpapi
ansible_httpapi_use_ssl: true
ansible_httpapi_validate_certs: false
ansible_become: true
ansible_become_method: enable
state: present
# Juniper Junos group
- name: Create Juniper Junos group
ansible.platform.group:
name: "juniper_junos"
inventory: "Network Infrastructure"
variables:
ansible_network_os: junipernetworks.junos.junos
ansible_connection: ansible.netcommon.netconf
state: present
Network Credential
- name: Create network credential
ansible.platform.credential:
controller_host: "{{ gateway_url }}"
controller_username: "{{ controller_user }}"
controller_password: "{{ controller_pass }}"
name: "Network SSH"
organization: "Network Operations"
credential_type: "Machine"
inputs:
username: "ansible"
password: "{{ vault_network_password }}"
become_password: "{{ vault_enable_password }}"
state: present
Configuration Backup
Multi-Vendor Backup Playbook
---
- name: Backup network device configurations
hosts: all
gather_facts: false
tasks:
- name: Backup Cisco IOS
cisco.ios.ios_config:
backup: true
backup_options:
filename: "{{ inventory_hostname }}_{{ ansible_date_time.date }}.cfg"
dir_path: /var/backups/network/
when: ansible_network_os == 'cisco.ios.ios'
- name: Backup Arista EOS
arista.eos.eos_config:
backup: true
backup_options:
filename: "{{ inventory_hostname }}_{{ ansible_date_time.date }}.cfg"
dir_path: /var/backups/network/
when: ansible_network_os == 'arista.eos.eos'
- name: Backup Juniper Junos
junipernetworks.junos.junos_config:
backup: true
backup_options:
filename: "{{ inventory_hostname }}_{{ ansible_date_time.date }}.cfg"
dir_path: /var/backups/network/
when: ansible_network_os == 'junipernetworks.junos.junos'
- name: Push backups to Git
ansible.builtin.shell: |
cd /var/backups/network/
git add -A
git commit -m "Network backup {{ ansible_date_time.iso8601 }}" || true
git push
delegate_to: localhost
run_once: true
See also: Ansible NETCONF Connection Plugin: Network Configuration Protocol Complete Guide
Configuration Management
Cisco IOS Configuration
- name: Configure Cisco IOS switches
hosts: cisco_ios
gather_facts: false
tasks:
- name: Configure hostname and domain
cisco.ios.ios_hostname:
config:
hostname: "{{ inventory_hostname }}"
domain_name: example.com
- name: Configure NTP
cisco.ios.ios_ntp_global:
config:
servers:
- server: 10.1.1.1
prefer: true
- server: 10.1.1.2
- name: Configure SNMP
cisco.ios.ios_snmp_server:
config:
communities:
- name: "{{ vault_snmp_community }}"
ro: true
acl_v4: SNMP_ACL
hosts:
- host: 10.1.1.100
version: "2c"
community_string: "{{ vault_snmp_community }}"
- name: Configure interfaces
cisco.ios.ios_l3_interfaces:
config:
- name: Loopback0
ipv4:
- address: "{{ loopback_ip }}/32"
- name: GigabitEthernet0/1
ipv4:
- address: "{{ mgmt_ip }}/24"
- name: Configure ACLs
cisco.ios.ios_acls:
config:
- afi: ipv4
acls:
- name: MGMT_ACCESS
aces:
- grant: permit
protocol_options:
tcp:
ssh: true
source:
address: 10.0.0.0
wildcard_bits: 0.0.0.255
destination:
any: true
sequence: 10
- grant: deny
source:
any: true
destination:
any: true
sequence: 999
Arista EOS Configuration
- name: Configure Arista EOS switches
hosts: arista_eos
gather_facts: false
tasks:
- name: Configure BGP
arista.eos.eos_bgp_global:
config:
as_number: "{{ bgp_asn }}"
router_id: "{{ loopback_ip }}"
neighbors:
- neighbor_address: "{{ bgp_peer_ip }}"
remote_as: "{{ bgp_peer_asn }}"
description: "Uplink to Core"
send_community:
standard: true
extended: true
- name: Configure VLANs
arista.eos.eos_vlans:
config:
- vlan_id: 100
name: Management
state: active
- vlan_id: 200
name: Production
state: active
- vlan_id: 300
name: DMZ
state: active
Juniper Junos Configuration
- name: Configure Juniper Junos
hosts: juniper_junos
gather_facts: false
tasks:
- name: Configure system settings
junipernetworks.junos.junos_config:
lines:
- set system host-name {{ inventory_hostname }}
- set system domain-name example.com
- set system name-server 10.1.1.1
- set system name-server 10.1.1.2
- set system ntp server 10.1.1.1 prefer
- set system syslog host 10.1.1.100 any warning
- name: Configure firewall filter
junipernetworks.junos.junos_config:
src: templates/junos-firewall.j2
comment: "Ansible managed - {{ ansible_date_time.iso8601 }}"
confirm: 2 # Auto-rollback after 2 minutes if not confirmed
Network Compliance Auditing
Compliance Check Playbook
---
- name: Network compliance audit
hosts: all
gather_facts: false
vars:
compliance_results: []
tasks:
- name: Gather device facts
ansible.netcommon.cli_command:
command: "{{ show_version_cmd }}"
register: version_output
vars:
show_version_cmd: "{{ 'show version' if ansible_network_os != 'junipernetworks.junos.junos' else 'show version brief' }}"
- name: Check NTP is configured
ansible.netcommon.cli_command:
command: "{{ ntp_cmd }}"
register: ntp_config
vars:
ntp_cmd: "{{ 'show ntp associations' if ansible_network_os == 'cisco.ios.ios' else 'show ntp status' }}"
- name: Verify SSH is enabled
ansible.netcommon.cli_command:
command: "show ip ssh"
register: ssh_config
when: ansible_network_os == 'cisco.ios.ios'
- name: Check running vs startup config
cisco.ios.ios_command:
commands:
- show archive config differences system:running-config nvram:startup-config
register: config_diff
when: ansible_network_os == 'cisco.ios.ios'
- name: Flag unsaved changes
ansible.builtin.debug:
msg: "WARNING: {{ inventory_hostname }} has unsaved configuration changes!"
when:
- ansible_network_os == 'cisco.ios.ios'
- config_diff.stdout[0] | length > 0
- name: Generate compliance report
ansible.builtin.template:
src: templates/compliance-report.j2
dest: "/var/reports/network/{{ inventory_hostname }}_compliance.html"
delegate_to: localhost
Change Management Workflow
Use AAP workflow templates for network change management:
[Pre-Change Backup] → [Validate Change Request]
↓ success
[Approval: Network Lead]
↓ success
[Deploy to Lab]
↓ success
[Verify Lab]
↓ success ↓ failure
[Deploy to Production] [Rollback Lab]
↓ success ↓ failure
[Post-Change Verify] [Rollback Production]
↓ success ↓ always
[Close Change Ticket] [Alert Network Team]
Rollback Playbook
- name: Rollback network configuration
hosts: "{{ target_devices }}"
gather_facts: false
tasks:
- name: Rollback Cisco IOS
cisco.ios.ios_config:
replace: config
src: "/var/backups/network/{{ inventory_hostname }}_{{ rollback_date }}.cfg"
when: ansible_network_os == 'cisco.ios.ios'
- name: Rollback Juniper (automatic with commit confirmed)
junipernetworks.junos.junos_config:
rollback: 1
comment: "Rollback initiated by AAP - {{ ansible_date_time.iso8601 }}"
when: ansible_network_os == 'junipernetworks.junos.junos'
- name: Verify connectivity after rollback
ansible.netcommon.cli_command:
command: "ping {{ mgmt_gateway }} repeat 5"
register: ping_result
failed_when: "'!!!!!' not in ping_result.stdout"
when: ansible_network_os == 'cisco.ios.ios'
Resource Modules (Declarative Approach)
Resource modules provide idempotent, declarative network configuration:
- name: Declarative interface configuration
cisco.ios.ios_l2_interfaces:
config:
- name: GigabitEthernet0/1
mode: access
access:
vlan: 100
- name: GigabitEthernet0/2
mode: trunk
trunk:
allowed_vlans: "100,200,300"
native_vlan: 1
state: replaced # Ensure exact match
States available for resource modules:
| State | Behavior |
|-------|----------|
| merged | Add/update config (default) |
| replaced | Replace specific resource config |
| overridden | Replace all resources of this type |
| deleted | Remove specific config |
| gathered | Read current config (no changes) |
| parsed | Parse offline config text |
| rendered | Generate device commands without applying |
FAQ
How do I handle devices that don't support collections?
Use ansible.netcommon.cli_command and ansible.netcommon.cli_config for generic CLI-based management. These work with any device that has SSH CLI access.
Can AAP manage network devices through a jump host?
Yes. Use ansible_ssh_common_args: '-o ProxyJump=jumphost.example.com' or configure hop nodes in Automation Mesh to place execution nodes in the same network as the devices.
How do I test network changes safely?
Use Juniper's commit confirmed (auto-rollback timer), Cisco's configure replace with archive, or AAP workflow templates with lab verification before production. Resource modules with state: rendered let you preview commands without applying.
Should I use CLI or API connections?
APIs (NETCONF, eAPI, NX-API) are preferred when available — they provide structured data, transactional commits, and better error handling. CLI is universal but requires parsing unstructured text output.
Can I automate network device firmware upgrades?
Yes. Use ansible.netcommon.net_put or vendor-specific modules to upload firmware, then schedule reboot with verification. Always include rollback procedures and test in lab first.
Conclusion
AAP 2.6 provides a unified platform for multi-vendor network automation. From configuration backup and compliance auditing to automated deployments with change management workflows, AAP transforms network operations from manual CLI sessions into repeatable, auditable, self-service automation.
Related Articles
• AAP 2.6 Architecture and Components: Complete Guide • AAP 2.6 Execution Environments: Build, Manage, and Deploy Custom EEs • AAP 2.6 Workflow Templates: Advanced Multi-Step Automation Guide • AAP 2.6 Automation Mesh: Distributed Execution Across Sites and Networks • AAP 2.6 Self-Service Portal for Network TeamsCategory: troubleshooting