Ansible is a nice tool to automate the deployment and configuration of network devices. I wrote the following playbook to automate the upgrade of Cisco IOS devices. This playbook has been tested successfully to upgrade a Cisco CSR1000v router and can be easily tweaked to support Cisco Nexus and Arista switches.

My Setup:

– Python 2.7.6 and Ansible 2.2 running on Ubuntu 14.4.5 LTS (codename: Trusty)

– Cisco CSR1000v running IOS-XE 3.10.S


– Obviously you must have Ansible version 2.2 or higher installed and configured properly to access via SSH the network devices defined in your hosts file. 

– You must also have ntc_ansible installed. The ntc_ansible modules were written by Jason Edelman; follow the instructions on Github to install them

– Some of the ntc_ansible modules have dependency on the pyntc library. Pyntc is an open-source multi-vendor library which makes it easier to copy files and upgrade network devices. Follow the steps here to install the library.

   Note: the pyntc package install should also install the future library which is required for pyntc to work. If Ansible spits out “No module named builtins” errors when you run the playbook, that means the future library is missing from your system and you can install it by executing sudo pip install future. A quick way to find out if the future library is installed on your system is by doing import pyntc from the Python interpreter. If the import works then both the pyntc and future libraries have been installed successfully.

– In my setup, Ansible is authenticating against the devices using username/password credentials. If you prefer to use Public key authentication instead, here is a quick tutorial on how to enable SSH RSA authentication on a Cisco router.


– name: Upgrade a Cisco IOS router

  hosts: csr





       gather_subset: hardware

       provider: “{{cli}}” 

    tags: always




      platform: cisco_ios_ssh

      local_file: images/{{ new_image }}

      host: “{{ inventory_hostname }}”

      username: “{{ username }}”

      password: “{{ password }}”

    when: ansible_net_version != “{{version}}”

    tags: copy





         – no boot system 

         – boot system flash bootflash:{{new_image}}

       provider: “{{cli}}”

       host: “{{ inventory_hostname }}”

    when: ansible_net_version != “{{version}}”

    tags: install




        platform: cisco_ios_ssh

        host: “{{ inventory_hostname }}”

        username: “{{ username }}”

        password: “{{ password }}”

        local_file: backup/{{ inventory_hostname }}.cfg

    when: ansible_net_version != “{{version}}”

    tags: backup




      platform: cisco_ios_ssh

      confirm: true

      timer: 2

      host: “{{ inventory_hostname }}”

      username: “{{ username }}”

      password: “{{ password }}”

    when: ansible_net_version != “{{version}}”

    tags: reload




         port: 22

         host: “{{inventory_hostname}}”

         timeout: 300

  – ios_command:

        commands: ping

        provider: “{{cli}}”


        – result[0] contains “!!!”

    register: result

    failed_when: “not ‘!!!’ in result.stdout[0]”

    tags: verify

The playbook is pretty straightforward and consists of 6 tasks:

1. GATHERING FACTS: the first task uses the ios_facts (core module that ships with Ansible itself) to gather facts about the device and see if it needs an upgrade. If the image running on the device matches the target image, Ansible skips that device. Otherwise it collects the facts and moves on to the next task.

2. COPYING IMAGE TO DEVICE FLASH:This task checks if the image file is available on the device flash and copies the file to flash if it doesn’t exist. 

  Note: this module uses SCP/Netmiko to copy the file. If you want to use this module with a Cisco Nexus switch, you will need to enable SCP on the switch (not a requirement for Cisco IOS) by doing: ip scp server enable

3. SETTING BOOT IMAGE: this task sets the boot image. Simple. 

4. SAVING CONFIGS: this task saves the running-configs as startup configs on the device and also saves a copy of the configs locally on the host in the backup folder

5. RELOADING THE DEVICE: This task reloads the device after a time interval (2 minutes) 

6. VERIFYING CONNECTIVITY: the final task waits for the device for 5 minutes to come up and become accessible via SSH before it pings a root DNS to verify internet connectivity. If the ping is not successful, Ansible generates an error.

This is it. Now you can run a single command for all of your devices and let Ansible do its magic.

Next I will be doing some work to automate Junos devices. Stay tuned for that post.
You can find the source code including the ansible configuration and variable files in my Github repo.

Here are also some resources that helped me in my Ansible learning journey:

Up and Running with Ansible (eBook)

Ivan Pepeljak’s Blog

Share This: