Subsections of Inventory

Ansible Inventory and Ansible.cfg

Ansible projects

For small companies, you can use a single Ansible configuration. But for larger ones, it’s a good idea to use different project directories. A project directory contains everything you need to work on a single project. Including:

  • playbooks
  • variable files
  • task files
  • inventory files
  • ansible.cfg

playbook An Ansible script written in YAML that enforce the desired configuration on manage hosts.

Inventory

A file that Identifies hosts that Ansible has to manage. You can also use this to list and group hosts and specify host variables. Each project should have it’s own inventory file.

/etc/ansible/hosts

  • can be used for system wide inventory.
  • default if no inventory file is specified.
  • has some basic inventory formatting info if you forget)
  • Ansible will also target localhosts if no hosts are found in the inventory file.
  • It’s a good idea to store inventory files in large environments in their own project folders.

localhost is not defined in inventory. It is an implicit host that is usable and refers to the Ansible control machine. Using localhost can be a good way to verify the accessibility of services on managed hosts.

Listing hosts

List hosts by IP address or hostname. You can list a range of hosts in an inventory file as well such as web-server[1:10].example.com

ansible1:2222 < specify ssh port if the host is not using the default port 22
ansible2
10.0.10.55
web-server[1:10].example.com

Listing groups

You can list groups and groups of groups. See the groups web and db are included in the group “servers:children”

ansible1
ansible2
10.0.10.55
web-server[1:10].example.com

[web]
web-server[1:10].example.com

[db]
db1
db2

[servers:children] <-- servers is the group of groups and children is the parameter that specifies child groups
web
db

There are 3 general approaches to using groups:

Functional groups Address a specific group of hosts according to use. Such as web servers or database servers.

Regional host groups Used when working with region oriented infrastructure. Such as USA, Canada.

Staging host groups Used to address different hosts according to the staging phase that the current environment is in. Such as testing, development, production.

Undefined host groups are called implicit host groups. These are all, ungrouped, and localhost. Names making the meaning obvious.

Host variables

In older versions of Ansible you could define variables for hosts. This is no longer used. Example:

[groupname:vars]
ansible=ansible_user

Variables are now set using host_vars and group_vars directories instead.

Multiple inventory files

Put all inventory files in a directory and specify the directory as the inventory to be used. For dynamic directories you also need to set the execution bit on the inventory file.

Ansible-inventory command

Inventory commands:

To view the inventory, specify the inventory file such as ~/base/inventory in the command line. You can name the inventory file anything you want. You can also set the default in the ansible.cfg file.

View the current inventory: ansible -i inventory <pattern> --list-hosts

List inventory hosts in JSON format: ansible-inventory -i inventory --list

Display overview of hosts as a graph: ansible-inventory -i inventory --graph

In our lab example:

[ansible@control base]$ pwd
/home/ansible/base

[ansible@control base]$ ls
inventory

[ansible@control base]$ cat inventory
ansible1
ansible2

[web]
web1
web2

[ansible@control base]$ ansible-inventory -i inventory --graph
@all:
  |--@ungrouped:
  |  |--ansible1
  |  |--ansible2
  |--@web:
  |  |--web1
  |  |--web2

[ansible@control base]$ ansible-inventory -i inventory --list
{
    "_meta": {
        "hostvars": {}
    },
    "all": {
        "children": [
            "ungrouped",
            "web"
        ]
    },
    "ungrouped": {
        "hosts": [
            "ansible1",
            "ansible2"
        ]
    },
    "web": {
        "hosts": [
            "web1",
            "web2"
        ]
    }
}

[ansible@control base]$ ansible -i inventory all --list-hosts
  hosts (4):
    ansible1
    ansible2
    web1
    web2
    
[ansible@control base]$ ansible -i inventory ungrouped --list-hosts
  hosts (2):
    ansible1
    ansible2

Using the ansible-inventory Command

  • default output of a dynamic inventory script is unformatted.
  • To show formatted JSON output of the scripts, you can use the ansible-inventory command.
  • Apart from the --list and --host options, this command also uses the --graph option to show a list of hosts, including the host groups they are a member of.
    [ansible@control rhce8-book]$ ansible-inventory -i listing101.py --graph
    [WARNING]: A duplicate localhost-like entry was found (localhost). First found
    localhost was 127.0.0.1
    @all:
      |--@ungrouped:
      |  |--127.0.0.1
      |  |--192.168.4.200
      |  |--192.168.4.201
      |  |--192.168.4.202
      |  |--ansible1
      |  |--ansible1.example.com
      |  |--ansible2
      |  |--ansible2.example.com
      |  |--control
      |  |--control.example.com
      |  |--localhost
      |  |--localhost.localdomain
      |  |--localhost4
      |  |--localhost4.localdomain4
      |  |--localhost6
      |  |--localhost6.localdomain6

