First simple Ansible playbooks

So, your lab is set up and waiting for something meaningful to do?
This post introduces the two probably most commonly used networking modules in the Cisco IOS world – it’s no rocket science to use other vendors’ modules in the same way, by the way. IOS_command executes, well, commands at the privileged level, while IOS_config is used in config mode – no surprise there, right?

ios_command

The inventory under /etc/ansible/hosts needs to be populated with some lab devices and ssh access from the control node / ansible host to each device should be verified up front.

[ios]
CSR-1 ansible_host=192.168.10.211 ansible_network_os=ios
CSR-2 ansible_host=192.168.10.212 ansible_network_os=ios

We want to learn something about the configured syslog servers, so the first playbook ios_command.yml looks like:

---
- name: DEMO IOS_COMMAND
  hosts: ios
  gather_facts: false
  connection: network_cli

  tasks:

  - name: SHOW COMMAND
    ios_command:
      commands: show run | incl logging host

Time to execute via ansible-playbook. Due to option -k the local user (nwmichl) is used to log into the lab device using the prompted ssh password. Use the option -u <USER> to accommodate the connection method in case of a different user on the lab devices.

Verbose mode

Hmm, plenty of green OKs (which we like very much), but no insights about this syslog thing … To see what actually happened, add -v (verbose) to the ansible-playbook command.

Ha, welcome to JSON hell, not very human readable at all, but something like jsonformatter comes to rescue.

{
  "ansible_facts": {
    "discovered_interpreter_python": "/usr/bin/python"
  },
  "changed": false,
  "stdout": [
    "logging host 192.168.1.12"
  ],
  "stdout_lines": [
    [
      "logging host 192.168.1.12"
    ]
  ]
}

Seems that we changed nothing (changed: false) and that at least CSR-1 has a configured syslog receiver with IP 192.168.1.12.
The result of the ios_command is reported back via stdout and stdout_lines. So, let’s complement the playbook with another task to only show the stdout_lines, if not empty:

---
- name: DEMO IOS_COMMAND
  hosts: ios
  gather_facts: false
  connection: network_cli

  tasks:

  - name: SHOW COMMAND
    ios_command:
      commands: show run | incl logging host
    register: result

  - debug:
      msg: "{{ result.stdout_lines[0] }}"
    when: result.stdout[0] != ""

Debug task

First, register the output of the ios_command in the variable result. This variable exists for each host / network device in the play, but only for the runtime of the next task. (Use set_fact if you would like to extend the lifetime).

The new task debug prints out a message about an operation on the result variable. ‘result.stdout_lines[0]‘ means: Take the first element of stdout_lines in the dictionary ‘result‘ – this element represents a list of lines. But only (when) the returned value is not empty.

Another run without option -v:

Ok, this output is more readable and sufficient for a fast overview of even a large number of devices queried. It is of course possible to save this and more sophisticated information about our network devices in files via Jinja2 templates, for example, but I want to keep it simple at first.

ios_config

We learned from the output above, that someone has forgotten to set a syslog receiver on CSR-2. Time to introduce the second network module ‘ios_config’ and (carefully) change the configuration by means of a new playbook named ios_config.yml:

---
- name: DEMO IOS_CONFIG
  hosts: ios
  gather_facts: false
  connection: network_cli

  tasks:

  - name: CONFIGURE SYSLOG SERVER
    ios_config:
      lines:
        - logging host 192.168.1.12
      save_when: changed

Dry run

It’s common best practice, to execute configuration playbooks with the –check option first, to see what will be changed. Ansible fetches the running config of each device and compares the state with the desired change by the ios_config module. By using the verbose mode (-v), you can evaluate what would actually be configured on the device CLI.

That’s what would be expected. CSR-1 already has this syslog server configured, so nothing will be changed [OK:]. CSR-2 on the other hand is missing the syslog configuration, so ios_config will be executed on this host [changed:], followed by the inevitable copy run start.

Idempotence

Now to the beauty of build in idempotence. If this playbook is executed again, nothing will change, because the running config of both devices already contains the line ‘logging host 192.168.1.12‘.

Idempotence doesn’t mean declarative

Please keep in mind that this playbook only ensures, that 192.168.1.12 is configured as a syslog destination. It does NOT check for other configured syslog receiver and deconfigures them! There are ways to achieve the goal of declarative / desired state configuration (ONLY 192.168.1.12) with ansible, but that is something for a future blog post.

Summary

Even those simple playbooks can already ease the burden of operating a (large) network in terms of information gathering or reliable configuration one-offs on plenty of devices. Prove compliance regarding some config details during an audit, check global cdp status to mitigate CDPwn or just deploy a new vlan on many devices, the possibilities are endless. Just change the commands or config lines in the playbook examples above according to your needs.

One thought on “First simple Ansible playbooks

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.