The first module Ansible implicitly runs on a playbook is the setup module, which gathers “facts” about the system (unless you’ve disabled that with the brand-new gather_facts: False option in the playbook). We can gather our own facts by writing modules we invoke explicitly in playbooks.

  1. We write the module in any language we want: the sole prerequisite is that the module output JSON. (Recall that the module is invoked on the managed node, so the node must have any language-specific modules your module requires installed.)
  2. Copy the module to the $ANSIBLE_LIBRARY directory on the Ansible manager (or onto each node when using Ansible in pull-mode).
  3. Add an invocation of our module to our playbooks.

A fact-gathering module really can be written as trivially as something like this:

#!/bin/sh

COUNT=`who | wc -l`
cat <<EOF
{
   "ansible_facts" : {
      "users_logged_in" : $COUNT
   }
}
EOF

The example I’m going to show you contains everything I consider important to know about the subject.

Ansible fact-gathering

It reports facts about the target node:

  1. Number of files in /tmp and a constant.
  2. Reads “contact” information from a file /etc/sysinfo which exists on all nodes. (I provision that file at system installation-time with, say, contact information, what the box is for, etc.)
  3. The Kerberos keytab name (configurable) and the principals contained therein. (If you’re “into” Kerberos, you may be interested to read how I automatically deploy Kerberos keytabs.)

The JSON string returned by our module looks like this. The ansible_facts key is important, as it triggers Ansible to use the values as facts:

{
    "ansible_facts": {
        "kerberos_keytab_princs": [
            "host/a1.ww.mens.de@MENS.DE", 
            "service/wallet@MENS.DE"
        ], 
        "keytab": "/etc/krb5.keytab", 
        "sysinfo": {
            "syscontact": "Jane Jolie", 
            "sysemail": "janej@example.com"
        }, 
        "tmpfiles": 21, 
        "year": 2006
    }
}

Facts returned by our localsetup module are available to all statements in the playbook just after it’s invoked, so I call it first (well, after setup has been invoked automatically):

---
- hosts: 127.0.0.1
  tasks:
  - name: Gather our facts
    action: localsetup keytab=/etc/krb5.keytab
  - name: Demotemplate
    action: template src=info.in dest=/tmp/info.txt

If I want to split fact-gathering out into more than one module, I can do that as well, invoking them all in my playbook.

The next action uses the template module with the following template:

{% if sysinfo.syscontact is defined -%}
The manager hereabouts is {{ sysinfo.syscontact }} <{{sysinfo.sysemail}}>
{% endif -%}
Currently {{tmpfiles}} files in /tmp

Kerberos information for {{ ansible_fqdn }}:
	keytab name: {{ keytab }}
	principals:
		{% for p in kerberos_keytab_princs -%}
		-> {{ p }}
		{% endfor -%}

and it produces the following output on the machine I ran it against:

The manager hereabouts is Jane Jolie <janej@example.com>
Currently 23 files in /tmp

Kerberos information for a1.ww.mens.de:
	keytab name: /etc/krb5.keytab
	principals:
		-> host/a1.ww.mens.de@MENS.DE
		-> service/wallet@MENS.DE

I’ve put the module’s source-code and all supporting files on Github for you.

See also: A very welcome addition to Ansible’s setup module.