1.. _intro_dynamic_inventory:
2.. _dynamic_inventory:
3
4******************************
5Working with dynamic inventory
6******************************
7
8.. contents::
9   :local:
10
11If your Ansible inventory fluctuates over time, with hosts spinning up and shutting down in response to business demands, the static inventory solutions described in :ref:`inventory` will not serve your needs. You may need to track hosts from multiple sources: cloud providers, LDAP, `Cobbler <https://cobbler.github.io>`_, and/or enterprise CMDB systems.
12
13Ansible integrates all of these options through a dynamic external inventory system. Ansible supports two ways to connect with external inventory:  :ref:`inventory_plugins` and `inventory scripts`.
14
15Inventory plugins take advantage of the most recent updates to the Ansible core code. We recommend plugins over scripts for dynamic inventory. You can :ref:`write your own plugin <developing_inventory>` to connect to additional dynamic inventory sources.
16
17You can still use inventory scripts if you choose. When we implemented inventory plugins, we ensured backwards compatibility through the script inventory plugin. The examples below illustrate how to use inventory scripts.
18
19If you prefer a GUI for handling dynamic inventory, the :ref:`ansible_tower` inventory database syncs with all your dynamic inventory sources, provides web and REST access to the results, and offers a graphical inventory editor. With a database record of all of your hosts, you can correlate past event history and see which hosts have had failures on their last playbook runs.
20
21.. _cobbler_example:
22
23Inventory script example: Cobbler
24=================================
25
26Ansible integrates seamlessly with `Cobbler <https://cobbler.github.io>`_, a Linux installation server originally written by Michael DeHaan and now led by James Cammarata, who works for Ansible.
27
28While primarily used to kickoff OS installations and manage DHCP and DNS, Cobbler has a generic
29layer that can represent data for multiple configuration management systems (even at the same time) and serve as a 'lightweight CMDB'.
30
31To tie your Ansible inventory to Cobbler, copy `this script <https://raw.githubusercontent.com/ansible-community/contrib-scripts/main/inventory/cobbler.py>`_ to ``/usr/local/etc/ansible`` and ``chmod +x`` the file. Run ``cobblerd`` any time you use Ansible and use the ``-i`` command line option (for example, ``-i /usr/local/etc/ansible/cobbler.py``) to communicate with Cobbler using Cobbler's XMLRPC API.
32
33Add a ``cobbler.ini`` file in ``/usr/local/etc/ansible`` so Ansible knows where the Cobbler server is and some cache improvements can be used. For example:
34
35.. code-block:: text
36
37    [cobbler]
38
39    # Set Cobbler's hostname or IP address
40    host = http://127.0.0.1/cobbler_api
41
42    # API calls to Cobbler can be slow. For this reason, we cache the results of an API
43    # call. Set this to the path you want cache files to be written to. Two files
44    # will be written to this directory:
45    #   - ansible-cobbler.cache
46    #   - ansible-cobbler.index
47
48    cache_path = /tmp
49
50    # The number of seconds a cache file is considered valid. After this many
51    # seconds, a new API call will be made, and the cache file will be updated.
52
53    cache_max_age = 900
54
55
56First test the script by running ``/usr/local/etc/ansible/cobbler.py`` directly. You should see some JSON data output, but it may not have anything in it just yet.
57
58Let's explore what this does.  In Cobbler, assume a scenario somewhat like the following:
59
60.. code-block:: bash
61
62    cobbler profile add --name=webserver --distro=CentOS6-x86_64
63    cobbler profile edit --name=webserver --mgmt-classes="webserver" --ksmeta="a=2 b=3"
64    cobbler system edit --name=foo --dns-name="foo.example.com" --mgmt-classes="atlanta" --ksmeta="c=4"
65    cobbler system edit --name=bar --dns-name="bar.example.com" --mgmt-classes="atlanta" --ksmeta="c=5"
66
67In the example above, the system 'foo.example.com' is addressable by ansible directly, but is also addressable when using the group names 'webserver' or 'atlanta'. Since Ansible uses SSH, it contacts system foo over 'foo.example.com', only, never just 'foo'. Similarly, if you tried "ansible foo", it would not find the system... but "ansible 'foo*'" would do, because the system DNS name starts with 'foo'.
68
69The script provides more than host and group info. In addition, as a bonus, when the 'setup' module is run (which happens automatically when using playbooks), the variables 'a', 'b', and 'c' will all be auto-populated in the templates:
70
71.. code-block:: text
72
73    # file: /srv/motd.j2
74    Welcome, I am templated with a value of a={{ a }}, b={{ b }}, and c={{ c }}
75
76Which could be executed just like this:
77
78.. code-block:: bash
79
80    ansible webserver -m setup
81    ansible webserver -m template -a "src=/tmp/motd.j2 dest=/etc/motd"
82
83.. note::
84   The name 'webserver' came from Cobbler, as did the variables for
85   the config file.  You can still pass in your own variables like
86   normal in Ansible, but variables from the external inventory script
87   will override any that have the same name.
88
89So, with the template above (``motd.j2``), this results in the following data being written to ``/etc/motd`` for system 'foo':
90
91.. code-block:: text
92
93    Welcome, I am templated with a value of a=2, b=3, and c=4
94
95And on system 'bar' (bar.example.com):
96
97.. code-block:: text
98
99    Welcome, I am templated with a value of a=2, b=3, and c=5
100
101And technically, though there is no major good reason to do it, this also works:
102
103.. code-block:: bash
104
105    ansible webserver -m ansible.builtin.shell -a "echo {{ a }}"
106
107So, in other words, you can use those variables in arguments/actions as well.
108
109.. _openstack_example:
110
111Inventory script example: OpenStack
112===================================
113
114If you use an OpenStack-based cloud, instead of manually maintaining your own inventory file, you can use the ``openstack_inventory.py`` dynamic inventory to pull information about your compute instances directly from OpenStack.
115
116You can download the latest version of the OpenStack inventory script `here <https://raw.githubusercontent.com/openstack/ansible-collections-openstack/master/scripts/inventory/openstack_inventory.py>`_.
117
118You can use the inventory script explicitly (by passing the `-i openstack_inventory.py` argument to Ansible) or implicitly (by placing the script at `/usr/local/etc/ansible/hosts`).
119
120Explicit use of OpenStack inventory script
121------------------------------------------
122
123Download the latest version of the OpenStack dynamic inventory script and make it executable::
124
125    wget https://raw.githubusercontent.com/openstack/ansible-collections-openstack/master/scripts/inventory/openstack_inventory.py
126    chmod +x openstack_inventory.py
127
128.. note::
129    Do not name it `openstack.py`. This name will conflict with imports from openstacksdk.
130
131Source an OpenStack RC file:
132
133.. code-block:: bash
134
135    source openstack.rc
136
137.. note::
138
139    An OpenStack RC file contains the environment variables required by the client tools to establish a connection with the cloud provider, such as the authentication URL, user name, password and region name. For more information on how to download, create or source an OpenStack RC file, please refer to `Set environment variables using the OpenStack RC file <https://docs.openstack.org/user-guide/common/cli_set_environment_variables_using_openstack_rc.html>`_.
140
141You can confirm the file has been successfully sourced by running a simple command, such as `nova list` and ensuring it returns no errors.
142
143.. note::
144
145    The OpenStack command line clients are required to run the `nova list` command. For more information on how to install them, please refer to `Install the OpenStack command-line clients <https://docs.openstack.org/user-guide/common/cli_install_openstack_command_line_clients.html>`_.
146
147You can test the OpenStack dynamic inventory script manually to confirm it is working as expected::
148
149    ./openstack_inventory.py --list
150
151After a few moments you should see some JSON output with information about your compute instances.
152
153Once you confirm the dynamic inventory script is working as expected, you can tell Ansible to use the `openstack_inventory.py` script as an inventory file, as illustrated below:
154
155.. code-block:: bash
156
157    ansible -i openstack_inventory.py all -m ansible.builtin.ping
158
159Implicit use of OpenStack inventory script
160------------------------------------------
161
162Download the latest version of the OpenStack dynamic inventory script, make it executable and copy it to `/usr/local/etc/ansible/hosts`:
163
164.. code-block:: bash
165
166    wget https://raw.githubusercontent.com/openstack/ansible-collections-openstack/master/scripts/inventory/openstack_inventory.py
167    chmod +x openstack_inventory.py
168    sudo cp openstack_inventory.py /usr/local/etc/ansible/hosts
169
170Download the sample configuration file, modify it to suit your needs and copy it to `/usr/local/etc/ansible/openstack.yml`:
171
172.. code-block:: bash
173
174    wget https://raw.githubusercontent.com/openstack/ansible-collections-openstack/master/scripts/inventory/openstack.yml
175    vi openstack.yml
176    sudo cp openstack.yml /usr/local/etc/ansible/
177
178You can test the OpenStack dynamic inventory script manually to confirm it is working as expected:
179
180.. code-block:: bash
181
182    /usr/local/etc/ansible/hosts --list
183
184After a few moments you should see some JSON output with information about your compute instances.
185
186Refreshing the cache
187--------------------
188
189Note that the OpenStack dynamic inventory script will cache results to avoid repeated API calls. To explicitly clear the cache, you can run the openstack_inventory.py (or hosts) script with the ``--refresh`` parameter:
190
191.. code-block:: bash
192
193    ./openstack_inventory.py --refresh --list
194
195.. _other_inventory_scripts:
196
197Other inventory scripts
198=======================
199
200In Ansible 2.10 and later, inventory scripts moved to their associated collections. Many are now in the `community.general scripts/inventory directory <https://github.com/ansible-collections/community.general/tree/main/scripts/inventory>`_. We recommend you use :ref:`inventory_plugins` instead.
201
202.. _using_multiple_sources:
203
204Using inventory directories and multiple inventory sources
205==========================================================
206
207If the location given to ``-i`` in Ansible is a directory (or as so configured in ``ansible.cfg``), Ansible can use multiple inventory sources
208at the same time.  When doing so, it is possible to mix both dynamic and statically managed inventory sources in the same ansible run. Instant
209hybrid cloud!
210
211In an inventory directory, executable files are treated as dynamic inventory sources and most other files as static sources. Files which end with any of the following are ignored:
212
213.. code-block:: text
214
215    ~, .orig, .bak, .ini, .cfg, .retry, .pyc, .pyo
216
217You can replace this list with your own selection by configuring an ``inventory_ignore_extensions`` list in ``ansible.cfg``, or setting the :envvar:`ANSIBLE_INVENTORY_IGNORE` environment variable. The value in either case must be a comma-separated list of patterns, as shown above.
218
219Any ``group_vars`` and ``host_vars`` subdirectories in an inventory directory are interpreted as expected, making inventory directories a powerful way to organize different sets of configurations. See :ref:`using_multiple_inventory_sources` for more information.
220
221.. _static_groups_of_dynamic:
222
223Static groups of dynamic groups
224===============================
225
226When defining groups of groups in the static inventory file, the child groups
227must also be defined in the static inventory file, otherwise ansible returns an
228error. If you want to define a static group of dynamic child groups, define
229the dynamic groups as empty in the static inventory file. For example:
230
231.. code-block:: text
232
233    [tag_Name_staging_foo]
234
235    [tag_Name_staging_bar]
236
237    [staging:children]
238    tag_Name_staging_foo
239    tag_Name_staging_bar
240
241
242.. seealso::
243
244   :ref:`intro_inventory`
245       All about static inventory files
246   `Mailing List <https://groups.google.com/group/ansible-project>`_
247       Questions? Help? Ideas?  Stop by the list on Google Groups
248   `irc.libera.chat <https://libera.chat/>`_
249       #ansible IRC chat channel
250