Ansible

Working with Ansible Inventory – Basics and Use Cases

Working with Ansible Inventory

Spacelift and Ansible

Manage and orchestrate Ansible with Spacelift’s vibrant ecosystem and excellent GitOps flow. Create powerful custom workflows combining multiple IaC tools.

Book a demo

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.

Ansible Inventory Basics

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.

Inventory and Variables

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. 

Multiple Ansible Inventory Sources

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

Ansible Dynamic Inventories

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. 

Ansible inventory AWS instance

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
Dynamic Inventory AWS EC2

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.

Key Points

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.

Start free trial