How To Create A Custom, Dynamic Inventory Script for Ansible

How To Create A Custom, Dynamic Inventory Script for Ansible

Writing a dynamic inventory script for Ansible can be useful for more than just getting a list of hosts to run a play against.  It can also function as a standalone command line utility for your infrastructure.

The first time I wrote one, I had a difficult time finding information on how to actually do it so I wanted to write about how to make one and then show an example of how it can be extended beyond a simple inventory script by adding additional flags to the scripts.

Let me set the scene, which may sound familiar: due to a lot of tech debt, there are thousands of servers–many of which are not tracked anywhere.  A server is often discovered when it stops working or needs to be patched, so new computers are constantly being added to a growing inventory list.   Rather than editing a static inventory file with changing values, names, and specs, it’s much more efficient to get an always up-to-date inventory of servers, hence the dynamic inventory script.

As time goes on, people need to know more and more about the servers–what’s installed on them, how many OS licenses we need to buy, who is a stakeholder, etc.

So now a dynamic inventory script I wrote has evolved into a command line utility that can be used to dynamically learn about your infrastructure among other things.  In lieu of a neglected CMDB and tribal knowledge, this command line app now has lots of value and saves me time going forward.

Creating A Dynamic Inventory Script

Output Requirements

Whatever language you choose to write your script in, if passed the --list flag, it should output JSON in this format:

  • a dictionary of groups (i.e development, production)
  • a list of hosts in each group
  • a dictionary of variables
  • a _meta dictionary
  • hosts and the hostvars
{
  "development": {
    "hosts": [
      "prometheus",
      "rotarran"
    ],
    "vars": {}
  },
  "production": {
    "hosts": [
      "talvath",
      "defiant"
    ],
    "vars": {}
  },
  "_meta": {
    "hostvars": {
      "defiant": {},
      "rotarran": {},
      "prometheus": {}, 
      "talvath": {}
    }
  }
}

and if passed the --host=<some host>, it should output the hostvars of that server:

{
  "network": [
    {
      "interface": "eth0",
      "ipv6": [
        {
          "address": "fed5::4418:fe:efdc:3330",
          "netmask": "64",
          "scope": "link"
        }
      ],
      "ip": "10.0.2.15",
      "netmask": "255.255.255.0",
      "broadcast": "10.0.2.255",
      "hardware_address": "12:67:11:26:10:50",
      "module": "e1000"
    },
    {
    {
      "interface": "eth1",
      "ipv6": [
        {
          "address": "fe80::a00:2ddf:fee2:dafe",
          "netmask": "64",
          "scope": "link"
        }
      ],
      "ip": "192.168.80.21",
      "netmask": "255.255.255.0",
      "broadcast": "192.168.80.255",
      "hardware_address": "12:04:4d:55:22:fe",
      "module": "e1000"
    }
  ],
}

Those two flags are the bare minimum to get a working inventory.

If you’d like to see an example of my inventory script, I wrote it in Python and submitted it to Ansible.  That PR is here.

So really, as long as your script can output the JSON format above and accepts the --host=<host> and --list, your script should work.  Writing it in Python is probably easiest since Ansible is Python, but take a look at the PR I made and see how it can also be used as an ad-hoc command line utility.

Where To Get The Information From

From the scenario I laid out earlier, Spacewalk was the software of choice because every server we had needed to be patched so whenever I found one, I would subscribe it to Spacewalk, add it to some groups, and then there was a lot of metadata also gathered by Spacewalk, which was useful.

Dynamic inventory scripts can pull from anything as long as you can code it.  It could be a database, DNS records, or some API.

Expanding The Dynamic Inventory Script For More Features

In my case, since servers were always added to Spacewalk, it grew into the source of truth.  Thus, I often found myself needing to query information for other reasons outside of Ansible.

After using the Spacewalk dynamic inventory script for some time, I began expanding it to do other things and it became a standalone command line utility.

A prime example of this is that I needed to learn how many Redhat servers we had so we could buy the appropriate amount of licenses at renewal time.–or at the very least, see if we wanted to buy more or less than what we currently have.

Without a CMDB or other easy way to query how many of each OS we have, let alone trying to determine Redhat’s different licenses, it was much easier to just query my command line utility that did it for me.