Ansible: Intersection of two host groups using yaml inventory

Viewed 2236

I have 2 groups, each with child groups called dev and test. I am trying to target test hosts in group2 (four.example.com) using the pattern group2:&test.

Given the yaml inventory:

all:
  children:
    group1:
      children:
        dev:
          hosts:
            one.example.com:
        test:
          hosts:
            two.example.com:
    group2:
      children:
        dev:
          hosts:
            three.example.com:
        test:
          hosts:
            four.example.com:
                  

And playbook:

- name: health check
  hosts: group2:&test
  tasks:
    - name: Ping hosts
      ping:

I am getting pings back from both two and four.

I expect pings from only four.

Although it's true that two is part of a group called test, it is not part of the group called group2, and I therefore would not expect to see it as part of the intersect.

It's as though it's doing a logical or (||), rather than a logical and (&&)

Is this a limitation of Ansible when using nested groups with non-unique child names?

1 Answers

Q: "Is this a limitation of Ansible when using nested groups with non-unique child names?"

A: Ansible by design doesn't store the inventory tree. This might seem a limitation. Quoting from How variables are merged

By default variables are merged/flattened to the specific host before a play is run. This keeps Ansible focused on the Host and Task, so groups don’t really survive outside of inventory and host matching.

Quoting from Inheriting variable values: group variables for groups of groups

Any host that is a member of a child group is automatically a member of the parent group.

There are 2 implications

  1. The patern 'group2:&test' is a self-contradiction. The group test contains hosts: two and four. You agree on this by having tried the intersection. Otherwise, the intersection wouldn't make sense. But the quoted statement says that members of test are automatically members of both group1 and group2. In other words, Ansible can't tell the difference between 1) the group test keeping all hosts and 2) the group test as a child of group1 or group2. As an example, the inventories below are equivalent to the inventory in the question
all:
  children:
    group1:
      children:
        dev:
          hosts:
            one.example.com:
            three.example.com:
        test:
          hosts:
            two.example.com:
            four.example.com:
    group2:
      children:
        dev:
        test:
all:
  children:
    dev:
      hosts:
        one.example.com:
        three.example.com:
    test:
      hosts:
        two.example.com:
        four.example.com:
    group1:
      children:
        dev:
        test:
    group2:
      children:
        dev:
        test:
  1. The nested groups with non-unique child names don't serve the purpose of this use-case. Try the inventory below instead
all:
  children:
    group1:
      hosts:
        one.example.com:
        two.example.com:
    group2:
      hosts:
        three.example.com:
        four.example.com:
    dev:
      hosts:
        one.example.com:
        three.example.com:
    test:
      hosts:
        two.example.com:
        four.example.com:

Notes

  • There might be differences among the inventories in which order the groups_vars will be merged if used. See How variables are merged.
Related