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.
[webservers]
host01.mycompany.com
host02.mycompany.com
[databases]
host03.mycompany.com
host04.mycompany.com
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.
[webservers]
host01.mycompany.com
host02.mycompany.com
[databases]
host03.mycompany.com
host04.mycompany.com
[europe]
host01.mycompany.com
host03.mycompany.com
[asia]
host02.mycompany.com
host04.mycompany.com
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.
[paris]
host01.mycompany.com
host02.mycompany.com
[amsterdam]
host03.mycompany.com
host04.mycompany.com
[europe:children]
paris
amsterdam
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):
databases
hosts[01:10].mycompany.com
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:
host01 ansible_host=host01.mycompany.com
A typical pattern for inventory setups is separating inventory files per environment. Find below an example of keeping separate staging and production inventories.
staging_inventory
[webservers]
host01.staging.mycompany.com
host02.staging.mycompany.com
production_inventory
[webservers]
host01.production.mycompany.com
host02.production.mycompany.com
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.
[webservers]
host01.mycompany.com app_version=1.0.1
host02.mycompany.com app_version=1.0.2
[databases]
host03.mycompany.com app_version=1.0.3
host04.mycompany.com app_version=1.0.4
Ansible-specific connection variables such as ansible_user
or 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.
[webservers]
host01.mycompany.com
host02.mycompany.com
[databases]
host03.mycompany.com
host04.mycompany.com
[webservers:vars]
app_version=1.0.1
[databases:vars]
app_version=1.0.2
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 group_vars
and host_vars
containing our variable files. For example, we could have a file group_vars/webservers
that contains all the variables for web servers:
group_vars/webservers
http_post: 80
ansible_user: automation_user
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 development
, testing
, staging
, and 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.
inventory/
static-inventory-1
static-inventory-2
dynamic-inventory-1
dynamic-inventory-1
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, environment
and 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.
dynamic_inventory_aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions:
- us-east-1
- us-east-2
- us-west-2
hostnames: tag:Name
keyed_groups:
- key: placement.region
prefix: aws_region
- key: tags['environment']
prefix: env
- key: tags['role']
prefix: role
groups:
# 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"
compose:
# use a private address where a public one isn't assigned
ansible_host: public_ip_address|default(private_ip_address)
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.
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.
Another great advantage of using Spacelift is that you can manage different infrastructure tools like Ansible, Terraform, Pulumi, AWS CloudFormation, and even Kubernetes from the same place and combine their Stacks with building workflows across tools.
Our latest Ansible enhancements solve three of the biggest challenges engineers face when they are using Ansible:
- Having a centralized place in which you can run your playbooks
- Combining IaC with configuration management to create a single workflow
- Getting insights into what ran and where
Provisioning, configuring, governing, and even orchestrating your containers can be performed with a single workflow, separating the elements into smaller chunks to identify issues more easily.
Would you like to see this in action – or just want a tl;dr? Check out this video I put together showing you Spacelift’s new Ansible functionality:
If you want to learn more about using Spacelift with Ansible, check our documentation, read our Ansible guide, or book a demo with one of our engineers.
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.
Thank you for reading, and I hope you enjoyed this as much as I did.
Manage Ansible Better with Spacelift
Managing large-scale playbook execution is hard. Spacelift enables you to automate Ansible playbook execution with visibility and control over resources, and seamlessly link provisioning and configuration workflows.