Introduction

In this article I will share my top 5 Ansible tips and tricks, that I hope will save you time when automating your networks with Ansible.

List Tasks

Ansible provides a rather handy little option to print each of your tasks for your plays via the option --list-tasks. Certainly a time saver, rather than having to write grep statements each time.

Below shows an example:

$ ansible-playbook playbooks/create_bf_snapshot.yml --list-tasks
playbook: playbooks/create_bf_snapshot.yml

  play #1 (localhost): Create Batfish Snapshot Directory        TAGS: []
    tasks:
      Create Snapshot Directory /       TAGS: []
      Create Snapshot Directory /configs        TAGS: []
      Create Snapshot Directory /hosts  TAGS: []
      Create Snapshot Directory /iptables       TAGS: []

  play #2 (linux): Create Batfish Snapshot Files for Hosts      TAGS: []
    tasks:
      Generate (via J2) and Save Host IP JSON   TAGS: []
      Collect IPTable Config    TAGS: []
      Save IPTable Config to Snapshot   TAGS: []

  play #3 (netdev): Create Batfish Snapshot Files for Net Devices       TAGS: []
    tasks:
      Collect ASA Network Device Configs        TAGS: []
      Collect JunOS Network Device Configs      TAGS: []
      Save ASA Network Device Configs to Snapshot       TAGS: []
      Save JunOS Network Device Configs to Snapshot     TAGS: []

Start at Task

A useful little option when needing to debug your tasks and playbooks is --start-at-task. This simply allows you to start the execution of your playbook at a given task. Below shows the syntax:

ansible-playbook push-config.yml --start-at-task="Clear ACLs"

Graph Inventory

The ansible-inventory option --graph allows you to, you guessed it - graph your inventory. Extremely handy when you have a large inventory and/or lots of groups.

Below shows an example:

$ ansible-inventory -i inventory/hosts --graph
@all:
  |--@linux:
  |  |--db
  |  |--websrv
  |--@netdev:
  |  |--@asa:
  |  |  |--fw1-asa
  |  |  |--fw2-asa
  |  |--@junos:
  |--@ungrouped:

List Inventory

Another inventory option is --list. This option displays the various variables and also group hierarchies from within your inventory. Again, extremely useful for large and complex inventory sets.

$ ansible-inventory -i inventory/hosts --list
{
    "_meta": {
        "hostvars": {
            "db": {
                "ansible_become_password": "cisco",
                "ansible_become_user": "root",
                "ansible_connection": "ssh",
                "ansible_host": "172.29.132.201",
                "ansible_ssh_pass": "cisco",
                "ansible_user": "cisco"
            },
            "fw1-asa": {
                "ansible_become": "yes",
                "ansible_become_method": "enable",
                "ansible_become_password": "cisco",
                "ansible_connection": "network_cli",
                "ansible_host": "172.29.132.100",
                "ansible_network_os": "asa",
                "ansible_ssh_pass": "cisco",
                "ansible_user": "cisco"
            },
            "fw2-asa": {
                "ansible_become": "yes",
                "ansible_become_method": "enable",
                "ansible_become_password": "cisco",
                "ansible_connection": "network_cli",
                "ansible_host": "172.29.132.200",
                "ansible_network_os": "asa",
                "ansible_ssh_pass": "cisco",
                "ansible_user": "cisco"
            },
            "websrv": {
                "ansible_become_password": "cisco",
                "ansible_become_user": "root",
                "ansible_connection": "ssh",
                "ansible_host": "172.29.132.101",
                "ansible_ssh_pass": "cisco",
                "ansible_user": "cisco"
            }
        }
    },
    "all": {
        "children": [
            "linux",
            "netdev",
            "ungrouped"
        ]
    },
    "asa": {
        "hosts": [
            "fw1-asa",
            "fw2-asa"
        ]
    },
    "linux": {
        "hosts": [
            "db",
            "websrv"
        ]
    },
    "netdev": {
        "children": [
            "asa",
            "junos"
        ]
    }
}

Pretty-Print Play Errors

By default when an Ansible play fails the result is a blob of text, like so, containing the error details:

TASK [Upload Configuration to Device] *************************************************************************************************
fatal: [fw1]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "cmd": "scp -o \"StrictHostKeyChecking no\" './data/snapshots/snapshot/configs/fw1.cfg' \"cisco\"@\"172.29.132.100\":\"disk0:configuration.txt\"", "delta": "0:00:21.205542", "end": "2019-12-12 20:09:05.637109", "msg": "non-zero return code", "rc": 1, "start": "2019-12-12 20:08:44.431567", "stdout": "ssh: connect to host 172.29.132.100 port 22: Resource temporarily unavailable\r\r\nlost connection", "stdout_lines": ["ssh: connect to host 172.29.132.100 port 22: Resource temporarily unavailable", "", "lost connection"]}
fatal: [fw2]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "cmd": "scp -o \"StrictHostKeyChecking no\" './data/snapshots/snapshot/configs/fw2.cfg' \"cisco\"@\"172.29.132.200\":\"disk0:configuration.txt\"", "delta": "0:00:21.208292", "end": "2019-12-12 20:09:05.639666", "msg": "non-zero return code", "rc": 1, "start": "2019-12-12 20:08:44.431374", "stdout": "ssh: connect to host 172.29.132.200 port 22: Resource temporarily unavailable\r\r\nlost connection", "stdout_lines": ["ssh: connect to host 172.29.132.200 port 22: Resource temporarily unavailable", "", "lost connection"]}

In order to get Ansible to print this in a more readable format, a callback plugin can be enabled. To enable add the following to your ansible.cfg.

[DEFAULTS]
stdout_callback = debug

Your output will now be displayed like so:

TASK [Upload Configuration to Device] *************************************************************************************************

fatal: [fw2]: FAILED! => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": true,
    "cmd": "scp -o \"StrictHostKeyChecking no\" './data/snapshots/snapshot/configs/fw2.cfg' \"cisco\"@\"172.29.132.200\":\"disk0:configuration.txt\"",
    "delta": "0:00:21.218448",
    "end": "2019-12-12 20:18:47.709376",
    "rc": 1,
    "start": "2019-12-12 20:18:26.490928"
}

STDOUT:
ssh: connect to host 172.29.132.200 port 22: Resource temporarily unavailable
lost connection

MSG:
non-zero return code

fatal: [fw1]: FAILED! => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": true,
    "cmd": "scp -o \"StrictHostKeyChecking no\" './data/snapshots/snapshot/configs/fw1.cfg' \"cisco\"@\"172.29.132.100\":\"disk0:configuration.txt\"",
    "delta": "0:00:21.222286",
    "end": "2019-12-12 20:18:47.711059",
    "rc": 1,
    "start": "2019-12-12 20:18:26.488773"
}

STDOUT:
ssh: connect to host 172.29.132.100 port 22: Resource temporarily unavailable lost connection

MSG:
non-zero return code