ansible.builtin.script inventory – Executes an inventory script that returns JSON

Note

This inventory plugin is part of ansible-core and included in all Ansible installations. In most cases, you can use the short plugin name script. However, we recommend you use the Fully Qualified Collection Name (FQCN) ansible.builtin.script for easy linking to the plugin documentation and to avoid conflicting with other collections that may have the same inventory plugin name.

Synopsis

  • The source provided must be an executable that returns Ansible inventory JSON

  • The source must accept --list and --host <hostname> as arguments. --host will only be used if no _meta key is present. This is a performance optimization as the script would be called one additional time per host otherwise.

Parameters

Parameter

Comments

always_show_stderr

boolean

Toggle display of stderr even when script was successful

Choices:

  • false

  • true ← (default)

Configuration:

Note

Configuration entries listed above for each entry type (Ansible variable, environment variable, and so on) have a low to high priority order. For example, a variable that is lower in the list will override a variable that is higher up. The entry types are also ordered by precedence from low to high priority order. For example, an ansible.cfg entry (further up in the list) is overwritten by an Ansible variable (further down in the list).

Notes

Note

  • Enabled in configuration by default.

  • The plugin does not cache results because external inventory scripts are responsible for their own caching.

  • To write your own inventory script see (Developing dynamic inventory from the documentation site.

  • To find the scripts that used to be part of the code release, go to https://github.com/ansible-community/contrib-scripts/.

  • Since 2.19 using a directory as an inventory source will no longer ignore .ini files by default, but you can still update the configuration to do so.

Examples

# fmt: code ### simple bash script #!/usr/bin/env bash if [ "$1" == "--list" ]; then cat<<EOF { "bash_hosts": { "hosts": [ "myhost.domain.com", "myhost2.domain.com" ], "vars": { "host_test": "test-value" } }, "_meta": { "hostvars": { "myhost.domain.com": { "host_specific_test_var": "test-value" } } } } EOF elif [ "$1" == "--host" ]; then # this should not normally be called by Ansible as we return _meta above if [ "$2" == "myhost.domain.com" ]; then echo '{"_meta": {hostvars": {"myhost.domain.com": {"host_specific-test_var": "test-value"}}}}' else echo '{"_meta": {hostvars": {}}}' fi else echo "Invalid option: use --list or --host <hostname>" exit 1 fi ### python example with ini config #!/usr/bin/env python """ # ansible_inventory.py """ import argparse import json import os.path import sys from configparser import ConfigParser from inventories.custom import MyInventoryAPI def load_config() -> ConfigParser: cp = ConfigParser() config_file = os.path.expanduser("~/.config/ansible_inventory_script.cfg") cp.read(config_file) if not cp.has_option('DEFAULT', 'namespace'): raise ValueError("Missing configuration option: DEFAULT -> namespace") return cp def get_api_data(namespace: str, pretty=False) -> str: """ :param namespace: parameter for our custom api :param pretty: Human readable JSON vs machine readable :return: JSON string """ found_data = list(MyInventoryAPI(namespace)) hostvars = {} data = { '_meta': { 'hostvars': {}},} groups = found_data['groups'].keys() for group in groups: groups[group]['hosts'] = found_data[groups].get('host_list', []) if group not in data: data[group] = {} data[group]['hosts'] = found_data[groups].get('host_list', []) data[group]['vars'] = found_data[groups].get('info', []) data[group]['children'] = found_data[group].get('subgroups', []) for host_data in found_data['hosts']: for name in host_data.items(): # turn info into vars data['_meta'][name] = found_data[name].get('info', {}) # set ansible_host if possible if 'address' in found_data[name]: data[name]['_meta']['ansible_host'] = found_data[name]['address'] data['_meta']['hostvars'] = hostvars return json.dumps(data, indent=pretty) if __name__ == '__main__': arg_parser = argparse.ArgumentParser( description=__doc__, prog=__file__) arg_parser.add_argument('--pretty', action='store_true', default=False, help="Pretty JSON") mandatory_options = arg_parser.add_mutually_exclusive_group() mandatory_options.add_argument('--list', action='store', nargs="*", help="Get inventory JSON from our API") mandatory_options.add_argument('--host', action='store', help="Get variables for specific host, not used but kept for compatibility") try: config = load_config() namespace = config.get('DEFAULT', 'namespace') args = arg_parser.parse_args() if args.host: print('{"_meta":{}}') sys.stderr.write('This script already provides _meta via --list, so this option is really ignored') elif len(args.list) >= 0: print(get_api_data(namespace, args.pretty)) else: raise ValueError("Valid options are --list or --host <HOSTNAME>") except ValueError: raise