Network Automation

Overview

Ansible provides extensive support for network automation across major vendors including Cisco, Juniper, Arista, F5, and more. This enables infrastructure-as-code for network configurations, compliance enforcement, and automated deployments.

Cisco IOS/IOS-XE Automation

Basic Interface Configuration

---
- name: Configure Cisco IOS devices
  hosts: cisco_routers
  gather_facts: no
  connection: network_cli

  tasks:
    - name: Configure interfaces
      cisco.ios.ios_config:
        lines:
          - description Uplink to Core Switch
          - ip address 192.168.1.1 255.255.255.0
          - no shutdown
        parents: interface GigabitEthernet0/1

    - name: Configure multiple VLANs
      cisco.ios.ios_vlan:
        vlan_id: "{{ item.id }}"
        name: "{{ item.name }}"
        state: present
      loop:
        - { id: 10, name: 'DATA' }
        - { id: 20, name: 'VOICE' }
        - { id: 30, name: 'GUEST' }

    - name: Save configuration
      cisco.ios.ios_config:
        save_when: always

Advanced Routing Configuration

---
- name: Configure OSPF routing
  hosts: cisco_routers
  gather_facts: no

  tasks:
    - name: Configure OSPF
      cisco.ios.ios_config:
        lines:
          - router-id 1.1.1.1
          - network 10.0.0.0 0.0.0.255 area 0
          - network 192.168.1.0 0.0.0.255 area 0
          - passive-interface default
          - no passive-interface GigabitEthernet0/1
        parents: router ospf 1

    - name: Configure BGP
      cisco.ios.ios_config:
        lines:
          - bgp router-id 1.1.1.1
          - neighbor 10.0.0.2 remote-as 65001
          - neighbor 10.0.0.2 description ISP_PRIMARY
          - address-family ipv4 unicast
          -  network 192.168.1.0 mask 255.255.255.0
        parents: router bgp 65000

Access Control Lists

---
- name: Configure ACLs
  hosts: cisco_routers
  tasks:
    - name: Create extended ACL
      cisco.ios.ios_acls:
        config:
          - afi: ipv4
            acls:
              - name: PERMIT_WEB
                acl_type: extended
                aces:
                  - sequence: 10
                    grant: permit
                    protocol: tcp
                    source:
                      any: true
                    destination:
                      host: 192.168.1.100
                      port_protocol:
                        eq: www
                  - sequence: 20
                    grant: permit
                    protocol: tcp
                    source:
                      any: true
                    destination:
                      host: 192.168.1.100
                      port_protocol:
                        eq: 443
                  - sequence: 30
                    grant: deny
                    protocol: ip
                    source:
                      any: true
                    destination:
                      any: true

    - name: Apply ACL to interface
      cisco.ios.ios_config:
        lines:
          - ip access-group PERMIT_WEB in
        parents: interface GigabitEthernet0/2

Juniper Junos Automation

Interface and Routing Configuration

---
- name: Configure Juniper devices
  hosts: juniper_routers
  gather_facts: no
  connection: netconf

  tasks:
    - name: Configure interfaces
      junipernetworks.junos.junos_config:
        lines:
          - set interfaces ge-0/0/1 description "Uplink to Core"
          - set interfaces ge-0/0/1 unit 0 family inet address 192.168.1.1/24
          - set interfaces ge-0/0/2 description "LAN"
          - set interfaces ge-0/0/2 unit 0 family inet address 10.0.0.1/24
        commit: yes

    - name: Configure OSPF
      junipernetworks.junos.junos_config:
        lines:
          - set protocols ospf area 0.0.0.0 interface ge-0/0/1.0
          - set protocols ospf area 0.0.0.0 interface ge-0/0/2.0
          - set protocols ospf area 0.0.0.0 interface lo0.0 passive
        commit: yes

    - name: Configure firewall filter
      junipernetworks.junos.junos_config:
        lines:
          - set firewall family inet filter PROTECT-RE term accept-established from protocol tcp
          - set firewall family inet filter PROTECT-RE term accept-established from tcp-established
          - set firewall family inet filter PROTECT-RE term accept-established then accept
          - set firewall family inet filter PROTECT-RE term accept-ssh from protocol tcp
          - set firewall family inet filter PROTECT-RE term accept-ssh from port ssh
          - set firewall family inet filter PROTECT-RE term accept-ssh then accept
          - set firewall family inet filter PROTECT-RE term default-deny then discard
        commit: yes

