'How can I get a list of child groups in Ansible?

I have an inventory file that looks like this:

[master]
host01

[nl]
host02

[us]
host03

[satellites:children]
nl
us

How can I get a list of groups that have satellites as their parent?

I am looking for a solution that works similar to this:

- debug: msg="{{ item }}"
  with_items: "{{ groups['satellites:children'] }}"

Update:

The only solution I was able to come with is this:

- debug: {{ item }}
  with_items: "{{ groups }}"
  when: item != "master" and item != "satellites" and item != "all" and item != "ungrouped"

But that is not very flexible.



Solution 1:[1]

You can try the following approaches:

  vars:
    parent: 'satellites'
  tasks:
      # functional style
    - debug: msg="{{hostvars[item].group_names | select('ne', parent) | first}}"
      with_items: "{{groups[parent]}}"
      # set style
    - debug: msg="{{hostvars[item].group_names | difference(parent) | first}}"
      with_items: "{{groups[parent]}}"

Also select('ne', parent) is interchangeable with reject('equalto', parent) depending on what is more readable to you.

Links:
set theory operator
select and reject filters


Updated answer based on comments. inspiration from this thread.

vars:
    parent: 'satellites'
tasks:
    - name: build a subgroups list
      set_fact: subgroups="{{ ((subgroups | default([])) + hostvars[item].group_names) | difference(parent) }}"
      with_items: "{{groups[parent]}}"

    - debug: var=subgroups

output:

 "subgroups": [
        "nl",
        "us"
    ]

Solution 2:[2]

There is also another way where you process the file as text file with bash commands like awk

If file is with contents like

cat /tmp/test.ini
[group1]
host1
host2

[group2]
host3
host4

[first_two_groups:children]
group1
group2

[group3]
host5
host6

[group4]
host7
host8

Then you can use one command like this for when file has Linux EOL:

awk "/first_two_groups\:children/,/^$/" /tmp/test.ini | grep -v children
group1
group2

Where group to find children of is called first_two_groups and it can be anywhere in the file. Command works as long as after the group definition there is empty line which is used as anchor to end the awk stream. I think most people add empty line in an ansible inventory file for readability and we can leverage that fact.

If the file happens to have Windows EOL then command is

awk "/first_two_groups\:children/,/^[\\r]$/" /tmp/test.ini | grep -v children

with ansible example something like this:

- name: get group children
  shell: awk "/first_two_groups\:children/,/^$/" /tmp/test.ini | grep -v children
  register: chlldren

#returns list of the children groups to loop through later 
- debug: var=children.stdout_lines

Above is not fully tested but if an issue then it could be at the most with the escaping of special chars in the shell module. REMEMBER - when you pass special chars in ansible shell module like colon use jinja expansion - instead of : use {{ ':' }} Also for consistent bash behavior is recommended to pass explicitly the executable by tacking at the end of the task

  args:
    executable: /bin/bash

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2