Bookmark this page

Chapter 6. Managing Complex Plays and Playbooks

Abstract

Goal Write playbooks for larger, more complex plays and playbooks.
Objectives
  • Write sophisticated host patterns to efficiently select hosts for a play.

  • Manage large playbooks by importing or including other playbooks or tasks from external files, either unconditionally or based on a conditional test.

Sections
  • Selecting Hosts with Host Patterns (and Guided Exercise)

  • Including and Importing Files (and Guided Exercise)

Lab
  • Managing Complex Plays and Playbooks

Selecting Hosts with Host Patterns

Objectives

  • Write sophisticated host patterns to efficiently select hosts for a play.

Referencing Inventory Hosts

Host patterns are used to specify the hosts on which your play runs. In its simplest form, the name of a managed host or a host group in the inventory is a host pattern that specifies that host or host group.

You have already used host patterns in this course. In a play, the hosts directive specifies the managed hosts to run the play against.

It is usually easier to control what hosts a play targets by carefully using host patterns and having appropriate inventory groups, instead of setting complex conditionals on the play's tasks. Therefore, it is important to have a robust understanding of host patterns.

The following example inventory is used throughout this section to illustrate host patterns.

[student@controlnode ~]$ cat myinventory
web.example.com
data.example.com

[lab]
labhost1.example.com
labhost2.example.com

[test]
test1.example.com
test2.example.com

[datacenter1]
labhost1.example.com
test1.example.com

[datacenter2]
labhost2.example.com
test2.example.com

[datacenter:children]
datacenter1
datacenter2

[new]
192.168.2.1
192.168.2.2

To demonstrate how host patterns are resolved, the following examples run playbook.yml Ansible Playbook, which contains a play that is edited to have different host patterns to target different subsets of managed hosts from the preceding example inventory.

Managed Hosts

The most basic host pattern is the name of a single managed host listed in the inventory. This specifies that the host is the only one in the inventory that is acted upon by the ansible-navigator command.

When the playbook runs, the first Gathering Facts task should run on all managed hosts that match the host pattern. A failure during this task causes the managed host to be removed from the play.

You can only use an IP address in a host pattern if it is explicitly listed in the inventory. If the IP address is not listed in the inventory, then you cannot use it to specify the host even if the IP address resolves to that host name in DNS.

The following example shows how a host pattern can be used to reference an IP address contained in an inventory.

[student@controlnode ~]$ cat playbook.yml
---
...output omitted...
  hosts: 192.168.2.1
...output omitted...

[student@controlnode ~]$ ansible-navigator run \
> -m stdout playbook.yml

PLAY [Test Host Patterns] **************************************************

TASK [Gathering Facts] *****************************************************
ok: [192.168.2.1]
...output omitted...

Note

One problem with referring to managed hosts by IP address in the inventory is that it can be hard to remember which IP address matches which host for your plays. However, you might have to specify the host by IP address for connection purposes if the host does not have a host name that your execution environment can resolve.

You can point an alias at a particular IP address in your inventory by setting the ansible_host host variable. For example, you could have a host in your inventory named host.example that you could use for host patterns and inventory groups, and direct connections using that name to the IP address 192.168.2.1 by creating a host_vars/host.example file containing the following host variable:

ansible_host: 192.168.2.1

Specifying Hosts Using a Group

You can use the names of inventory host groups as host patterns. When a group name is used as a host pattern, it specifies that the play acts on the managed hosts that are members of the group.

[student@controlnode ~]$ cat playbook.yml
---
...output omitted...
  hosts: lab
...output omitted...
[student@controlnode ~]$ ansible-navigator run \
> -m stdout playbook.yml

PLAY [Test Host Patterns] **************************************************

TASK [Gathering Facts] *****************************************************
ok: [labhost1.example.com]
ok: [labhost2.example.com]
...output omitted...

Remember that there is a special group named all that matches all managed hosts in the inventory.

[student@controlnode ~]$ cat playbook.yml
...output omitted...
  hosts: all
...output omitted...
[student@controlnode ~]$ ansible-navigator run \
> -m stdout playbook.yml