Arista EOS Automation

VXLAN and EVPN Configuration

---
- name: Configure Arista leaf switches
  hosts: arista_leaf
  gather_facts: no
  connection: network_cli

  tasks:
    - name: Configure VLANs
      arista.eos.eos_vlans:
        config:
          - vlan_id: 10
            name: WEB
          - vlan_id: 20
            name: APP
          - vlan_id: 30
            name: DB

    - name: Configure VXLAN
      arista.eos.eos_config:
        lines:
          - source-interface Loopback1
          - vlan 10 vni 10010
          - vlan 20 vni 10020
          - vlan 30 vni 10030
        parents: interface Vxlan1

    - name: Configure BGP EVPN
      arista.eos.eos_config:
        lines:
          - router-id {{ router_id }}
          - neighbor SPINE peer group
          - neighbor SPINE remote-as 65000
          - neighbor SPINE send-community extended
          - neighbor 10.0.1.1 peer group SPINE
          - neighbor 10.0.1.2 peer group SPINE
          - address-family evpn
          -  neighbor SPINE activate
        parents: router bgp {{ asn }}

    - name: Configure MLAG
      arista.eos.eos_config:
        lines:
          - local-interface Vlan4094
          - peer-address 192.168.0.2
          - peer-link Port-Channel1
          - domain-id MLAG1
        parents: mlag configuration

F5 BIG-IP Automation

Load Balancer Configuration

---
- name: Configure F5 BIG-IP
  hosts: f5_devices
  gather_facts: no
  connection: local

  tasks:
    - name: Create pool
      f5networks.f5_modules.bigip_pool:
        provider:
          server: "{{ ansible_host }}"
          user: "{{ bigip_user }}"
          password: "{{ bigip_password }}"
          validate_certs: no
        name: web_pool
        lb_method: round-robin
        monitors:
          - http
        monitor_type: and_list

    - name: Add pool members
      f5networks.f5_modules.bigip_pool_member:
        provider:
          server: "{{ ansible_host }}"
          user: "{{ bigip_user }}"
          password: "{{ bigip_password }}"
          validate_certs: no
        pool: web_pool
        host: "{{ item.ip }}"
        port: "{{ item.port }}"
        state: present
      loop:
        - { ip: '10.0.1.10', port: 80 }
        - { ip: '10.0.1.11', port: 80 }
        - { ip: '10.0.1.12', port: 80 }

    - name: Create virtual server
      f5networks.f5_modules.bigip_virtual_server:
        provider:
          server: "{{ ansible_host }}"
          user: "{{ bigip_user }}"
          password: "{{ bigip_password }}"
          validate_certs: no
        name: web_vs
        destination: 203.0.113.10
        port: 443
        pool: web_pool
        snat: automap
        profiles:
          - http
          - clientssl
        state: present

    - name: Create iRule
      f5networks.f5_modules.bigip_irule:
        provider:
          server: "{{ ansible_host }}"
          user: "{{ bigip_user }}"
          password: "{{ bigip_password }}"
          validate_certs: no
        name: redirect_to_https
        content: |
          when HTTP_REQUEST {
            HTTP::redirect https://[HTTP::host][HTTP::uri]
          }

Network Backup and Restore

Automated Configuration Backup

---
- name: Backup network device configurations
  hosts: network_devices
  gather_facts: no

  vars:
    backup_dir: "./backups/{{ inventory_hostname }}"

  tasks:
    - name: Create backup directory
      file:
        path: "{{ backup_dir }}"
        state: directory
      delegate_to: localhost
      run_once: no

    - name: Backup Cisco IOS config
      cisco.ios.ios_config:
        backup: yes
        backup_options:
          filename: "{{ inventory_hostname }}_{{ ansible_date_time.date }}.cfg"
          dir_path: "{{ backup_dir }}"
      when: ansible_network_os == 'ios'

    - name: Backup Juniper config
      junipernetworks.junos.junos_config:
        backup: yes
        backup_options:
          filename: "{{ inventory_hostname }}_{{ ansible_date_time.date }}.conf"
          dir_path: "{{ backup_dir }}"
      when: ansible_network_os == 'junos'

    - name: Store backup in Git
      shell: |
        cd {{ backup_dir }}
        git add .
        git commit -m "Backup {{ inventory_hostname }} - {{ ansible_date_time.iso8601 }}"
        git push
      delegate_to: localhost
      when: backup_to_git | default(false)

