How to extract host lists from ansible inventory?

George Shuklin
OpsOps
Published in
2 min readNov 19, 2022

I just got a serious jq level up, rehearsing it here.

The problem: I need to get list of hosts in the Ansible inventory.

The problem: you can’t just ‘ — list-hosts’ and have nice json in output. There going to be a lot of gunk floating, including vars, _meta and other things.

JQ is to rescue, but JQ is harder than it sound.

Solution: list all hosts from all groups:

ansible-inventory --export --list | \
jq "[.[].hosts]|flatten|unique|map(select(. != null))"

Explanation:

  • inner .[] — take input, convert to a list
  • .[].hosts — take input, convert to a list, take .hosts key from each element.
  • outer [.....] — make it a list. It will created list of lists
  • |flatten — convert list of lists to flat list
  • |unique … guess for yourself.
  • We also need to use ‘select’ expression at the end to remove ‘null’ for absent hosts in empty group. map just clear everything which is not passed ‘select’ with check for been ‘not null.

Good? Nope.

Escalating problem: We need all hosts except for a group ‘skip’. Host expression for this is ‘all,!skip’. But ansible-inventory does not support host expressions.

Behold, my JQ 130:

ansible-inventory --export --list | \
jq '{"skip": .skip.hosts, "all":[.[].hosts]|flatten|unique|map(select(. != null))}|.all - .skip'

It’s really hard to decode, but:

  • we construct dict with two keys: ‘all’ and ‘skip’.
  • ‘all’ gets value for all hosts from previous section
  • ‘skip’ gets value from .skip.hosts expression, which is ‘take value from dict by key ‘skip’’ and ‘take value from dict by key ‘hosts’).
  • We pass it to a next filter, which took difference between lists: .all-.skip

How I learned it? Trial and error, but this site helped me a lot: https://jqplay.org

P.S. Bonus trick: converting json list to a ‘bash list’ (values, separated by space):

ansible-inventory --export --list | \
jq -c -r '{"skip": .skip.hosts, "all":[.[].hosts]|flatten|unique|map(select(. != null))}|.all - .skip|join(" ")'

join is trivial, but ‘-c’ and ‘-r’ options are important here, they remove quotes.

--

--

George Shuklin
OpsOps

I work at Servers.com, most of my stories are about Ansible, Ceph, Python, Openstack and Linux. My hobby is Rust.