PLAY [Test Host Patterns] **************************************************

TASK [Gathering Facts] *****************************************************
ok: [labhost2.example.com]
ok: [test2.example.com]
ok: [web.example.com]
ok: [data.example.com]
ok: [labhost1.example.com]
ok: [192.168.2.1]
ok: [test1.example.com]
ok: [192.168.2.2]

There is also a special group named ungrouped, which includes all managed hosts in the inventory that are not members of any other group:

[student@controlnode ~]$ cat playbook.yml
...output omitted...
  hosts: ungrouped
...output omitted...
[student@controlnode ~]$ ansible-navigator run \
> -m stdout playbook.yml

PLAY [Test Host Patterns] **************************************************

TASK [Gathering Facts] *****************************************************
ok: [web.example.com]
ok: [data.example.com]

Matching Multiple Hosts with Wildcards

Another method of accomplishing the same thing as the all host pattern is to use the asterisk (*) wildcard character, which matches any string. If the host pattern is just a quoted asterisk, then all hosts in the inventory match.

[student@controlnode ~]$ cat playbook.yml
...output omitted...
  hosts: '*'
...output omitted...
[student@controlnode ~]$ ansible-navigator run \
> -m stdout playbook.yml

PLAY [Test Host Patterns] **************************************************

TASK [Gathering Facts] *****************************************************
ok: [labhost2.example.com]
ok: [test2.example.com]
ok: [web.example.com]
ok: [data.example.com]
ok: [labhost1.example.com]
ok: [192.168.2.1]
ok: [test1.example.com]
ok: [192.168.2.2]

Important

Some characters that are used in host patterns also have meaning for the shell. If you are using any special wildcards or list characters in an Ansible Playbook, then you must put your host pattern in single quotes to ensure it is parsed correctly.

  hosts: '!test1.example.com,development'

The asterisk character can also be used to match any managed hosts or groups that contain a particular substring.

For example, the following wildcard host pattern matches all inventory names that end in .example.com:

[student@controlnode ~]$ cat playbook.yml
...output omitted...
  hosts: '*.example.com'
...output omitted...
[student@controlnode ~]$ ansible-navigator run \
> -m stdout playbook.yml

PLAY [Test Host Patterns] **************************************************

TASK [Gathering Facts] *****************************************************
ok: [labhost1.example.com]
ok: [test1.example.com]
ok: [labhost2.example.com]
ok: [test2.example.com]
ok: [web.example.com]
ok: [data.example.com]

The following example uses a wildcard host pattern to match the names of hosts or host groups that start with 192.168.2.:

[student@controlnode ~]$ cat playbook.yml
...output omitted...
  hosts: '192.168.2.*'
...output omitted...
[student@controlnode ~]$ ansible-navigator run \
> -m stdout playbook.yml

PLAY [Test Host Patterns] **************************************************

TASK [Gathering Facts] *****************************************************
ok: [192.168.2.1]
ok: [192.168.2.2]

The next example uses a wildcard host pattern to match the names of hosts or host groups that begin with datacenter.

[student@controlnode ~]$ cat playbook.yml
...output omitted...
  hosts: 'datacenter*'
...output omitted...
[student@controlnode ~]$ ansible-navigator run \
> -m stdout playbook.yml

PLAY [Test Host Patterns] **************************************************

TASK [Gathering Facts] *****************************************************
ok: [labhost1.example.com]
ok: [test1.example.com]
ok: [labhost2.example.com]
ok: [test2.example.com]

Important

The wildcard host patterns match all inventory names, hosts, and host groups. They do not distinguish between names that are DNS names, IP addresses, or groups, which can lead to some unexpected matches.

For example, compare the results of specifying the datacenter* host pattern from the preceding example with the results of the data* host pattern based on the example inventory:

[student@controlnode ~]$ cat playbook.yml
...output omitted...
  hosts: 'data*'
...output omitted...
[student@controlnode ~]$ ansible-navigator run \
> -m stdout playbook.yml

PLAY [Test Host Patterns] **************************************************

TASK [Gathering Facts] *****************************************************
ok: [labhost1.example.com]
ok: [test1.example.com]
ok: [labhost2.example.com]
ok: [test2.example.com]
ok: [data.example.com]