Network Validation and Testing

Pre/Post Change Validation

---
- name: Network change with validation
  hosts: cisco_routers
  gather_facts: no

  tasks:
    # Pre-change validation
    - name: Get pre-change state
      cisco.ios.ios_command:
        commands:
          - show ip interface brief
          - show ip route summary
          - show ip bgp summary
      register: pre_change_state

    - name: Save pre-change state
      copy:
        content: "{{ pre_change_state.stdout | to_nice_json }}"
        dest: "./validation/{{ inventory_hostname }}_pre.json"
      delegate_to: localhost

    # Make changes
    - name: Apply configuration changes
      cisco.ios.ios_config:
        src: "configs/{{ inventory_hostname }}.cfg"
        replace: block

    # Post-change validation
    - name: Wait for device to stabilize
      wait_for:
        timeout: 30
      delegate_to: localhost

    - name: Get post-change state
      cisco.ios.ios_command:
        commands:
          - show ip interface brief
          - show ip route summary
          - show ip bgp summary
      register: post_change_state

    - name: Validate routing protocols
      cisco.ios.ios_command:
        commands:
          - show ip ospf neighbor
      register: ospf_neighbors
      failed_when: "'FULL' not in ospf_neighbors.stdout[0]"

    - name: Validate BGP peers
      cisco.ios.ios_command:
        commands:
          - show ip bgp summary
      register: bgp_summary
      failed_when: "'Established' not in bgp_summary.stdout[0]"

    - name: Compare states
      assert:
        that:
          - pre_change_state.stdout[0] | regex_findall('up.*up') | length == post_change_state.stdout[0] | regex_findall('up.*up') | length
        fail_msg: "Interface count changed after configuration"
        success_msg: "All interfaces remain operational"

Multi-Vendor Network Inventory

# inventory/network.yml
all:
  children:
    cisco:
      hosts:
        core-sw-01:
          ansible_host: 10.0.1.10
        dist-sw-01:
          ansible_host: 10.0.1.11
      vars:
        ansible_connection: network_cli
        ansible_network_os: ios
        ansible_become: yes
        ansible_become_method: enable

    juniper:
      hosts:
        edge-rtr-01:
          ansible_host: 10.0.2.10
      vars:
        ansible_connection: netconf
        ansible_network_os: junos

    arista:
      hosts:
        leaf-01:
          ansible_host: 10.0.3.10
        leaf-02:
          ansible_host: 10.0.3.11
      vars:
        ansible_connection: network_cli
        ansible_network_os: eos

    f5:
      hosts:
        lb-01:
          ansible_host: 10.0.4.10
      vars:
        ansible_connection: local

  vars:
    ansible_user: admin
    ansible_password: "{{ vault_network_password }}"
    ansible_python_interpreter: /usr/bin/python3

Network Compliance and Auditing

Configuration Compliance Check

---
- name: Audit network configuration compliance
  hosts: cisco_routers
  gather_facts: no

  vars:
    compliance_rules:
      - name: "NTP servers configured"
        command: "show run | include ntp server"
        expected: "ntp server"
      - name: "Logging configured"
        command: "show run | include logging"
        expected: "logging host"
      - name: "AAA enabled"
        command: "show run | include aaa"
        expected: "aaa new-model"

  tasks:
    - name: Run compliance checks
      cisco.ios.ios_command:
        commands: "{{ item.command }}"
      register: compliance_result
      loop: "{{ compliance_rules }}"
      failed_when: item.expected not in compliance_result.stdout[0]

    - name: Generate compliance report
      template:
        src: compliance_report.j2
        dest: "./reports/{{ inventory_hostname }}_compliance.html"
      delegate_to: localhost

    - name: Check for forbidden commands
      cisco.ios.ios_command:
        commands:
          - show run | include ip http server
      register: http_check
      failed_when: "'ip http server' in http_check.stdout[0] and 'ip http secure-server' not in http_check.stdout[0]"

