All posts
Ansibleautomationdynamic inventoryAPI

How to build an Ansible dynamic inventory from your IPAM

Stop hand-maintaining hosts files. Generate an Ansible dynamic inventory straight from your IPAM API so automation always targets the real, current network.

Danbyte5 min read

A static hosts file is a snapshot of your network on the day someone last remembered to edit it. Your real network kept moving. This post shows how to point Ansible at your IPAM instead, so every playbook run targets the devices that actually exist right now — grouped by role, site, and tag automatically.

The problem: static inventories rot

Hand-maintained inventory files drift from reality almost immediately. Someone racks a new switch and forgets to add it. A server is decommissioned but stays in [webservers] for another six months. A site gets renamed in one place and not the other.

The result is the worst kind of automation failure: the playbook runs successfully against the wrong set of hosts. Nothing errors, but a machine that should have been patched was silently skipped, or a decommissioned box gets a config push it should never have received.

The fix is to stop treating the inventory as a document you edit and start treating it as a query you run. If your IPAM is already the source of truth for what exists — and it should be — then Ansible can ask it directly at runtime.

The concept: inventory as a live query

A dynamic inventory is an inventory source that Ansible resolves each time it runs. Instead of reading a file, Ansible executes something that returns the current host list as JSON. That "something" pulls devices from your IPAM over its REST API and shapes them into Ansible groups on the fly.

Because Danbyte models IP space, physical devices, sites, and tags in one place, it already holds everything an inventory needs:

  • Devices and their management addresses — what to connect to
  • Roles (router, switch, hypervisor, edge firewall) — natural functional groups
  • Sites and racks — natural location groups
  • Tags — arbitrary, cross-cutting groups like pci, staging, or needs-reboot

Map those to Ansible groups once, and your inventory maintains itself forever after.

Step 1: make IPAM the source of truth

This is a discipline step, not a code step. If devices are born in the IPAM — when they're racked, addressed, and given a role and tags — then the inventory is correct by construction. If people still add hosts "just for Ansible" on the side, you're back to drift.

Danbyte's REST API exposes devices, addresses, sites, roles, and tags for exactly this kind of automation, and because 100% of features are in the open-source release, there's no gated "automation tier" to buy. Get an API token, confirm you can list devices, and you're ready.

Step 2: choose a dynamic inventory approach

You have two honest options, and both are fine:

  1. An inventory script — any executable that prints inventory JSON on --list. Simplest to understand, works with any Ansible version, easy to run anywhere. Great for getting started and for CI jobs.
  2. An inventory plugin — a Python plugin driven by a small YAML config. More idiomatic in modern Ansible, supports caching and composed groups, and is nicer to maintain at scale.

Start with a script to prove the concept, then graduate to a plugin if you want caching and richer group logic. The data source is identical either way.

Step 3: map tags, roles, and sites to groups

Here is a minimal script skeleton. It GETs devices from the API and emits the JSON structure Ansible expects. Endpoints and field names are illustrative — check the Danbyte docs for the exact paths and query parameters.

#!/usr/bin/env python3
import json, os, requests

BASE  = os.environ["IPAM_URL"]      # e.g. https://ipam.example.net/api
TOKEN = os.environ["IPAM_TOKEN"]
headers = {"Authorization": f"Token {TOKEN}"}

# Ask the IPAM for active devices (paginate for real deployments)
devices = requests.get(f"{BASE}/devices",
                       params={"status": "active"},
                       headers=headers).json()["results"]

inv = {"_meta": {"hostvars": {}}}

def group(name):
    inv.setdefault(name, {"hosts": []})
    return inv[name]

for d in devices:
    name = d["name"]
    ip   = d.get("primary_ip")          # management address
    if not ip:
        continue

    inv["_meta"]["hostvars"][name] = {"ansible_host": ip}

    # Group by role, site, and every tag
    if d.get("role"):
        group(f"role_{d['role']}")["hosts"].append(name)
    if d.get("site"):
        group(f"site_{d['site']}")["hosts"].append(name)
    for tag in d.get("tags", []):
        group(f"tag_{tag}")["hosts"].append(name)

print(json.dumps(inv))

A few things worth doing in a production version:

  • Paginate the device query — don't assume one page holds everything.
  • Filter server-side (status=active, a specific site, a role) so you transfer only what you need.
  • Set useful hostvars — management address, platform, and any connection details your playbooks rely on.
  • Cache results if you run inventory hundreds of times a day; the plugin approach makes this easy.

Step 4: run it

Make the script executable and preview what Ansible sees. This is your fastest sanity check — it hits the API and prints the resolved inventory without touching a single host:

export IPAM_URL=https://ipam.example.net/api
export IPAM_TOKEN=****

chmod +x ipam_inventory.py
ansible-inventory -i ipam_inventory.py --list        # full JSON
ansible-inventory -i ipam_inventory.py --graph       # group tree
ansible all -i ipam_inventory.py -m ping             # smoke test

Now --limit tag_pci, --limit site_ams1, or --limit role_switch all work, and they always reflect the current state of the IPAM. Decommission a device there and it vanishes from the next run — no hosts file edit required.

Where to go from here

Once inventory is live, the same API becomes the backbone for the rest of your automation: provisioning scripts, Terraform runs, and compliance checks can all read from — and write back to — the one source of truth. The same REST API is available whether you self-host on your own hardware or run the identical open-source software on Danbyte Cloud, so a script you write today keeps working if you ever move.

And because Danbyte can run air-gapped with no phone-home and no telemetry, this works just as well on an isolated management network as it does anywhere else.

If your hosts file has more archaeology than accuracy in it, this is a good weekend to retire it.

Try Danbyte

Danbyte is a free, open-source IPAM / DCIM platform — your network source of truth, self-hosted or managed for you.