Ansible Cisco Automation
Introduction
Ansible is a powerful open-source automation tool that has revolutionized the way network engineers manage and configure network devices. In this guide, we'll explore how Ansible can be specifically used to automate Cisco network devices, enabling you to manage your infrastructure more efficiently and consistently.
Cisco network automation with Ansible allows you to:
- Configure multiple devices simultaneously
- Standardize network configurations
- Reduce human error
- Implement changes rapidly
- Create repeatable and auditable network operations
Whether you're maintaining a small network or managing thousands of devices across multiple data centers, Ansible's agentless architecture and simple YAML syntax make it an ideal choice for Cisco network automation.
Prerequisites
Before diving into Cisco automation with Ansible, ensure you have:
- Basic understanding of Ansible concepts
- Ansible installed on your control node (version 2.9+)
- Python 3.6+ installed
- Network connectivity to your Cisco devices
- Valid credentials for your Cisco devices
- Basic familiarity with Cisco IOS commands
Setting Up Your Environment
Installing Required Packages
First, let's install the necessary Ansible collections for Cisco automation:
# Install Ansible Cisco collections
ansible-galaxy collection install cisco.ios
ansible-galaxy collection install ansible.netcommon
Inventory Configuration
Create an inventory file to define your Cisco devices:
# inventory.ini
[cisco_switches]
switch1 ansible_host=192.168.1.101
switch2 ansible_host=192.168.1.102
[cisco_routers]
router1 ansible_host=192.168.1.201
router2 ansible_host=192.168.1.202
[cisco:children]
cisco_switches
cisco_routers
[cisco:vars]
ansible_network_os=ios
ansible_connection=network_cli
ansible_user=admin
ansible_password=your_secure_password
ansible_become=yes
ansible_become_method=enable
ansible_become_password=your_enable_password
Ansible Configuration
Create or modify your ansible.cfg
file to optimize for network automation:
[defaults]
inventory = ./inventory.ini
host_key_checking = False
timeout = 30
forks = 10
gathering = explicit
[persistent_connection]
connect_timeout = 60
command_timeout = 60
Basic Cisco Operations with Ansible
Gathering Device Information
Let's start with a simple playbook to gather information from your Cisco devices:
---
# get_cisco_info.yml
- name: Gather Cisco device information
hosts: cisco
gather_facts: false
tasks:
- name: Collect device facts
cisco.ios.ios_facts:
gather_subset: all
register: device_facts
- name: Display device model
debug:
msg: "Device {{ inventory_hostname }} is a {{ device_facts.ansible_facts.ansible_net_model }}"
- name: Display IOS version
debug:
msg: "Running IOS version: {{ device_facts.ansible_facts.ansible_net_version }}"
To run this playbook:
ansible-playbook get_cisco_info.yml
Example output:
TASK [Display device model] *********************************************************
ok: [switch1] => {
"msg": "Device switch1 is a WS-C3750X-48P"
}
ok: [switch2] => {
"msg": "Device switch2 is a WS-C3750X-24P"
}
TASK [Display IOS version] **********************************************************
ok: [switch1] => {
"msg": "Running IOS version: 15.2(4)E7"
}
ok: [switch2] => {
"msg": "Running IOS version: 15.2(4)E7"
}
Configuring Interface Settings
Now, let's create a playbook to configure multiple interfaces:
---
# configure_interfaces.yml
- name: Configure Cisco interfaces
hosts: cisco_switches
gather_facts: false
vars:
vlans:
- id: 10
name: DATA
- id: 20
name: VOICE
- id: 30
name: MANAGEMENT
tasks:
- name: Configure VLANs
cisco.ios.ios_vlans:
config:
- name: "{{ item.name }}"
vlan_id: "{{ item.id }}"
state: active
state: merged
loop: "{{ vlans }}"
- name: Configure access ports
cisco.ios.ios_interfaces:
config:
- name: "GigabitEthernet1/0/1"
description: "User Access Port - VLAN 10"
enabled: true
- name: "GigabitEthernet1/0/2"
description: "User Access Port - VLAN 20"
enabled: true
state: merged
- name: Assign VLANs to access ports
cisco.ios.ios_l2_interfaces:
config:
- name: "GigabitEthernet1/0/1"
mode: access
access:
vlan: 10
- name: "GigabitEthernet1/0/2"
mode: access
access:
vlan: 20
state: merged
Creating Backup and Restore Solutions
Backing Up Configurations
Create a playbook to back up device configurations:
---
# backup_configs.yml
- name: Backup Cisco configurations
hosts: cisco
gather_facts: false
tasks:
- name: Get current date and time
set_fact:
backup_time: "{{ lookup('pipe', 'date +%Y-%m-%d-%H-%M-%S') }}"
run_once: true
- name: Create backup directory
file:
path: "./backups/{{ backup_time }}"
state: directory
run_once: true
delegate_to: localhost
- name: Backup running configurations
cisco.ios.ios_config:
backup: yes
backup_options:
filename: "{{ inventory_hostname }}_config.txt"
dir_path: "./backups/{{ backup_time }}"
Restoring Configurations
A playbook to restore configurations:
---
# restore_config.yml
- name: Restore Cisco configuration
hosts: cisco
gather_facts: false
vars_prompt:
- name: backup_dir
prompt: "Enter backup directory name (format: YYYY-MM-DD-HH-MM-SS)"
private: no
tasks:
- name: Check if backup exists
stat:
path: "./backups/{{ backup_dir }}/{{ inventory_hostname }}_config.txt"
register: backup_file
delegate_to: localhost
- name: Fail if backup doesn't exist
fail:
msg: "Backup file for {{ inventory_hostname }} does not exist in the specified directory"
when: not backup_file.stat.exists
- name: Restore configuration
cisco.ios.ios_config:
src: "./backups/{{ backup_dir }}/{{ inventory_hostname }}_config.txt"
when: backup_file.stat.exists
Network Compliance and Validation
Enforcing Security Policies
Create a playbook to enforce security best practices:
---
# security_compliance.yml
- name: Enforce security policies on Cisco devices
hosts: cisco
gather_facts: false
tasks:
- name: Configure secure SSH
cisco.ios.ios_config:
lines:
- ip ssh version 2
- ip ssh authentication-retries 3
- ip ssh time-out 60
- login block-for 120 attempts 3 within 60
save_when: modified
- name: Disable unused services
cisco.ios.ios_config:
lines:
- no service pad
- no ip http server
- no ip http secure-server
- no cdp run
save_when: modified
- name: Configure NTP
cisco.ios.ios_config:
lines:
- ntp server 192.168.1.250
- ntp server 192.168.1.251
save_when: modified
- name: Configure logging
cisco.ios.ios_config:
lines:
- logging host 192.168.1.200
- logging trap notifications
- logging buffered 16384
save_when: modified
Network State Validation
Create a playbook to validate network state:
---
# validate_network.yml
- name: Validate network state
hosts: cisco
gather_facts: false
tasks:
- name: Check device uptime
cisco.ios.ios_command:
commands: show version | include uptime
register: uptime_result
- name: Display uptime
debug:
msg: "{{ inventory_hostname }}: {{ uptime_result.stdout[0] }}"
- name: Check CPU utilization
cisco.ios.ios_command:
commands: show processes cpu | include CPU utilization
register: cpu_result
- name: Display CPU utilization
debug:
msg: "{{ inventory_hostname }}: {{ cpu_result.stdout[0] }}"
- name: Check memory usage
cisco.ios.ios_command:
commands: show memory summary | include Processor
register: memory_result
- name: Display memory usage
debug:
msg: "{{ inventory_hostname }}: {{ memory_result.stdout[0] }}"
- name: Check interface status
cisco.ios.ios_command:
commands: show ip interface brief
register: interface_result
- name: Create report directory
file:
path: "./reports"
state: directory
run_once: true
delegate_to: localhost
- name: Save interface status to file
copy:
content: "{{ interface_result.stdout[0] }}"
dest: "./reports/{{ inventory_hostname }}_interfaces.txt"
delegate_to: localhost
Real-World Automation Workflow Example
Let's create a complete workflow for deploying a standard branch office configuration:
---
# deploy_branch_office.yml
- name: Deploy branch office configuration
hosts: new_branch_router
gather_facts: false
vars:
branch_id: 101
branch_prefix: 10.{{ branch_id }}.0.0
branch_mask: 255.255.255.0
wan_ip: 203.0.113.{{ branch_id }}
wan_mask: 255.255.255.252
tasks:
- name: Configure hostname
cisco.ios.ios_config:
lines:
- hostname BRANCH-{{ branch_id }}
save_when: modified
- name: Configure WAN interface
cisco.ios.ios_config:
parents: interface GigabitEthernet0/0
lines:
- description WAN Interface to HQ
- ip address {{ wan_ip }} {{ wan_mask }}
- no shutdown
save_when: modified
- name: Configure LAN interface
cisco.ios.ios_config:
parents: interface GigabitEthernet0/1
lines:
- description Branch LAN Interface
- ip address {{ branch_prefix.split('.')[0] }}.{{ branch_prefix.split('.')[1] }}.1.1 {{ branch_mask }}
- no shutdown
save_when: modified
- name: Configure default route
cisco.ios.ios_config:
lines:
- ip route 0.0.0.0 0.0.0.0 {{ wan_ip | ipmath(1) }}
save_when: modified
- name: Configure DHCP service
cisco.ios.ios_config:
lines:
- ip dhcp excluded-address {{ branch_prefix.split('.')[0] }}.{{ branch_prefix.split('.')[1] }}.1.1 {{ branch_prefix.split('.')[0] }}.{{ branch_prefix.split('.')[1] }}.1.10
- ip dhcp pool BRANCH-{{ branch_id }}-POOL
- network {{ branch_prefix.split('.')[0] }}.{{ branch_prefix.split('.')[1] }}.1.0 {{ branch_mask }}
- default-router {{ branch_prefix.split('.')[0] }}.{{ branch_prefix.split('.')[1] }}.1.1
- dns-server 8.8.8.8 8.8.4.4
- lease 7
save_when: modified
- name: Configure basic security
cisco.ios.ios_config:
lines:
- service password-encryption
- no ip domain-lookup
- ip domain-name branch{{ branch_id }}.example.com
- crypto key generate rsa modulus 2048
save_when: modified
- name: Configure NTP and Logging
cisco.ios.ios_config:
lines:
- ntp server 10.0.0.1
- logging host 10.0.0.2
- logging trap informational
save_when: modified
- name: Verify configuration
cisco.ios.ios_command:
commands:
- show ip interface brief
- show ip route
register: verification
- name: Display verification output
debug:
var: verification.stdout_lines
Working with Cisco IOS Templates
Creating Jinja2 Templates
Create a file named interface_template.j2
:
{% for interface in interfaces %}
interface {{ interface.name }}
description {{ interface.description }}
{% if interface.ip_address is defined %}
ip address {{ interface.ip_address }} {{ interface.subnet_mask }}
{% endif %}
{% if interface.vlan is defined %}
switchport mode access
switchport access vlan {{ interface.vlan }}
{% endif %}
{% if interface.trunk is defined and interface.trunk %}
switchport mode trunk
switchport trunk allowed vlan {{ interface.allowed_vlans | default('all') }}
{% endif %}
{% if interface.enabled %}
no shutdown
{% else %}
shutdown
{% endif %}
{% endfor %}
Now, create a playbook that uses this template:
---
# configure_interfaces_template.yml
- name: Configure interfaces using template
hosts: cisco_switches
gather_facts: false
vars:
interfaces:
- name: GigabitEthernet1/0/1
description: User Port - Finance Department
vlan: 10
enabled: true
- name: GigabitEthernet1/0/2
description: User Port - Marketing Department
vlan: 20
enabled: true
- name: GigabitEthernet1/0/3
description: Printer Port
vlan: 30
enabled: true
- name: GigabitEthernet1/0/24
description: Uplink to Distribution Switch
trunk: true
allowed_vlans: "10,20,30,99"
enabled: true
tasks:
- name: Generate configuration from template
template:
src: interface_template.j2
dest: ./tmp/{{ inventory_hostname }}_interfaces.cfg
delegate_to: localhost
- name: Apply interface configuration
cisco.ios.ios_config:
src: ./tmp/{{ inventory_hostname }}_interfaces.cfg
Troubleshooting and Debugging
Ansible provides several ways to troubleshoot issues when automating Cisco devices:
Increasing Verbosity
Run playbooks with increased verbosity to see more details:
ansible-playbook -vvv your_playbook.yml
Using Check Mode
Test playbooks without making actual changes:
ansible-playbook --check your_playbook.yml
Using Debug Module
Add debug tasks to your playbooks:
- name: Debug registration variable
debug:
var: your_registered_variable
verbosity: 1
Common Issues and Solutions
Here's a diagram showing common issues and their solutions:
Advanced Topics
Using Ansible Roles for Cisco Automation
Create a standardized role structure for Cisco automation:
cisco_automation_role/
├── defaults/
│ └── main.yml
├── tasks/
│ ├── main.yml
│ ├── backup.yml
│ ├── interfaces.yml
│ └── security.yml
├── templates/
│ ├── interfaces.j2
│ └── security.j2
├── vars/
│ └── main.yml
└── README.md
Continuous Network Compliance
Create a playbook for automated compliance checks:
---
# compliance_check.yml
- name: Network compliance verification
hosts: cisco
gather_facts: false
tasks:
- name: Check for security compliance
cisco.ios.ios_command:
commands:
- show run | include password
- show run | include ssh
- show run | include access-list
register: security_checks
- name: Analyze compliance results
set_fact:
weak_passwords: "{{ security_checks.stdout[0] | regex_findall('password .{1,5}$') }}"
ssh_v1: "{{ 'ip ssh version 1' in security_checks.stdout[1] }}"
- name: Generate compliance report
copy:
content: |
Compliance Report for {{ inventory_hostname }}
========================================
Weak Passwords Found: {{ weak_passwords | length }}
{% if weak_passwords | length > 0 %}
Weak Password Lines:
{% for line in weak_passwords %}
- {{ line }}
{% endfor %}
{% endif %}
SSH Version 1 Enabled: {{ ssh_v1 }}
Raw Security Output:
{{ security_checks.stdout | join('
') }}
dest: "./compliance/{{ inventory_hostname }}_report.txt"
delegate_to: localhost
Summary
In this guide, we've explored how to use Ansible to automate Cisco network devices, including:
- Setting up your Ansible environment for Cisco automation
- Creating playbooks for basic device information gathering
- Configuring interfaces, VLANs, and security settings
- Implementing backup and restore solutions
- Validating network state and compliance
- Using templates for more flexible configuration management
- Advanced topics like roles and continuous compliance
Ansible's declarative approach to network automation helps ensure consistency, reduce errors, and increase efficiency in your Cisco network operations. By leveraging the techniques covered in this guide, you can build a more reliable, consistent, and manageable network infrastructure.
Additional Resources
To continue learning about Ansible Cisco automation:
- Practice with the provided examples on a lab environment
- Explore the official Ansible Network Automation documentation
- Join networking communities like Network to Code or DevNet
- Try implementing these automation techniques on a small set of devices before rolling out to production
Exercises
- Create a playbook that collects and formats "show version" output from all your Cisco devices
- Develop a template to standardize SNMP configuration across your network
- Build a complete role for deploying a standard switch configuration
- Create a playbook that validates BGP neighbor relationships on your routers
- Implement a workflow that can detect and report on configuration drift across your network
If you spot any mistakes on this website, please let me know at feedback@compilenrun.com. I’d greatly appreciate your feedback! :)