Upgrading Cisco IOS Devices with Ansible
26 Jun 2018Upgrading network images to stable and or later versions is nothing new in the networking world. A recent discussion with a customer, however, encouraged the creation of a simple, yet effective playbook to help automate this process.
The two use cases were around:
-
CVE. A vulnerability is reported and an image upgrade is required to resolve it.
-
Compliance checking. Ensure that the networking devices are at the compliant and approved version of code, and if not, make sure that it is.
At the time, the customer would SSH into each device to upgrade the image, wait for it to boot back up, check to make sure the image was upgraded to the correct version, then move onto the next device. In the case of the vulnerability, this became an all hands on deck activity that resulted in many hours from multiple engineers to patch the network devices.
Now, let’s take a look at an Ansible Playbook that can automate this process.
---
- name: UPGRADE ROUTER FIRMWARE
hosts: routers
connection: network_cli
gather_facts: no
vars:
compliant_ios_version: 16.08.01a
tasks:
- name: GATHER ROUTER FACTS
ios_facts:
- name: UPGRADE IOS IMAGE IF NOT COMPLIANT
block:
- name: COPY OVER IOS IMAGE
command: "scp system-image-filename.bin {{inventory_hostname}}:/system-image-filename.bin"
- name: SET BOOT SYSTEM FLASH
ios_config:
commands:
- "boot system flash:system-image-filename.bin"
- name: REBOOT ROUTER
ios_command:
commands:
- "reload\n"
- name: WAIT FOR ROUTER TO RETURN
wait_for:
host: "{{inventory_hostname}}"
port: 22
delay: 60
delegate_to: localhost
when: ansible_net_version != compliant_ios_version
- name: GATHER ROUTER FACTS FOR VERIFICATION
ios_facts:
- name: ASSERT THAT THE IOS VERSION IS CORRECT
assert:
that:
- compliant_ios_version == ansible_net_version
Let’s step through the components!
First, the playbook level variables:
---
- name: UPGRADE ROUTER FIRMWARE
hosts: routers
connection: network_cli
gather_facts: no
vars:
compliant_ios_version: 16.08.01a
tasks:
We’re passing in the following:
name
- Naming our playbook.
hosts
- What nodes are we targeting? In this case, we are targeting a group called routers.
connection
- Defining the connection type.
gather_facts
- This is a server specific task and does not apply to us.
vars
- Defining a variable that we will use to compare IOS versions.
tasks
- Where we will list the tasks we want the playbook to accomplish.
The first task:
- name: GATHER ROUTER FACTS
ios_facts:
The above tasks runs the ios_facts module which collects facts from remote devices running Cisco IOS.
ios_facts_module
The ios_facts
module provides us with the ansible_net_version
which defines the operating system version running on the remote device. We’ll be using this as conditional logic in our proceeding tasks.
Next, we create a block
which contains a list of tasks. We put a conditional on this block
with a when
statement near the bottom of the playbook - or at the end of the block
. The when
statement does a check to see if the current version of the remote device is NOT equal to the version we specify. If it’s NOT equal, the tasks in the block will execute and proceed to upgrade the remote device to the version of code that’s required.
- name: UPGRADE IOS IMAGE IF NOT COMPLIANT
block:
- name: COPY OVER IOS IMAGE
command: "scp system-image-filename.bin {{inventory_hostname}}:/system-image-filename.bin"
- name: SET BOOT SYSTEM FLASH
ios_config:
commands:
- "boot system flash:system-image-filename.bin"
- name: REBOOT ROUTER
ios_command:
commands:
- "reload\n"
- name: WAIT FOR ROUTER TO RETURN
wait_for:
host: "{{inventory_hostname}}"
port: 22
delay: 60
delegate_to: localhost
when: ansible_net_version != compliant_ios_version
The first task within the block is to copy over the Cisco IOS image to the remote device. We utilize the SCP
command to do so.
Next, we utilize the ios_config
module to set the boot system of the remote device so it’ll use the image the new image if the version is not compliant or what we want it to be.
We are now ready to reboot the remote device so it boots into the correct image. We pass in “reload\n” into the ios_command
to perform the reload.
The next task, as the name suggests, waits for
the remote device to reachable again. We specify the port we want to test reachability against and an initial delay before we begin to poll for reachability. Once the remote device is reachable, we do a final verification of the version.
- name: GATHER ROUTER FACTS FOR VERIFICATION
ios_facts:
- name: ASSERT THAT THE IOS VERSION IS CORRECT
assert:
that:
- compliant_ios_version == ansible_net_version
With the remote device having been rebooted, we execute the ios_facts
module again to capture the new ansible_net_version
.
Finally, we use the assert
module to ensure that the upgrade was successful and the remote device is running the compliant IOS version. By using the assert
module, we make the playbook fail if it is NOT the correct version.