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?


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.

CSR-1 ansible_host= ansible_network_os=ios
CSR-2 ansible_host= ansible_network_os=ios

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

  hosts: ios
  gather_facts: false
  connection: network_cli


  - name: SHOW 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"
  "stdout_lines": [
      "logging host"

Seems that we changed nothing (changed: false) and that at least CSR-1 has a configured syslog receiver with IP
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:

  hosts: ios
  gather_facts: false
  connection: network_cli


  - name: SHOW 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.


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:

  hosts: ios
  gather_facts: false
  connection: network_cli


        - logging host
      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.


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‘.

Idempotence doesn’t mean declarative

Please keep in mind that this playbook only ensures, that 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 with ansible, but that is something for a future blog post.


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 Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

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