awx.awx.inventory module input_inventories parameter fails when inventory names are duplicated across organisations
#16393 opened on Apr 8, 2026
Description
Please confirm the following
- I agree to follow this project's code of conduct.
- I have checked the current issues for duplicates.
- I understand that AWX is open source software provided for free and that I might not receive a timely response.
- I am NOT reporting a (potential) security vulnerability. (These should be emailed to
security@ansible.cominstead.)
Bug Summary
When using input_inventories on a constructed inventory, the module internally calls resolve_name_to_id('inventories', item) without scoping the lookup to any organisation. If the same inventory name exists in multiple organisations, the lookup returns more than one result and the module fails with:
Request to /api/v2/inventories/?name=<inventory_name> returned 2 items, expected 1
This occurs despite the organization parameter being correctly set on the constructed inventory itself. The organization parameter scopes the constructed inventory lookup correctly (as seen in module.get_one with data={'organization': org_id}) but this org scope is not passed to the resolve_name_to_id call for input_inventories.
AWX version
24.6.1
Select the relevant components
- UI
- UI (tech preview)
- API
- Docs
- Collection
- CLI
- Other
Installation method
kubernetes
Modifications
no
Ansible version
2.19.4 with awx.awx collection 24.6.1
Operating system
Debian
Web browser
No response
Steps to reproduce
Create two organisations — org-a and org-b
Create an inventory named my-inventory in both org-a and org-b
Create a constructed inventory in org-a referencing my-inventory as an input inventory:
- name: Create constructed inventory
awx.awx.inventory:
name: my-constructed-inventory
organization: org-a
kind: constructed
input_inventories:
- my-inventory
state: present
controller_host: "{{ awx_host }}"
controller_oauthtoken: "{{ awx_token }}"
Expected results
The module should scope the input_inventories lookup to the same organisation as the constructed inventory, or provide a parameter to specify the lookup organisation for input inventories.
Actual results
Request to /api/v2/inventories/?name=my-inventory returned 2 items, expected 1
Additional information
Workaround
Pass inventory IDs instead of names in input_inventories:
- name: Get inventory IDs scoped to org
set_fact:
input_inventory_ids: "{{ query('awx.awx.controller_api', 'inventories',
query_params={'organization__name': 'org-a'},
host=awx_host,
oauth_token=awx_token,
return_all=true,
max_objects=2000)
| selectattr('name', 'equalto', 'my-inventory')
| map(attribute='id')
| map('string')
| list }}"
- name: Create constructed inventory using IDs
awx.awx.inventory:
name: my-constructed-inventory
organization: org-a
kind: constructed
input_inventories: "{{ input_inventory_ids }}"
state: present
controller_host: "{{ awx_host }}"
controller_oauthtoken: "{{ awx_token }}"
Root cause
In inventory.py, the input_inventories resolution loop does not pass org scoping data to resolve_name_to_id:
for item in input_inventory_names:
association_fields['input_inventories'].append(module.resolve_name_to_id('inventories', item))
Compare to the correctly scoped inventory lookup earlier in the same file:
inventory = module.get_one('inventories', name_or_id=name, **{'data': {'organization': org_id}})
Suggested fix
Pass org_id as a data filter to resolve_name_to_id for input_inventories:
for item in input_inventory_names:
association_fields['input_inventories'].append(
module.resolve_name_to_id('inventories', item, data={'organization': org_id})
)
Note on suggested fix:
resolve_name_to_id is defined in controller_api.py at line 488 and does not currently accept additional keyword arguments or data filters. The fix therefore requires two changes:
- Update
resolve_name_to_idto accept an optional data parameter and pass it through to the underlying lookup - Update the
input_inventoriesloop ininventory.pyto passdata={'organization': org_id}
Alternatively the input_inventories loop could be refactored to use get_one directly instead of resolve_name_to_id, since get_one already supports the data parameter for scoped lookups as demonstrated elsewhere in the same file.