Skip to content

Instantly share code, notes, and snippets.

@kfiresmith
Created July 10, 2025 17:43
Show Gist options
  • Save kfiresmith/e3e279245dd9112b9d0810ebe74de943 to your computer and use it in GitHub Desktop.
Save kfiresmith/e3e279245dd9112b9d0810ebe74de943 to your computer and use it in GitHub Desktop.
A helper script for Ansible inventory to quickly return a list of group memberships for any given host in inventory. Uses first string match found in inventory, so you can be lazy and enter 'webs' instead of 'webserver01.college.edu'
#!/bin/bash
#
# Purpose:
# Quickly determine which Ansible inventory groups a host belongs to.
# Accepts a partial hostname match (e.g., "webse" instead of "webserver.college.edu") and resolves it to
# the first full hostname found in the inventory.
# Parses the output of `ansible-inventory --graph` to efficiently return
# group membership, including nested groups, with sub-second runtime.
#
# Usage:
# ansgroups <hostname_substring>
#
# Example:
# ansgroups wpdev
#
# Notes:
# - Uses a static inventory file at /home/sysadm/ansible-configuration/hosts
# - Requires ansible-inventory in PATH
# - Returns first match only
#
# 2025-07-10 - Kodiak Firesmith <[email protected]>
inventory_file="~/ansible/hosts"
input="$1"
if [[ -z "$input" || "$input" == "-h" || "$input" == "--help" ]]; then
echo "Usage: $(basename "$0") <hostname_substring>"
echo
echo "Description:"
echo " Return the list of Ansible groups for the first inventory host"
echo " matching the given substring (e.g., 'wpdev' matches wpdev.whoi.edu)."
exit 1
fi
# Find the first matching host
host=$(ansible-inventory -i "$inventory_file" --graph | awk -v pat="$input" '
/[[:space:]]+\|--/ {
line = gensub(/^.*\|--/, "", "g", $0)
if (line !~ /^@/ && line ~ pat) {
print line
exit
}
}')
if [[ -z "$host" ]]; then
echo "Error: No matching host found in inventory for pattern '$input'" >&2
exit 2
fi
# Extract groups
groups=$(ansible-inventory -i "$inventory_file" --graph | awk -v host="$host" '
function print_parents(indent) {
for (i = 0; i <= indent; i++) {
if (level[i] && !seen[level[i]]++) print level[i]
}
}
{
indent = length($0) - length(gensub(/^[[:space:]]*/, "", "g", $0))
line = gensub(/^.*\|--/, "", "g", $0)
if (match(line, /^@(.+):$/, m)) {
level[indent] = m[1]
for (j in level) if (j > indent) delete level[j]
} else if (line == host) {
print_parents(indent)
}
}' | sort -u)
# Separate 'all' from others
all_group=""
other_groups=()
while IFS= read -r group; do
if [[ "$group" == "all" ]]; then
all_group="[all]"
else
other_groups+=("$group")
fi
done <<< "$groups"
IFS=$'\n' sorted_groups=($(sort <<<"${other_groups[*]}"))
unset IFS
# Output
echo "$host"
if [[ -n "$all_group" ]]; then
echo " - $all_group"
for group in "${sorted_groups[@]}"; do
echo " - $group"
done
else
for group in "${sorted_groups[@]}"; do
echo " - $group"
done
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment