# Question about YML inventory structure

**URL:** <https://forum.ansible.com/t/question-about-yml-inventory-structure/44418>\
**Category:** Get Help\
**Created:** [September 4, 2025, 12:53pm UTC](https://forum.ansible.com/t/question-about-yml-inventory-structure/44418 "2025-09-04T12:53:00Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![Lyle\_McKarns](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/lyle_mckarns/32/8142_2.png) [@Lyle\_McKarns](https://forum.ansible.com/u/Lyle_McKarns)\
**Post date:** [September 4, 2025, 12:53pm UTC](https://forum.ansible.com/t/question-about-yml-inventory-structure/44418/1 "2025-09-04T12:53:00Z")

</div>

I’m having trouble wrapping my head around how to accomplish something with an inventory. I have multiple datacetners that internally have similar structures that I want to represent.

What I’d like to be able to do is be able to address “all items in DC1” with a typical `--limit dc1` (this does not work as written). What I end up with when trying to do that limit is still all hosts. What I believe is happening is that because the group names match, and `dev` exists in both groups, it loads both `dev` groups, and then the dc2 dev group vars are loaded second, and override the DC1 vars.

Is there a way to structure this to achieve what I’m looking for?

```auto
---
all:
  vars:
    service1_user: "{{ lookup('env', 'SERVICE1_USER') }}"
    service1_password: "{{ lookup('env','SERVICE1_PASSWORD') }}"
    service2_password: "{{ lookup('env','SERVICE2_PASSWORD') }}"
    service3_user: "{{ lookup('env', 'SERVICE3_USER') }}"
    service3_pass: "{{ lookup('env', 'SERVICE3_PASS') }}"

dc1:
  vars:
    var0: dc1 value
  children:
    dev:
      vars:
        var1: DC1 dev value
        var2: DC1 dev value
        var3: DC1 dev value
      hosts:
        dc1devhost1
        dc1devhost2
        dc1devhost3
        dc1devhost4
    uat:
      vars:
        var1: DC1 uat value
        var2: DC1 uat value
        var3: DC1 uat value
      hosts:
        dc1uathost1
        dc1uathost2
        dc1uathost3
        dc1uathost4
    prod:
      vars:
        var1: DC1 prod value
        var2: DC1 prod value
        var3: DC1 prod value
      hosts:
        dc1prodhost1
        dc1prodhost2
        dc1prodhost3
        dc1prodhost4

dc2:
  vars:
    var0: dc2 value
  children:
    dev:
      vars:
        var1: dc2 dev value
        var2: dc2 dev value
        var3: dc2 dev value
      hosts:
        dc2devhost1
        dc2devhost2
        dc2devhost3
        dc2devhost4
    uat:
      vars:
        var1: dc2 uat value
        var2: dc2 uat value
        var3: dc2 uat value
      hosts:
        dc2uathost1
        dc2uathost2
        dc2uathost3
        dc2uathost4
    prod:
      vars:
        var1: dc2 prod value
        var2: dc2 prod value
        var3: dc2 prod value
      hosts:
        dc2prodhost1
        dc2prodhost2
        dc2prodhost3
        dc2prodhost4

```

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [September 4, 2025, 2:50pm UTC](https://forum.ansible.com/t/question-about-yml-inventory-structure/44418/2 "2025-09-04T14:50:08Z")

</div>

> [@Lyle\_McKarns](#):
>
> […] it loads both `dev` groups […]

There’s your problem right there! There aren’t multiple `dev` groups. Groups don’t nest. It’s easy to imagine that they do, especially when you define them in a complex data structure — a `dev` under `dc1` and a `dev` under `dc2` — but that isn’t what’s happening.

What _is_ happening is, you’re saying “Put these hosts associated with `dc1` in the `dev` group, and put these other hosts associated with `dc2` _also_ in the one-and-only `dev` group.”

In other words, the “group namespace” is flat, not a hierarchy, regardless of how you define them.

---

<div class="post-metadata">

**Author:** ![Lyle\_McKarns](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/lyle_mckarns/32/8142_2.png) [@Lyle\_McKarns](https://forum.ansible.com/u/Lyle_McKarns)\
**Post date:** [September 4, 2025, 2:58pm UTC](https://forum.ansible.com/t/question-about-yml-inventory-structure/44418/3 "2025-09-04T14:58:37Z")

</div>

Yeah, that’s what I expected given the results I saw. Is there a way to structure this / use a `--limit` that might achieve that I am looking for?

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [September 4, 2025, 3:02pm UTC](https://forum.ansible.com/t/question-about-yml-inventory-structure/44418/4 "2025-09-04T15:02:11Z")

</div>

> [@utoddl](#):
>
> In other words, the “group namespace” is flat, not a hierarchy, regardless of how you define them.

The solution, then, is to use group names that reflect the hierarchy that you wish they were in.

```
<dc>_<env>_<hostname>

```

Also include an `all` at the `<dc>` and `<env>` levels, and fully articulate the groups. So you would have

```
dcall
dc1
dc1_all
dc1_dev
dc1_uat
dc2
dc2_all
dc2_dev
dc2_uat

```

That may seem like overkill, but it gives you the flexibility to construct group names programmatically without having to worry about undefined group names.

---

<div class="post-metadata">

**Author:** ![Lyle\_McKarns](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/lyle_mckarns/32/8142_2.png) [@Lyle\_McKarns](https://forum.ansible.com/u/Lyle_McKarns)\
**Post date:** [September 4, 2025, 3:04pm UTC](https://forum.ansible.com/t/question-about-yml-inventory-structure/44418/5 "2025-09-04T15:04:11Z")

</div>

I’m not quite following how the `all` groups would function here (when compared to the groups of the same name without `all` added).

Can you wrangle my example into shape to elaborate?

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [September 4, 2025, 3:29pm UTC](https://forum.ansible.com/t/question-about-yml-inventory-structure/44418/6 "2025-09-04T15:29:01Z")

</div>

A fair question. It may be overdoing it a bit. However, In our experience, we do a lot of slicing and dicing with generated group names, and we’ve found them to be handy.

For example, if you need to do something across the `uat` groups in all data centers, you can say `"dcall_uat"` instead of `"dc1_uat,dc2_uat"`.

In practice, we have a prefix for each of our inventories, so if this were in `mw` inventory, we’d have these groups:

```
mw_all
mw_all_dev
mw_all_uat
mw_all_prd
mw_dc1
mw_dc1_dev
mw_dc1_uat
mw_dc1_prd
mw_dc2
mw_dc2_dev
mw_dc2_uat
mw_dc2_prd

```

And of course these would have the obvious `children` relationships.

We used to start with only the group names we’d actually need, then back-fill the intermediate groups as needed for convenience. But we found that remembering which groups we he had and had not back-filled was more of a pain than just fully articulating the entire hierarchy from the outset. We probably define some group names that we never use, but we know we _could use them_ because we’re pretty consistent in setting this up this way. We’ve done it across ~45 projects (where a “project” maps to the 2nd level in our `mw_*` inventory), so we know how our group names are defined regardless of which project we’re currently working on. (It one project, it goes to 5 levels!)

“_Pedantic Consistency for the Win!_”

---

<div class="post-metadata">

**Author:** ![bcoca](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/bcoca/32/9269_2.png) [@bcoca](https://forum.ansible.com/u/bcoca)\
**Post date:** [September 4, 2025, 3:31pm UTC](https://forum.ansible.com/t/question-about-yml-inventory-structure/44418/7 "2025-09-04T15:31:17Z")

</div>

If i could rewrite the YAML inventory today:

- `vars` would only be allowed on groups and hosts at top level under `groups` and `hosts` keys
- `hosts` and `children` keys under groups would be simple lists, which can ‘create’ the hosts, but not allow chaining definitions
- I would change the `children` keyword to `include`

I believe that would clarify how the inventory works as the current model, while very convenient, that allows you to add to host/group definitions anywhere they appear, leads to much confusion about a hierarchy that does not really exist in the inventory. I hope the above helps give you a clearer picture on how to organize.

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [September 4, 2025, 3:37pm UTC](https://forum.ansible.com/t/question-about-yml-inventory-structure/44418/8 "2025-09-04T15:37:07Z")

</div>

> [@Lyle\_McKarns](#):
>
> Can you wrangle my example into shape to elaborate?

I _think_ this will do it. (I’m in a zoom at the moment and this is a pleasant distraction!)

_[Edit: I had left out some `*_all` groups.]_

````yaml
---
all:
  vars:
    service1_user: "{{ lookup('env', 'SERVICE1_USER') }}"
    service1_password: "{{ lookup('env','SERVICE1_PASSWORD') }}"
    service2_password: "{{ lookup('env','SERVICE2_PASSWORD') }}"
    service3_user: "{{ lookup('env', 'SERVICE3_USER') }}"
    service3_pass: "{{ lookup('env', 'SERVICE3_PASS') }}"
dcall:
  children:
    dc1: {}
    dc2: {}
dc1:
  vars:
    var0: dc1 value
  children:
    dc1_all:
      children:
        dc1_dev: {}
        dc1_uat: {}
        dc1_prod: {}
    dc1_dev:
      vars:
        var1: DC1 dev value
        var2: DC1 dev value
        var3: DC1 dev value
      hosts:
        dc1devhost1
        dc1devhost2
        dc1devhost3
        dc1devhost4
    dc1_uat:
      vars:
        var1: DC1 uat value
        var2: DC1 uat value
        var3: DC1 uat value
      hosts:
        dc1uathost1
        dc1uathost2
        dc1uathost3
        dc1uathost4
    dc1_prod:
      vars:
        var1: DC1 prod value
        var2: DC1 prod value
        var3: DC1 prod value
      hosts:
        dc1prodhost1
        dc1prodhost2
        dc1prodhost3
        dc1prodhost4

dc2:
  vars:
    var0: dc2 value
  children:
    dc2_all:
      children:
        dc2_dev: {}
        dc2_uat: {}
        dc2_prod: {}
    dc2_dev:
      vars:
        var1: dc2 dev value
        var2: dc2 dev value
        var3: dc2 dev value
      hosts:
        dc2devhost1
        dc2devhost2
        dc2devhost3
        dc2devhost4
    dc2_uat:
      vars:
        var1: dc2 uat value
        var2: dc2 uat value
        var3: dc2 uat value
      hosts:
        dc2uathost1
        dc2uathost2
        dc2uathost3
        dc2uathost4
    dc2_prod:
      vars:
        var1: dc2 prod value
        var2: dc2 prod value
        var3: dc2 prod value
      hosts:
        dc2prodhost1
        dc2prodhost2
        dc2prodhost3
        dc2prodhost4```
````

---

<div class="post-metadata">

**Author:** ![Lyle\_McKarns](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/lyle_mckarns/32/8142_2.png) [@Lyle\_McKarns](https://forum.ansible.com/u/Lyle_McKarns)\
**Post date:** [September 4, 2025, 3:40pm UTC](https://forum.ansible.com/t/question-about-yml-inventory-structure/44418/9 "2025-09-04T15:40:15Z")

</div>

Thanks. That more or less matches what I did as a “temporary” fix while researching if there was a solution that more accurately aligned with what I wanted.

If I were to break dc1/dc2 into distinct files (`inventory/dc1.yml`, `inventory/dc2.yml`) and then did `--limit dc1` would _that_ behave the way I expect, or would it still parse both anyway?

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [September 4, 2025, 3:43pm UTC](https://forum.ansible.com/t/question-about-yml-inventory-structure/44418/10 "2025-09-04T15:43:14Z")

</div>

I don’t think splitting the inventory into different files changes anything. Any groups that are children of `dc1` would be in play.

---

<div class="post-metadata">

**Author:** ![bcoca](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/bcoca/32/9269_2.png) [@bcoca](https://forum.ansible.com/u/bcoca)\
**Post date:** [September 4, 2025, 3:44pm UTC](https://forum.ansible.com/t/question-about-yml-inventory-structure/44418/11 "2025-09-04T15:44:38Z")

</div>

parsing depends on what you pass to ansible as an inventory source:

`-i inventory/dc1.yml` will ONLY get that file  
`-i inventory/` will merge all inventory source files in that directory into a single inventory  
`-i inventory/dc1.yml -i inventory/dc2.yml` will merge both files as a single inventory also

limit runs AFTER creating a unified inventory, it only acts on play/target selection, not definitions

---

<div class="post-metadata">

**Author:** ![Lyle\_McKarns](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/lyle_mckarns/32/8142_2.png) [@Lyle\_McKarns](https://forum.ansible.com/u/Lyle_McKarns)\
**Post date:** [September 4, 2025, 3:48pm UTC](https://forum.ansible.com/t/question-about-yml-inventory-structure/44418/12 "2025-09-04T15:48:31Z")

</div>

@bcoca & @utoddl, thanks for taking the time to help clear that up. This is more or less the outcome I expected, but I was hoping I was missing something.

My path forward seems clear. I appreciate it.

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [September 4, 2025, 3:50pm UTC](https://forum.ansible.com/t/question-about-yml-inventory-structure/44418/13 "2025-09-04T15:50:51Z")

</div>

I highly encourage you to use the `ansible-inventory` command to see what you’ve got with various inventory specifications. For example, I’m doing this to check my copy of your inventory:

```txt
$ ansible-inventory --graph -i Lyle_McKarns-inventory.yml
@all:
  |--@ungrouped:
  |--@dcall:
  | |--@dc1:
  | | |--@dc1_all:
  | | | |--@dc1_dev:
  | | | | |--dc1devhost1 dc1devhost2 dc1devhost3 dc1devhost4
  | | | |--@dc1_uat:
  | | | | |--dc1uathost1 dc1uathost2 dc1uathost3 dc1uathost4
  | | | |--@dc1_prod:
  | | | | |--dc1prodhost1 dc1prodhost2 dc1prodhost3 dc1prodhost4
  | | |--@dc1_dev:
  | | | |--dc1devhost1 dc1devhost2 dc1devhost3 dc1devhost4
  | | |--@dc1_uat:
  | | | |--dc1uathost1 dc1uathost2 dc1uathost3 dc1uathost4
  | | |--@dc1_prod:
  | | | |--dc1prodhost1 dc1prodhost2 dc1prodhost3 dc1prodhost4
  | |--@dc2:
  | | |--@dc2_all:
  | | | |--@dc2_dev:
  | | | | |--dc2devhost1 dc2devhost2 dc2devhost3 dc2devhost4
  | | | |--@dc2_uat:
  | | | | |--dc2uathost1 dc2uathost2 dc2uathost3 dc2uathost4
  | | | |--@dc2_prod:
  | | | | |--dc2prodhost1 dc2prodhost2 dc2prodhost3 dc2prodhost4
  | | |--@dc2_dev:
  | | | |--dc2devhost1 dc2devhost2 dc2devhost3 dc2devhost4
  | | |--@dc2_uat:
  | | | |--dc2uathost1 dc2uathost2 dc2uathost3 dc2uathost4
  | | |--@dc2_prod:
  | | | |--dc2prodhost1 dc2prodhost2 dc2prodhost3 dc2prodhost4

```

Surprises (a.k.a. mistakes) tend to jump out.

---

<div class="post-metadata">

**Author:** ![Lyle\_McKarns](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/lyle_mckarns/32/8142_2.png) [@Lyle\_McKarns](https://forum.ansible.com/u/Lyle_McKarns)\
**Post date:** [September 4, 2025, 3:52pm UTC](https://forum.ansible.com/t/question-about-yml-inventory-structure/44418/14 "2025-09-04T15:52:57Z")

</div>

Yeah, that’s what I did to confirm what was happening with the groups merging. I didn’t think to do it before hand because I thought I knew what I was doing (I clearly did not). It very quickly showed me what was happening.

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [September 4, 2025, 3:55pm UTC](https://forum.ansible.com/t/question-about-yml-inventory-structure/44418/15 "2025-09-04T15:55:08Z")

</div>

I was using it before posting, and managed to completely ignore the purple error messages. 🙂  
I’ve edited (again) the revised inventory I posted above.

If you end up taking a different approach, I’d like to see what you did.

Good luck!

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/ansible/original/1X/1882d9a01da5d8996a862359b0913cc568a39b84.svg) [@system](https://forum.ansible.com/u/system)\
**Post date:** [October 4, 2025, 3:55pm UTC](https://forum.ansible.com/t/question-about-yml-inventory-structure/44418/16 "2025-10-04T15:55:43Z")

</div>

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.