Dynamic Inventory

Dynamic inventory scripts

A script is used to detect inventory hosts so that you do not have to manually enter them. This is good for larger environments. You can find community provided dynamic inventory scripts that come with an .ini file that provides information on how to connect to a resource.

Inventory scripts must include –list and –host options and output must be JSON formatted. Here is an example from sandervanvught that generates an inventory script using /etc/hosts:

[ansible@control base]$ cat inventory-helper.py

#!/usr/bin/python

from subprocess import Popen,PIPE
import sys

try:
     import json
except ImportError:
     import simplejson as json



result = {}

result['all'] = {}



pipe = Popen(['getent', 'hosts'], stdout=PIPE, universal_newlines=True)


result['all']['hosts'] = []

for line in pipe.stdout.readlines():
    s = line.split()
    result['all']['hosts']=result['all']['hosts']+s


result['all']['vars'] = {}


if len(sys.argv) == 2 and sys.argv[1] == '--list':
    print(json.dumps(result))

elif len(sys.argv) == 3 and sys.argv[1] == '--host':
    print(json.dumps({}))

else:
    print("Requires an argument, please use --list or --host <host>")

When ran on our sample lab:

[ansible@control base]$sudo python3 ./inventory-helper.py
Requires an argument, please use --list or --host <host>

[ansible@control base]$ sudo python3 ./inventory-helper.py --list
{"all": {"hosts": ["127.0.0.1", "localhost", "localhost.localdomain", "localhost4", "localhost4.localdomain4", "127.0.0.1", "localhost", "localhost.localdomain", "localhost6", "localhost6.localdomain6", "192.168.124.201", "ansible1", "192.168.124.202", "ansible2"], "vars": {}}}

To use a dynamic inventory script:

[ansible@control base]$ chmod u+x inventory-helper.py 
[ansible@control base]$ sudo ansible -i inventory-helper.py all --list-hosts
[WARNING]: A duplicate localhost-like entry was found (localhost). First found localhost was 127.0.0.1
  hosts (11):
    127.0.0.1
    localhost
    localhost.localdomain
    localhost4
    localhost4.localdomain4
    localhost6
    localhost6.localdomain6
    192.168.124.201
    ansible1
    192.168.124.202
    ansible2

Configuring Dynamic Inventory

dynamic inventory

  • script that can be used to detect whether new hosts have been added to the managed environment.

  • Dynamic inventory scripts are provided by the community and exist for many different environments.

  • easy to write your own dynamic inventory script.

  • The main requirement is that the dynamic inventory script works with a --list and a --host <hostname> option and produces its output in JSON format.

  • Script must have the Linux execute permission set.

  • Many dynamic inventory scripts are written in Python, but this is not a requirement.

  • Writing dynamic inventory scripts is not an exam requirement

#!/usr/bin/python
    
from subprocess import Popen,PIPE
import sys
    
try:
     import json
except ImportError:
     import simplejson as json
    
result = {}
result['all'] = {}
    
pipe = Popen(['getent', 'hosts'], stdout=PIPE, universal_newlines=True)
    
result['all']['hosts'] = []
    
for line in pipe.stdout.readlines():
    s = line.split()
    result['all']['hosts']=result['all']['hosts']+s
    
result['all']['vars'] = {}
    
if len(sys.argv) == 2 and sys.argv[1] == '--list':
    print(json.dumps(result))
    
elif len(sys.argv) == 3 and sys.argv[1] == '--host':
    print(json.dumps({}))
    
else:
    print("Requires an argument, please use --list or --host <host>")

