In Ansible and automation controller, as you know, everything starts with an inventory. There are a several methods how inventories can be created, starting from simple static definitions over importing inventory files to dynamic and smart inventories.
In real life it’s very common to deal with external dynamic inventory sources (think cloud, CMDB, containers, …). In this chapter we’ll introduce you to building dynamic inventories using custom inventory scripts. Another great feature of the automation controller to deal with inventories is the Smart Inventory feature which you’ll do a lab on as well.
Quite often just using static inventories will not be enough. You might be dealing with ever-changing cloud environments or you have to get your managed systems from a CMDB or other sources of truth.
Controller includes built-in support for syncing dynamic inventory from cloud sources such as Amazon AWS, Google Compute Engine, among others. Controller also offers the ability to use custom inventory scripts to pull the data from your own inventory source.
In this chapter you’ll get started with dynamic inventories in the automation controller. Aside from the built-in sources you can write inventory scripts in any programming/scripting language that you have installed on the controller machine. To keep it easy we’ll use a most simple custom inventory script using… Bash! Yes!
Don’t get this wrong… we’ve chosen to use Bash to make it as simple as possible to show the concepts behind dynamic and custom inventories. Usually you’d use Python or some other scripting/programming language.
First you need a source. In real life this would be your cloud provider, your CMDB or what not. For the sake of this lab we put a simple file into a Github repository.
Use curl to query your “external inventory source”:
[lab-user@bastion ~]$ curl https://raw.githubusercontent.com/ansible-labs-crew/playbooks_adv_summit2021/master/inventory/inventory_list
{
"dyngroup":{
"hosts":[
"cloud1.cloud.example.com",
"cloud2.cloud.example.com"
],
"vars":{
"var1": true
}
},
"_meta":{
"hostvars":{
"cloud1.cloud.example.com":{
"type":"web"
},
"cloud2.cloud.example.com":{
"type":"database"
}
}
}
}
Well, this is handy, the output is already presented as JSON, the way Ansible would expect… ;-)
Okay, seriously, in real life your script would likely get some information from your source system, format it as JSON and return the data to the automation controller.
An inventory script has to follow some conventions. It must accept the –list and –host <hostname> arguments. When it is called with –list, the script must output a JSON-encoded data containing all groups and hosts to be managed. When called with –host <hostname> it must return an JSON-formatted hash or dictionary of host variables (can be empty).
As looping over all hosts and calling the script with –host can be pretty slow, it is possible to return a top level element called “_meta” with all of the host variables in one script run. And this is what we’ll do. So this is our custom inventory script:
#!/bin/bash
if [ "$1" == "--list" ] ; then
curl -sS https://raw.githubusercontent.com/ansible-labs-crew/playbooks_adv_summit2021/master/inventory/inventory_list
elif [ "$1" == "--host" ]; then
echo '{"_meta": {"hostvars": {}}}'
else
echo "{ }"
fi
What it basically does is to return the data collected by curl when called with –list and as the data includes _meta information about the host variables Ansible will not call it with –host. The curl command is of course the place where your script would get data by whatever means, format it as proper JSON and return it (-sS
makes curl silent, except for error messages).
But before we integrate the custom inventory script into our controller cluster, it’s a good idea to test it on the command line first:
dyninv.sh
with the content shown above.[lab-user@bastion ~]$ chmod +x dyninv.sh
[lab-user@bastion ~]$ ./dyninv.sh --list
{
"dyngroup":{
"hosts":[
"cloud1.cloud.example.com",
"cloud2.cloud.example.com"
],
"vars":{
"var1": true
}
},
"_meta":{
"hostvars":{
"cloud1.cloud.example.com":{
"type":"web"
},
"cloud2.cloud.example.com":{
"type":"database"
}
}
}
}
The script should output the JSON-formatted output shown above.
As simple as it gets, right? More information can be found on how to develop dynamic inventories.
So now you have a source of (slightly static) dynamic inventory data (talk about oxymoron…) and a script to fetch and pass it to controller. Now you need to get this into controller.
In Ansible Tower up to version 3.8, you could create inventory scripts directly in the web UI. Since automation controller 4.0 the only way to get inventory scripts into controller is by putting the script into a source control repository.
For this lab the inventory script was already created in the Git repo you have configured as a Project earlier, so you can use this as-is.
You can directly proceed to adding the dynamic inventory and pointing it to the inventory script.
In the web UI, open Resources ⇒ Inventories.
To create a new custom inventory, click the button and click on Add inventory.
Fill in the needed data:
Click Save
Change to the Sources tab and once more click the blue button.
Fill in the needed data:
Name: Cloud Inventory Script
Source: Sourced from a Project
Project: AWX Project
Inventory file: inventory/inventory-script
enable Update on launch
Click on Save
Start the initial sync by clicking on Sync
Navigate to Views ⇒ Jobs to watch the initial sync, the Type is Inventory Sync
.
After the inventory sync has finished investigate the new hosts which were added by it to your inventory, by navigating to Resources ⇒ Hosts. You should find two new hosts: cloud1.cloud.example.com
and cloud2.cloud.example.com
.
Using this simple example you have:
Created a script to query an inventory source
Integrated the script into controller
Populated an inventory using the custom script
You will most likely have inventories from different sources in your controller installation. Maybe you have a local CMDB, your virtualization management and your public cloud provider to query for managed systems. Imagine you now want to run automation jobs across these inventories on hosts matching certain search criteria.
This is where Smart Inventories come in. A Smart Inventory is a collection of hosts defined by a stored search. Search criteria can be host attributes (like groups) or facts (such as installed software, services, hardware or whatever information Ansible facts are collected). A Smart Inventory can be viewed like a standard inventory and used for job runs.
Automation controller 4.0 introduces a new UI to build these search filters without the need of advanced regular expression kung-fu.
Let’s start with a simple string example. In your controller web UI, open the Resources ⇒ Inventories view. Then click the button and choose to create a new Smart Inventory. In the next view:
Name: Simple Smart Inventory
Click the magnifying glass icon next to Smart host filter
A window Perform a search to define a host filter opens, here you define the search query
To start with you can just use simple search terms. Try cloud or example.com as search terms and see what you get after hitting ENTER.
Search terms are automatically saved so make sure to hit Clear all filters to clear the saved search when testing expressions.
Or what about searching by inventory groups? Switch from Name to Group and enter dyngroup
into the search field. After hitting ENTER you should only see cloud1 and cloud2. Try to narrow it down further by e.g. adding a second filter for Name cloud2
.
When your search returns the results you want, hit Select for the Perform a search to define a host filter window and Save for the Smart Inventory. Now your Smart Inventory is usable for executing job templates!
There are many additional attributes you can create a filter for - including Ansible facts returned from your managed nodes - but that’s for another lab…
Change the Simple Smart Inventory filter to include only enabled hosts in your smart inventory. Hosts can be temporarily disabled, for example due to some maintenance work. We want to exclude them from our inventory.
To test the filter, go to Resources ⇒ Hosts and disable cloud1.cloud.example.com
by switching the slider button to the right to Off. Then open Resources ⇒ Inventories ⇒ Simple Smart Inventory, go to the Hosts tab and check the hosts.
This is an advanced lab, no solution here. But check the previous lab and change the filter to Enabled and Yes.