In this blog post, we will dive deep into different concepts around building an Ansible inventory, along with best practices. We will look into basic functionality, managing variables, and combining multiple inventory sources and options for working with dynamic inventories.
If you are new to Ansible or interested in other Ansible concepts, these Ansible tutorials on Spacelift’s blog might be handy.
An Ansible inventory is a collection of managed hosts we want to manage with Ansible for various automation and configuration management tasks. Typically, when starting with Ansible, we define a static list of hosts known as the inventory. These hosts can be grouped into different categories, and then we can leverage various patterns to run our playbooks selectively against a subset of hosts.
By default, the inventory is stored in
/etc/ansible/hosts, but you can specify a different location with the
-i flag or the ansible.cfg configuration file.
Let’s take a look at what a simple inventory looks like. The most common formats are either INI or YAML.
In this example, we use the INI format, define four managed hosts, and we group them into two host groups; webservers and databases. The group names can be specified between brackets, as shown above.
Inventory groups are one of the handiest ways to control Ansible execution. Hosts can also be part of multiple groups.
Note: By default, we can also reference two groups without defining them. The
all group targets all our hosts in the inventory, and the
ungrouped contains any host that isn’t part of any user-defined group.
We can also create nested groups of hosts if necessary.
To list a range of hosts with similar patterns, you can leverage the range functionality instead of defining them one by one. For example, to define ten hosts (host01, host02, …. host10):
Another useful functionality is the option to define aliases for hosts in the inventory. For example, we can run Ansible against the host alias
host01 if we define it in the inventory as:
A typical pattern for inventory setups is separating inventory files per environment. Find below an example of keeping separate staging and production inventories.
When defining groups of hosts in the inventory, we categorize them based on application, function, location, department, and other shared characteristics between hosts, allowing us to target them in groups. Try understanding which groups make sense for your use case and automation needs.
An important aspect of Ansible’s project setup is variable’s assignment and management. Ansible offers many different ways of setting variables (check this blog post for detailed information about Ansible variables), and defining them in the inventory is one of them.
For example, let’s define one variable for a different application version for every host in our dummy inventory from before.
Ansible-specific connection variables such as
ansible_host are examples of host variables defined in the inventory. Check the Connecting to hosts: behavioral inventory parameters section of the docs for more options.
Similarly, variables can also be set at the group level in the inventory and offer a convenient way to apply variables to hosts with common characteristics.
Although setting variables in the inventory is possible, it’s usually not the preferred way. Instead, store separate host and group variable files to enable better organization and clarity for your Ansible projects. Note that host and group variable files must use the YAML syntax.
In the same directory where we keep our inventory file, we can create two folders named
host_vars containing our variable files. For example, we could have a file
group_vars/webservers that contains all the variables for web servers:
A possible use case for having separate variable files instead of storing them in the inventory is to keep sensitive values without persisting them in playbooks or source control systems.
As mentioned, there are multiple options to set variables in Ansible, so look at the official documentation Understanding variable precedence and How variables are merged if you plan on using different layers for your vars.
We have the option to combine different inventories and sources, such as directories, dynamic inventory scripts, or inventory plugins during runtime or in the configuration.
This becomes especially useful in cases where we want to target multiple environments or otherwise isolated setups. For example, to target the
production hosts all at the same time:
ansible-playbook apply_updates.yml -i development -i testing -i staging -i production
We can combine multiple inventory sources in a directory and then manage them as one. This approach comes in handy when controlling static and dynamic hosts.
Then, we can run playbooks against all hosts in these inventories like this:
ansible-playbook apply_updates.yml -i inventory
Many modern environments are dynamic, cloud-based, possibly spread across multiple providers, and constantly changing. In these cases, maintaining a static list of managed nodes is time-consuming, manual, and error-prone.
Ansible has two methods to properly track and target a dynamic set of hosts: inventory plugins and inventory scripts. The official suggestion is to prefer inventory plugins that benefit from the recent updates to ansible core.
To see a list of available inventory plugins you can leverage to build dynamic inventories, you can execute
ansible-doc -t inventory -l. We will look at one of them, the
amazon.aws.aws_ec2, to get hosts from Amazon Web Services EC2.
For plugin-specific docs and examples, use the command
ansible-doc -t inventory amazon.aws.aws_ec2. Install the Ansible Galaxy collection
amazon.aws to work with the plugin by running
ansible-galaxy collection install amazon.aws
For this example, we have created four ec2 instances on AWS, and we would like to build an inventory file dynamically using the plugin.
We added two tags,
role, on these ec2 instances that we will later use to create inventory groups.
Let’s create a YAML file named
dynamic_inventory_aws_ec2.yml with the plugin configuration required for our use case.
- key: placement.region
- key: tags['environment']
- key: tags['role']
# add hosts to the "private_only" group if the host doesn't have a public IP associated to it
private_only: "public_ip_address is not defined"
# use a private address where a public one isn't assigned
We declare the plugin we want to use and other options, including regions to consider fetching data from, setting hostnames from the tag
Name, and creating inventory groups based on region, environment, and role.
Let’s view the generated inventory with the command:
ansible-inventory -i dynamic_inventory_aws_ec2.yml --graph
The plugin fetches information from our AWS account and creates several groups according to our configuration and options.
To persist the inventory to a file, you can use this command:
ansible-inventory -i dynamic_inventory_aws_ec2.yml --list --output inventory/dynamic_inventory_aws_ec2 -y
In case you want to integrate a source that isn’t covered by the existing inventory plugins, you also have the option to develop your own custom inventory plugin.
In this article, we explored Ansible inventory basics and various use cases for defining groups and variables in static inventories. We also learned how to combine multiple inventory sources and an example of fetching our inventory of hosts dynamically from AWS.
You can also explore how Spacelift’s vibrant ecosystem and excellent GitOps flow can greatly assist you in managing and orchestrating Ansible. By introducing Spacelift on top of Ansible, you can then easily create custom workflows based on pull requests and apply any necessary compliance checks for your organization.
Thank you for reading, and I hope you enjoyed this as much as I did.
The most flexible management platform for Infrastructure as Code
Spacelift is a sophisticated SaaS product for Infrastructure as Code that helps DevOps develop and deploy new infrastructures or changes quickly and with confidence. It supports Terraform, Cloudformation, Pulumi, and Kubernetes, with initial support for Ansible.