pipe = Popen(\['getent', 'hosts'\], stdout=PIPE, universal_newline=True)

  • gets a list of hosts using the getent function.
  • This queries all hosts in /etc/hosts and other mechanisms where host name resolving is enabled.
  • To show the resulting host list, you can use the \--list command
  • To show details for a specific host, you can use the option \--host hostname.
    [ansible@control rhce8-book]$ ./listing101.py --list
    {"all": {"hosts": ["127.0.0.1", "localhost", "localhost.localdomain", "localhost4", "localhost4.localdomain4", "127.0.0.1", "localhost", "localhost.localdomain", "localhost6", "localhost6.localdomain6", "192.168.4.200", "control.example.com", "control", "192.168.4.201", "ansible1.example.com", "ansible1", "192.168.4.202", "ansible2.example.com", "ansible2"], "vars": {}}}
  • Dynamic inventory scripts are activated in the same way as regular inventory scripts: you use the -i option to either the ansible or the ansible-playbook command to pass the name of the inventory script as an argument.

External directory service can be based on a wide range of solutions:

  • FreeIPA

  • Active Directory

  • Red Hat Satellite

  • etc.

  • Also are available for virtual machine-based infrastructures such as VMware of Red Hat Enterprise Virtualization, where virtual machines can be discovered dynamically.

  • Can be found in cloud environments, where scripts are available for many solutions, including AWS, GCE, Azure, and OpenStack.

When you are working with dynamic inventory, additional parameters are normally required:

  • To get an inventory from an EC2 cloud environment, you need to enter your web keys.
  • To pass these parameters, many inventory scripts come with an additional configuration file that is formatted in .ini style.
  • The community-provided ec2.py script, for instance, comes with an ec2.ini parameter file.

Another feature that is seen in many inventory scripts is cache management:

  • Can use a cache to store names and parameters of recently discovered hosts.
  • If a cache is provided, options exist to manage the cache, allowing you, for instance, to make sure that the inventory information really is recently discovered.

Host Name Patterns

Working with host name patterns

Working with Host Name Patterns

  • If you want to use an IP address in a playbook, the IP address must be specified as such in the inventory.

  • You cannot use IP addresses that are based only on DNS name resolving.

  • So specifying an IP address in the playbook but not in the inventory file—assuming DNS name resolution is going to take care of the IP address resolving—doesn’t work.

  • apart from the specified groups, there are the implicit host groups all and ungrouped.

  • host name wildcards may be used.

    • ansible -m ping 'ansible\*'
      • match all hosts that have a name starting with ansible.
      • Must put the pattern between single quotes or it will fail with a no matching hosts error.
    • Can be used at any place in the host name.
      • ansible -m ping '\*ble1'
  • When you use wildcards to match host names, Ansible doesn’t distinguish between IP addresses, host names, or hosts; it just matches anything.

    • 'web\*'
      • Matches all servers that are members of the group ‘webservers’, but also hosts ‘web1’ and ‘web2’.

To address multiple hosts:

  • You specify a comma-separated list of targets to address multiple hosts:
    • ansible -m ping ansible1,192.168.4.202
    • Can be a mix of host names, IP addresses, and host group names.

Operators:

  • Can specify a logical AND condition by including an ampersand (&), and a logical NOT by using an exclamation point (!).
    • web,&file applies to hosts only if they are members of the web and file groups
    • web,!webserver1 applies to all hosts in the web group, except host webserver1.
    • When you use the logical AND operator, the position of the ampersand doesn’t matter.
      • web,&file as &web,file also.
    • You can use a colon (:) instead of a comma (,), but using a comma is better to avoid confusion when using IPv6 addresses.

Using Multiple Inventories

Working with Multiple Inventory Files

  • Ansible supports working with multiple inventory files.
  • One way of using multiple inventory files is to enter multiple -i parameters with the ansible or ansible-playbook commands to specify the name of the files to be used.
  • ansible-inventory -i inventory -i listing101.py --list
    • Would produce an output list based on the static inventory in the inventory file, as well as the dynamic inventory that is generated by the listing101.py Python script.
  • You can also specify the name of a directory using the -i option.
    • Uses all files in the directory as inventory files.
    • When using an inventory directory, dynamic inventory files still must be executable for this approach to work.

Lab: Using Multiple Inventories

  • Open a shell as the ansible user and create a directory with the name inventories.
  • Copy the file listing101.py to the directory inventories.
  • Also copy the inventory file to the directory inventories.
  • To make sure both inventories have some unique contents, add the following lines to the file inventories/inventory:
webserver1
webserver2
  • Add the following lines to the Linux /etc/hosts file:
192.168.4.203    ansible3.example.com    ansible3
192.168.4.204    ansible4.example.com    ansible4
  • Use the command ansible-inventory -i inventories --list.