Lists

Multiple entries in an inventory can be referenced using logical lists. A comma-separated list of host patterns matches all hosts that match any of those host patterns.

If you provide a comma-separated list of managed hosts, then all those managed hosts are targeted:

[student@controlnode ~]$ cat playbook.yml
...output omitted...
  hosts: labhost1.example.com,test2.example.com,192.168.2.2
...output omitted...
[student@controlnode ~]$ ansible-navigator run \
> -m stdout playbook.yml

PLAY [Test Host Patterns] **************************************************

TASK [Gathering Facts] *****************************************************
ok: [labhost1.example.com]
ok: [test2.example.com]
ok: [192.168.2.2]

If you provide a comma-separated list of groups, then all hosts in any of those groups are targeted:

[student@controlnode ~]$ cat playbook.yml
...output omitted...
  hosts: lab,datacenter1
...output omitted...
[student@controlnode ~]$ ansible-navigator run \
> -m stdout playbook.yml

PLAY [Test Host Patterns] **************************************************

TASK [Gathering Facts] *****************************************************
ok: [labhost1.example.com]
ok: [labhost2.example.com]
ok: [test1.example.com]

You can also mix managed hosts, host groups, and wildcards, as shown below:

[student@controlnode ~]$ cat playbook.yml
...output omitted...
  hosts: lab,data*,192.168.2.2
...output omitted...
[student@controlnode ~]$ ansible-navigator run \
> -m stdout playbook.yml

PLAY [Test Host Patterns] **************************************************

TASK [Gathering Facts] *****************************************************
ok: [labhost1.example.com]
ok: [labhost2.example.com]
ok: [test1.example.com]
ok: [test2.example.com]
ok: [data.example.com]
ok: [192.168.2.2]

Note

The colon character (:) can be used instead of a comma. However, the comma is the preferred separator, especially when working with IPv6 addresses as managed host names. You might see the colon syntax in earlier examples.

If an item in a list starts with an ampersand character (&), then hosts must match that item in order to match the host pattern. It operates similarly to a logical AND.

For example, based on our example inventory, the following host pattern matches machines in the lab group only if they are also in the datacenter1 group:

[student@controlnode ~]$ cat playbook.yml
...output omitted...
  hosts: lab,&datacenter1
...output omitted...
[student@controlnode ~]$ ansible-navigator run \
> -m stdout playbook.yml

PLAY [Test Host Patterns] **************************************************

TASK [Gathering Facts] *****************************************************
ok: [labhost1.example.com]

You could also specify that machines in the datacenter1 group match only if they are in the lab group with the host patterns &lab,datacenter1 or datacenter1,&lab.

You can exclude hosts that match a pattern from a list by using the exclamation point or "bang" character (!) in front of the host pattern. This operates like a logical NOT.

This example matches all hosts defined in the datacenter group, except test2.example.com based on the example inventory:

[student@controlnode ~]$ cat playbook.yml
...output omitted...
  hosts: datacenter,!test2.example.com
...output omitted...
[student@controlnode ~]$ ansible-navigator run \
> -m stdout playbook.yml

PLAY [Test Host Patterns] **************************************************

TASK [Gathering Facts] *****************************************************
ok: [labhost1.example.com]
ok: [test1.example.com]
ok: [labhost2.example.com]

The pattern '!test2.example.com,datacenter' could have been used in the preceding example to achieve the same result.

The final example shows the use of a host pattern that matches all hosts in the test inventory, except the managed hosts in the datacenter1 group.

[student@controlnode ~]$ cat playbook.yml
...output omitted...
  hosts: all,!datacenter1
...output omitted...
[student@controlnode ~]$ ansible-navigator run \
> -m stdout playbook.yml

PLAY [Test Host Patterns] **************************************************

TASK [Gathering Facts] *****************************************************
ok: [web.example.com]
ok: [data.example.com]
ok: [labhost2.example.com]
ok: [test2.example.com]
ok: [192.168.2.1]
ok: [192.168.2.2]
Revision: rh294-9.0-c95c7de