Anisble documentation or examples for Cisco devices appear to be a bit hit-n-miss, so I'm documenting my "Hello World Primer" with hope it'll be helpful to others; as this is quite long it will be two posts, one for getting started with a simple "show clock" and a 2nd for making changes. Part two making changes is available here.
TLDR; Some example files are on github: https://github.com/linickx/ansible-cisco
To get started, you need ansible installed on your Laptop/PC, I'm running OSX with homebrew so installation is a simple brew install ansible
.
Getting Started - A sane ansible.cfg
Once anisble is installed, the first thing you need an inventory/hosts's file... a lot of the linux examples will get you to start fiddling about with /etc/ansible
, firstly this folder doesn't exist on OSX and secondly this is a bad idea on linux as this is a system wide change.
IMHO a better start is to create an ansible directory in your home: mkdir ~/ansible;cd ~/ansible
In your personal ansible directory, create ansible.cfg; in this file you can set a local inventory file and some sane defaults, mine looks like this:
linickx:ansible $ cat ansible.cfg
[defaults]
hostfile = ./inventory.txt
retry_files_enabled = False
timeout = 5
host_key_checking = False
#log_path= ./log.txt
linickx:ansible $
As you can see I have a few other thing set, firstly my local hosts file (inventory.txt) and secondly ansible fails it creates a local retry file, I don't want these. Timeout is ssh timeout, use whatever you like, something between 1 and 10 works for me.
host_key_checking
has security implications. The above is set to False
because this is my personal test machine, in Production I would set this to True
as I want to know if an SSH MitM is happening.
Finally, notice the #
, this is a comment; I have commented out the log_path directive, if you enable it, ansible will log all screen output to a file, useful for searching for something instead of scrolling through your terminal.
The Inventory File - Your personal list of devices
The inventory defines the devices you want to manage, anisble expects all devices to have resolvable FQDNs, which is unlikely in most enterprises for Cisco devices, if you're a fan of remembering IP addresses you can have a simple list of IP's but if you're like me an like to know the names of devices you can do something like this...
linickx:ansible $ cat inventory.txt
[all:vars]
# OSX / Homebrew hack, as I have python3 installed.
ansible_python_interpreter=/usr/local/Cellar/python/2.7.13/bin/python2.7
[ios_devices]
r1 ansible_host=10.10.10.135
r2 ansible_host=10.10.10.136
[ios_devices_enable]
r3 ansible_host=10.10.10.137
linickx:ansible $
In my inventory file, the []
are group names, I have two custom groups [ios_devices]
and [ios_devices_enable]
, you can call these anything you want, my naming structure will make more sense as this post continues.
All devices belong to a default [all]
group that doesn't need to be defeined in the file, but in my config file I have [all:vars]
this means: set the following variables for the all group; I have python3 installed for my other python projects, so ansible_python_interpreter
is a hack to point ansible to the correct version of python (Python 3 is not supported - yet - for ansible)
For each device listed in my file (r1,r2,r3, etc) I have ansible_host
set to an IP address, that's because my test devices do not have resolvable FQDNs.
Hello World! ... or Show Clock play
Ansible is primarily about Play Books, a Play Book is a list of tasks (instructions) that you want to perform on one or many devices; the idea is that you create repeatable plays to standardise configuration and such in your environment.
For my Hello World! example, I'm not going to make a change but show you how to run show clock
on many devices (using the ios_command module), in ansible world this is a play.
Start by creating a file called show_clock.yml
with the following contents ( including the first --- ):
---
- hosts: ios_devices*
gather_facts: no
connection: local
tasks:
- name: Include Login Credentials
include_vars: secrets.yml
- name: Define Provider
set_fact:
provider:
host: "{{ ansible_host }}"
username: "{{ creds['username'] }}"
password: "{{ creds['password'] }}"
- name: RUN 'Show Clock'
ios_command:
provider: "{{ provider }}"
commands:
- show clock
register: clock
- debug: var=clock.stdout_lines
This playbook has three tasks: 1. Include Login Credentials, i.e. Read login credentials from a file 2. Define Provider, i.e. Use the credentials in something called a "fact" that is named "provider" 3. Run show clock
Read login credentials from a file
You'll need a credential file, so create secrets.yml
with contents like this (update as appropriate):
---
creds:
username: nick
password: my_password
auth_pass: my_enable
Something called a "fact" that is named "provider"
The provider is what tells ansible how to connect to a device, all networking equipment needs one, so the provider includes the ip address and credentials, a fact is a device specific variable, so this task is assigning connectivity details to the device. Anything inside {{ }}
is a variable.
Run show clock ...
To execute the Play Book or play, you use the ansible-playbook
command, like this...
linickx:ansible $ ansible-playbook show_clock.yml
PLAY [ios_devices*] ************************************************************
TASK [Include Login Credentials] ***********************************************
ok: [r1]
ok: [r2]
ok: [r3]
TASK [Define Provider] *********************************************************
ok: [r2]
ok: [r1]
ok: [r3]
TASK [RUN 'Show Clock'] ********************************************************
ok: [r1]
ok: [r3]
ok: [r2]
TASK [debug] *******************************************************************
ok: [r1] => {
"clock.stdout_lines": [
[
"18:58:24.812 UTC Mon Mar 27 2017"
]
]
}
ok: [r2] => {
"clock.stdout_lines": [
[
"18:58:23.490 UTC Mon Mar 27 2017"
]
]
}
ok: [r3] => {
"clock.stdout_lines": [
[
".18:58:25.064 UTC Mon Mar 27 2017"
]
]
}
PLAY RECAP *********************************************************************
r1 : ok=4 changed=0 unreachable=0 failed=0
r2 : ok=4 changed=0 unreachable=0 failed=0
r3 : ok=4 changed=0 unreachable=0 failed=0
linickx:ansible $
What happened there then?!
In my ansible-playbook show_clock.yml
you'll see the three tasks ran against three routers; in my show_clock.yml
the first line hosts: ios_devices*
says run against any wildcard group, starting with ios_devices, which in my inventory.txt
is both ios_devices
and ios_devices_enable
.
Notice how there where four tasks not three, the -debug
entry was an unnamed task which simply outputs the results of the previous task :)
But storing credentials in a file is INSECURE!!
Anyone that's been on linickx.com before will find that example of storing a username/password in a plain text file highly out of character, so lets look at how to prompt for the credentials instead; create show_clock_prompt.yml
with the following:
---
- hosts: ios_devices
gather_facts: no
connection: local
vars_prompt:
- name: "mgmt_username"
prompt: "Username"
private: no
- name: "mgmt_password"
prompt: "Password"
vars:
provider:
host: "{{ ansible_host }}"
username: "{{ mgmt_username }}"
password: "{{ mgmt_password }}"
tasks:
- name: RUN 'Show Clock'
ios_command:
provider: "{{ provider }}"
commands:
- show clock
register: clock
- debug: var=clock.stdout_lines
Notice a few changes, firstly no *
, this play will be run just against one inventory group [ios_devices]
, secondly one-ish task: RUN 'Show Clock'
. In this example, before running any tasks we prompt for some input and save the provider variables, once we have those we can then run the tasks. Run the play.. ansible-playbook show_clock_prompt.yml
:
linickx:ansible $ ansible-playbook show_clock_prompt.yml
Username: nick
Password:
PLAY [ios_devices] *************************************************************
TASK [RUN 'Show Clock'] ********************************************************
ok: [r2]
ok: [r1]
TASK [debug] *******************************************************************
ok: [r1] => {
"clock.stdout_lines": [
[
"19:12:37.863 UTC Mon Mar 27 2017"
]
]
}
ok: [r2] => {
"clock.stdout_lines": [
[
"19:12:39.742 UTC Mon Mar 27 2017"
]
]
}
PLAY RECAP *********************************************************************
r1 : ok=2 changed=0 unreachable=0 failed=0
r2 : ok=2 changed=0 unreachable=0 failed=0
linickx:ansible $
This time, we're prompted for credentials and less tasks take place.
Download the files from github!
My ansible files are on github, feel free to download them: https://github.com/linickx/ansible-cisco
Once you're happy with this, check out Primer 2 making changes .