Network Monitoring Integration

---
- name: Configure SNMP monitoring
  hosts: network_devices
  tasks:
    - name: Configure SNMP on Cisco
      cisco.ios.ios_config:
        lines:
          - snmp-server community {{ snmp_community }} RO
          - snmp-server location {{ site_location }}
          - snmp-server contact {{ site_contact }}
          - snmp-server host {{ monitoring_server }} version 2c {{ snmp_community }}
          - snmp-server enable traps snmp linkdown linkup
          - snmp-server enable traps config
      when: ansible_network_os == 'ios'

    - name: Register device with monitoring system
      uri:
        url: "https://{{ monitoring_server }}/api/devices"
        method: POST
        body_format: json
        body:
          hostname: "{{ inventory_hostname }}"
          ip_address: "{{ ansible_host }}"
          snmp_community: "{{ snmp_community }}"
          device_type: "{{ ansible_network_os }}"
        status_code: 201
      delegate_to: localhost

Network Automation Pipeline

Complete Change Management Workflow

---
- name: Network change management workflow
  hosts: network_devices
  gather_facts: no
  serial: 1  # Change one device at a time

  tasks:
    - name: Step 1 - Create change ticket
      servicenow.servicenow.snow_record:
        username: "{{ snow_user }}"
        password: "{{ snow_password }}"
        instance: "{{ snow_instance }}"
        state: present
        table: change_request
        data:
          short_description: "Network configuration change - {{ inventory_hostname }}"
          description: "Automated network configuration deployment"
          assignment_group: Network Operations
      register: change_ticket
      delegate_to: localhost

    - name: Step 2 - Backup current configuration
      include_tasks: backup_config.yml

    - name: Step 3 - Run pre-change validation
      include_tasks: validate_network.yml
      vars:
        validation_type: pre

    - name: Step 4 - Apply configuration
      cisco.ios.ios_config:
        src: "configs/{{ inventory_hostname }}.cfg"
        save_when: modified

    - name: Step 5 - Run post-change validation
      include_tasks: validate_network.yml
      vars:
        validation_type: post

    - name: Step 6 - Update change ticket
      servicenow.servicenow.snow_record:
        username: "{{ snow_user }}"
        password: "{{ snow_password }}"
        instance: "{{ snow_instance }}"
        state: present
        table: change_request
        number: "{{ change_ticket.record.number }}"
        data:
          work_notes: "Configuration successfully applied to {{ inventory_hostname }}"
          state: Review
      delegate_to: localhost

  rescue:
    - name: Rollback on failure
      cisco.ios.ios_config:
        src: "{{ backup_dir }}/{{ inventory_hostname }}_latest.cfg"
        replace: config

    - name: Update change ticket with failure
      servicenow.servicenow.snow_record:
        username: "{{ snow_user }}"
        password: "{{ snow_password }}"
        instance: "{{ snow_instance }}"
        state: present
        table: change_request
        number: "{{ change_ticket.record.number }}"
        data:
          work_notes: "Configuration failed - rolled back"
          state: Failed
      delegate_to: localhost

Best Practices Summary

Network Automation Best Practices

  • Testing: Always test in lab environment before production deployment
  • Validation: Use check mode and pre/post-change validation
  • Backups: Automated configuration backups before every change
  • Version Control: Store network configurations in Git
  • Rollback: Implement automated rollback procedures
  • Serialization: Use serial execution to limit blast radius
  • Monitoring: Integrate with monitoring systems for alerting
  • Compliance: Regular compliance audits against security baselines
  • Documentation: Auto-generate network documentation from configs
  • Change Management: Integrate with ITSM tools (ServiceNow, Jira)
  • Idempotency: Ensure network playbooks are idempotent
  • Multi-Vendor: Use platform-agnostic patterns where possible