Eric Stewart: Running Off At The Mouth

Learning Ansible in (not for) Cisco Modeling Labs

by Eric Stewart on Jul.21, 2026, under Networking, Technology

I’ve been working on learning Ansible at work on and off (mostly off).  I’ve hit issues along the way with just Googling and since I learn more by doing, have avoided asking those in my group familiar with Ansible for assistance.  In this post I’m going to outline what it took to get things running.

This post assumes some level of familiarity with Cisco IOS switches, at least some basic routing, and a solid understanding of the Linux and Cisco command lines.

First, let’s talk about the network (CML YAML download): There’s not much to it.  It’s an Ubuntu box with connections to a “dumb” switch and then NAT to the outside world.  The dumb switch is essentially a management network (indeed, the Cisco images connected to it will have a management VRF with one interface on this network, preconfigured).  The connection to the outside world is needed to run updates and to install new packages (which, as I am very bad at taking notes, I may miss a step somewhere – my bad).  The three Cisco images are IOSvL2 images, and they’re in a simple line – SW1 connects to SW2, which connects to SW3.  Nothing other than a management VRF connection is preconfigured, leaving the rest to be done 100% through Ansible … when we figure enough out about it to actually do configurations.

A CML network showing three IOSvL2 devices connected to a management network that includes an ubuntu box.All the usernames and passwords are, to make my life easier, “cisco”.  Obviously, this isn’t production, and you’ll want to secure the Ubuntu box a lot better than I did if you change your external connection to a bridge mode and give it an IP on anything resembling a more “open” network (which you MAY want to do if you want to log into something other than the console).  Thinking about the future, you could always use Ansible, once you think you’re savvy enough, to actually secure the switches with a better username and password (and maybe even more complex authentication).  However, get it wrong and you could lock yourself out of the switches.

Prepping your Ubuntu box

There are two ways to run commands that need to be run as root: you can preface every command with “sudo”, or you can live dangerously and just run “sudo /bin/bash” which will put you into a root shell.  I live dangerously, especially on lab equipment.  So, all of these commands you’ll want to run assume you are, somehow, root, and some of these may already be installed by default:

apt update
apt upgrade
apt install ansible python3-paramiko
ansible-galaxy collection install cisco.ios cisco.nxos

Next, you have some options when you put in an ansible.cfg file.  I went the route of installing one in /etc/ansible, but the option of having a .ansible directory (I think – I haven’t looked into it yet).  Mine is:

[persistent_connection]
ssh_type = paramiko

[ssh_connection]
ssh_args = -o StrictHostKeyChecking=accept-new

Note that the SSH connection entry isn’t working as desired for me, but the workaround at this point is to just log in via ssh to the management addresses of the switches from your Ubuntu box and accept the keys.

Create an “ansible” directory …

Then, make sure the following directories exist:

cisco@ansible:~/ansible$ ls -l
total 20
drwxrwxr-x 2 cisco cisco 4096 Jul 20 19:23 inventory
drwxrwxr-x 2 cisco cisco 4096 Jun 23 10:52 logs
drwxrwxr-x 2 cisco cisco 4096 Jul 20 17:57 show-output
drwxrwxr-x 2 cisco cisco 4096 Jul 20 17:38 vars_files

You’re going to need them.

Inventory

A lot of the files are usually done in some form of YAML or JSON, but an acceptable inventory format is the ini, so create a switches.ini file in the inventory directory (this need not be done as root):

[Switches]
sw1 ansible_host=192.168.254.1
sw2 ansible_host=192.168.254.2
sw3 ansible_host=192.168.254.3

[Switches:vars]
ansible_network_os=cisco.ios.ios
ansible_connection=ansible.netcommon.network_cli

You’re essentially declaring your three switches with a name, and rather than relying on any DNS, giving their IPs.  Then you’re indicating what Cisco language they speak (IOS) and how to connect to them.

Secrets!

Usernames and passwords are probably not a good idea to put into plain text, so, go into the vars_files directory and create a new secrets file:

ansible-vault create secret.yaml

This should open a vi/vim editing instance where you’re allowed to put in some data, and once you exit it will write out an encrypted version of the file.

ansible_connection: network_cli
ansible_user: cisco
ansible_ssh_pass: cisco

Not much to it at this point.  If at any point you need to edit this file, just replace “create” with “edit” on the command line.

Baby’s First Ansible Playbook

In the main “ansible” directory, I have a “test.yaml”:

---

- name: Testing Switches
  hosts: Switches
  gather_facts: false
  vars_files:
    - ./vars_files/secret.yaml

tasks:

  - name: Show int status
    cisco.ios.ios_command:
      commands:
        - show int status
    register: config

  - name: save output
    copy:
      content: "{{ config.stdout | replace('\\n', '\n') }}"
      dest: "show-output/{{ inventory_hostname }}.ios"

All this does is log into each of the switches, run a “show int status”, and then dump the output to a file.  This is basically the Ansible version of crawling before you start running.

Run It!

cisco@ansible:~/ansible$ ansible-playbook test.yaml -i ./inventory/switches.ini -J
Vault password:

PLAY [Testing Switches] ********************************************************

TASK [Show int status] *********************************************************
ok: [sw2]
ok: [sw1]
ok: [sw3]

TASK [save output] *************************************************************
ok: [sw1]
ok: [sw2]
ok: [sw3]

PLAY RECAP *********************************************************************
sw1 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=
0 ignored=0 
sw2 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=
0 ignored=0 
sw3 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=
0 ignored=0

cisco@ansible:~/ansible$

Mind you I’ve run mine several times so the first time the output might be different.  And you may run into errors.  Check the output:

cisco@ansible:~/ansible$ cat show-output/sw1.ios 
['Port Name Status Vlan Duplex Speed Type 
Gi0/0 connected 1 a-full auto RJ45
Gi0/1 notconnect 1 a-full auto RJ45
Gi0/2 notconnect 1 a-full auto RJ45
Gi0/3 notconnect 1 a-full auto RJ45
Gi1/0 notconnect 1 a-full auto RJ45
Gi1/1 notconnect 1 a-full auto RJ45
Gi1/2 notconnect 1 a-full auto RJ45
Gi1/3 notconnect 1 a-full auto RJ45
Gi2/0 notconnect 1 a-full auto RJ45
Gi2/1 notconnect 1 a-full auto RJ45
Gi2/2 notconnect 1 a-full auto RJ45
Gi2/3 notconnect 1 a-full auto RJ45
Gi3/0 notconnect 1 a-full auto RJ45
Gi3/1 notconnect 1 a-full auto RJ45
Gi3/2 notconnect 1 a-full auto RJ45
Gi3/3 connected routed a-full auto RJ45']

Formatting of course broken by copypasta, but if it successfully ran, you should see the results of the command here.

Next Steps?

Now I just need to take the time to develop a more complex playbook that:

  1. Configures each switch with appropriate interfaces on appropriate subnets, perhaps including a loopback
  2. Configures a routing protocol on all switches
  3. Waits enough time for convergence to occur
  4. Checks the routing tables

Hopefully I’ll get around to either adding to this post or writing a new one when I get to that point.


Hi! Did you get all the way down here and not find an answer to your question? The two preferred options for contacting me are:
  • Twitter: Just start your Twitter message with @BotFodder and I'll respond to it when I see it.
  • Reply to the post: Register (if you haven't already) on the site, submit your question as a comment to the blog post, and I'll reply as a comment.

Leave a Reply

You must be logged in to post a comment.