Ansible Advanced Course
Other Topics
Dynamic Inventory
In this article, we explore how dynamic inventory files work in Ansible and highlight the differences between dynamic and static inventories. Traditionally, you might have used a static inventory file—manually created with hostnames, IP addresses, and grouping details. For example, a basic static inventory file in INI format would look like this:
/etc/ansible/hosts
web1 ansible_host=172.20.1.100 ansible_ssh_pass=Passw0rd
web2 ansible_host=172.20.1.101 ansible_ssh_pass=Passw0rd
[web_servers]
web1
web2
This static approach works well for small setups with just a few servers. However, when managing large environments with hundreds or thousands of servers—whether on-premise or in the cloud—the manual upkeep of a static inventory becomes impractical, especially when servers are frequently provisioned or decommissioned.
Dynamic Inventory Benefits
Dynamic inventory allows Ansible to programmatically retrieve host and group data at runtime by querying external sources, such as CMDBs or cloud provider APIs. This eliminates the need for manual updates, ensuring that your inventory stays current.
Converting a Static Inventory to a Dynamic Inventory Script
Assume you have a static inventory as shown above. To convert it into a dynamic inventory, you can create a Python script—say, inventory.py
—that outputs the inventory data in JSON format. You can then run your playbook with either the static file or the dynamic script:
$ ansible-playbook playbook.yml -i inventory.txt
$ ansible-playbook playbook.yml -i inventory.py
Below is a simplified example of what the inventory.py
script might look like:
#!/usr/bin/env python
import json
import argparse
def get_inventory_data():
return {
"web_servers": {
"hosts": ["web1", "web2"]
},
"_meta": {
"hostvars": {
"web1": {
"ansible_host": "172.20.1.100",
"ansible_ssh_pass": "Passw0rd"
},
"web2": {
"ansible_host": "172.20.1.101",
"ansible_ssh_pass": "Passw0rd"
}
}
}
}
def read_cli_args():
parser = argparse.ArgumentParser()
parser.add_argument('--list', action='store_true', help='List inventory')
parser.add_argument('--host', help='Get inventory for a specific host')
return parser.parse_args()
if __name__ == "__main__":
args = read_cli_args()
inventory_data = get_inventory_data()
if args.list:
print(json.dumps(inventory_data))
When you run this script with the --list
argument, it produces the complete inventory in JSON format:
$ ./inventory.py --list
{
"web_servers": {
"hosts": [
"web1",
"web2"
]
},
"_meta": {
"hostvars": {
"web1": {
"ansible_host": "172.20.1.100",
"ansible_ssh_pass": "Passw0rd"
},
"web2": {
"ansible_host": "172.20.1.101",
"ansible_ssh_pass": "Passw0rd"
}
}
}
}
If the --host
argument is passed, the script should return the specific host's variables in JSON format. Although this example is basic, it effectively demonstrates the concept of a dynamic inventory in Ansible. In production environments, you might enhance the script to fetch live inventory data from a CMDB or cloud service API.
Exam Tip
In certification exams, you are not typically required to write such inventory scripts. However, you should understand how these scripts function and supply inventory data to Ansible.
Using Dynamic Inventory with Cloud Providers
Dynamic inventory is especially useful when managing cloud instances. For instance, to manage AWS instances, you can use the EC2 inventory script. After downloading the ec2.py
script from the Ansible GitHub repository, run your playbook with the following command:
$ ansible-playbook playbook.yml -i ec2.py
Since the script interacts with AWS, you must authenticate first. A common method is to export your AWS credentials:
$ export AWS_ACCESS_KEY_ID=AK123
$ export AWS_SECRET_ACCESS_KEY=ABC123
$ ansible-playbook playbook.yml -i ec2.py
Once authenticated, the script retrieves the necessary data from AWS, enabling your playbooks to manage EC2 instances seamlessly.
Other Inventory Formats and Plugins
In addition to static (INI) and dynamic (Python script) inventories, Ansible also supports YAML inventory files. For example, the same inventory data can be expressed in YAML as follows:
web_servers:
hosts:
web1:
ansible_host: 172.20.1.100
ansible_ssh_pass: Passw0rd
web2:
ansible_host: 172.20.1.101
ansible_ssh_pass: Passw0rd
Ansible uses various inventory plugins to process these different formats:
- INI plugin: Reads inventory data from INI files.
- Script plugin: Executes a script and interprets its JSON output as inventory.
- YAML plugin: Interprets YAML-formatted files.
These plugins are essentially Python scripts working in the background, and Ansible’s extensible design even allows you to write your own plugins for custom data sources (e.g., XML).
You can control which inventory plugins are active via the Ansible configuration file, typically located at /etc/ansible/ansible.cfg
, under the [inventory]
section:
[inventory]
enable_plugins = host_list, script, auto, yaml, ini
Scripts vs. Plugins
While the dynamic inventory script is a tried-and-true method, Ansible now recommends using inventory plugins. Although inventory scripts are simple and can be implemented in any language, inventory plugins offer better integration with the Ansible ecosystem and additional functionality.
Consider the following example of an advanced dynamic inventory script written in Python:
#!/usr/bin/env python
import json
import argparse
def get_inventory_data():
return {
"web_servers": {
"hosts": ["web1", "web2"]
},
"_meta": {
"hostvars": {
"web1": {
"ansible_host": "172.20.1.100",
"ansible_ssh_pass": "Passw0rd"
},
"web2": {
"ansible_host": "172.20.1.101",
"ansible_ssh_pass": "Passw0rd"
}
}
}
}
def read_cli_args():
parser = argparse.ArgumentParser()
parser.add_argument('--list', action='store_true', help='List inventory')
parser.add_argument('--host', help='Get inventory for a specific host')
return parser.parse_args()
if __name__ == "__main__":
args = read_cli_args()
inventory_data = get_inventory_data()
if args.list:
print(json.dumps(inventory_data))
Here’s an excerpt showcasing how an inventory plugin might extend Ansible’s functionality:
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import hashlib
import os
import string
from ansible.errors import AnsibleError, AnsibleParserError
from ansible.inventory.group import to_safe_group_name as original_safe
from ansible.parsing.utils.addresses import parse_address
from ansible.plugins import AnsiblePlugin
from ansible.plugins.cache import CachePluginAdjudicator as CacheObject
from ansible.module_utils._text import to_bytes, to_native
display = Display()
def expand_hostname_range(line=None):
if line:
# Example: Replace delimiters and split to get numeric bounds
line = line.replace('!', '|').replace('|', ' ').split('|')
bounds = [int(x) for x in line]
if len(bounds) not in (2, 3):
raise AnsibleError("Host range must be in the format begin:end or begin:end:step")
This example demonstrates that while you can create a basic dynamic inventory with a standalone script, inventory plugins offer a more robust, integrated solution for advanced use cases.
Testing Your Inventory
Debugging your inventory configuration is simple with the ansible-inventory
command. This utility displays how Ansible interprets your inventory data. For example, when testing the EC2 inventory script, you might run:
$ ansible-inventory -i ec2.py
{
"_meta": {
"hostvars": {
"172.20.1.109": {
"ansible_ssh_pass": "PasswOrd",
"ansible_ssh_user": "root",
"ec2_region": "ca-central-1",
"ec2_state": "Running"
},
"172.20.1.110": {
"ansible_ssh_pass": "PasswOrd",
"ansible_ssh_user": "root",
"ec2_region": "us-east-1",
"ec2_state": "Running"
}
}
},
"all": {
"children": {
"group": {
"hosts": [
"172.20.1.109",
"172.20.1.110"
]
},
"ungrouped": {}
}
}
}
Inventory Debugging
Using the ansible-inventory
command is an excellent way to troubleshoot issues with inventory variables and to ensure that your dynamic inventory is working as expected.
That concludes our exploration of dynamic inventory in Ansible. Experiment with both static and dynamic inventories to find the best approach for your environment, and leverage Ansible's extensive inventory plugins for a streamlined and scalable automation experience.
Further Reading
Watch Video
Watch video content
Practice Lab
Practice lab