Chapter 7: Managing Complex Plays and Playbooks
Selecting hosts with host patterns
Every play starts by answering one question: which machines? So far the answer has been a single group name. A host pattern can say much more: these two groups, this group except the staging machines, only the hosts that are in both. Getting the pattern right is what makes it safe to run a play against a large inventory.
- Select hosts by name, by group, with wildcards and with lists.
- Combine groups with intersections (
&) and exclusions (!). - Narrow a run from the command line with
--limit. - Check what a pattern selects before anything runs.
Where patterns are used
A host pattern is the value you give to hosts: in a play. The same syntax works in two other places:
| Place | Example |
|---|---|
The hosts keyword of a play | hosts: web |
The --limit option of ansible-navigator run | --limit 'web,!staging' |
| The host argument of an ad hoc command | ansible web -m ansible.builtin.ping |
Patterns are matched against the names in the inventory. A name that resolves in DNS but is not in the inventory matches nothing.
Try it first
The rest of this lesson explains each form. Type a pattern, or pick one, and watch which hosts light up:
Hosts and groups
The simplest patterns are a single name:
hosts: servera.lab.example.com # one managed host, exactly as the inventory names it
hosts: web # every host in the group web, including its child groups
hosts: all # every host in the inventory
hosts: ungrouped # hosts that are in no group except allIf the inventory lists a host by IP address, the pattern is that IP address. The pattern never looks anything up: it only compares names.
If you would rather use a name in patterns but must connect by address, give the host a name in the inventory and set ansible_host for it, for example web01.example.com ansible_host=192.168.2.1, or the same variable in host_vars/web01.example.com. Patterns and groups use the name; the connection uses the address.
A pattern decides which hosts start the play. A host that fails during the play, even in the automatic Gathering Facts task, is dropped from the rest of the play, so the recap can show fewer hosts finishing than the pattern selected.
Wildcards
* matches any run of characters, in host names and group names:
hosts: '*' # the same as all
hosts: '*.lab.example.com' # every host whose name ends this way
hosts: 'prod*' # hosts and groups whose names begin with prod
hosts: 'server[ab]*' # servera… and serverb…Lists
Separate several patterns with commas to add their hosts together:
hosts: web,db
hosts: servera.lab.example.com,serverc.lab.example.comA colon works as a separator too (web:db). It is the older form, and it is ambiguous with IPv6 addresses, so use commas.
Intersections and exclusions
Two prefixes change what a term does:
Keep only the hosts that are also in this group. web,&production is the web servers that are in production.
Remove these hosts. web,!staging is every web server except the staging ones.
hosts: 'web,&production' # in web AND in production
hosts: 'all,!db' # everything except the database servers
hosts: 'staging,&db,!serverc*' # three steps: see belowAnsible does not read a pattern left to right. Whatever order you write the terms in, it works in three passes:
Every plain term is added together into one set of hosts.
Each & term then throws away the hosts that are not in it.
Each ! term finally removes its hosts.
So !staging,web and web,!staging select the same hosts. A pattern with no plain term, such as !staging on its own, starts from all.
Regular expressions and positions
Two more forms are useful now and then:
hosts: '~server[ab]' # ~ starts a regular expression
hosts: 'db[0]' # the first host of the group db
hosts: 'site[1:2]' # the second and third hosts of the group site
hosts: 'web[-1]' # the last host of the group webPositions count from 0 in the order the inventory lists the hosts, and a range includes both ends. They are handy for trying a change on one machine of a group first.
Quote your patterns
*, ! and & mean something in YAML, and *, !, & and [ mean something to the shell. A pattern that begins with one of them must be quoted in a playbook, and any pattern that contains one should be quoted on the command line.
hosts: '*.lab.example.com' # without quotes, YAML reads * as an alias
hosts: '!staging'[student@workstation project]$ ansible-navigator run -m stdout site.yml --limit 'web,!staging'Quoting every pattern that is more than a single name is a habit that costs nothing.
Narrowing a run with --limit
--limit does not replace the play's hosts:. It narrows it: a play runs on the hosts that match both.
[student@workstation project]$ ansible-navigator run -m stdout site.yml --limit servera.lab.example.comIf a play's hosts and the limit have nothing in common, that play is skipped and the run moves on:
PLAY [Show which hosts a pattern selects] **************************************
skipping: no hosts matchedCheck before you run
Two read-only commands show what a pattern selects. Use them before a play that changes things.
[student@workstation project]$ ansible 'web,!staging' --list-hosts
hosts (1):
serverb.lab.example.com
[student@workstation project]$ ansible-navigator run -m stdout site.yml --list-hosts
playbook: /home/student/project/site.yml
play #1 (web): Web servers are configured TAGS: []
pattern: ['web']
hosts (2):
servera.lab.example.com
serverb.lab.example.comThe first tests a pattern by itself. The second shows, play by play, the hosts a playbook would touch, and it takes --limit into account.
A term that matches nothing is not an error. Ansible warns and carries on with the rest:
[WARNING]: Could not match supplied host pattern, ignoring: nosuch- Which pattern selects the hosts that are in both `web` and `production`?
- What does `'!staging,web'` select?
- A play has `hosts: db`. You run it with `--limit web`, and no host is in both groups. What happens?
- Why must `hosts: *.example.com` be quoted?
- Which command lists the hosts each play of `site.yml` would run on, without running it?
Takeaway
Select hosts by name, by group, with wildcards and with lists. Use the chapter lab to check this on a real host.