AnsibleXXX · Host Targeting and Blast RadiusPatterns
Host pattern syntax
What you'll learn
- Write the correct pattern for a stated target set using union, intersection and exclusion
- Predict what a glob matches, including the group names it matches
- Use group indexing and slices, and state which slice forms are supported
- Recognise the pattern forms that look plausible and are not supported
Prerequisites
Verified against ansible-core 2.21.x · ansible (community package) 14.x · Python (controller) 3.12+ · ansible-lint 26.x · Molecule 26.x · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL / Rocky / AlmaLinux 9.x · 2026-08-11
Pattern syntax is the most confidently misremembered part of Ansible.
People are sure web[:3] works, sure that a slice is exclusive at the
upper bound like Python’s, and sure that an IP glob matches by address.
None of those is true in ansible-core 2.21.
So everything below was run. The inventory is ten hosts in overlapping groups, using documentation-reserved addresses, and every output is the real thing:
all:
children:
production:
children:
web:
hosts:
web01.example.com: {ansible_host: 192.0.2.11}
web02.example.com: {ansible_host: 192.0.2.12}
web03.example.com: {ansible_host: 192.0.2.13}
web04.example.com: {ansible_host: 192.0.2.14}
db:
hosts:
db01.example.com: {ansible_host: 192.0.2.21}
db02.example.com: {ansible_host: 192.0.2.22}
cache:
hosts:
cache01.example.com: {ansible_host: 192.0.2.31}
staging:
children:
web_stage:
hosts:
stage-web01.example.com: {ansible_host: 198.51.100.11}
stage-web02.example.com: {ansible_host: 198.51.100.12}
db_stage:
hosts:
stage-db01.example.com: {ansible_host: 198.51.100.21}
eu_west:
hosts: {web01.example.com: , db01.example.com: , cache01.example.com: }
us_east:
hosts: {web02.example.com: , web03.example.com: , web04.example.com: , db02.example.com: }
canary:
hosts: {web01.example.com: }
do_not_automate:
hosts: {db01.example.com: }Verify anything here yourself with ansible -i inventory/hosts.yml '<pattern>' --list-hosts, which connects to nothing and is the safest
command in the course.
The complete vocabulary
| Form | Meaning | Example |
|---|---|---|
all or * | Every host in the inventory | all |
| name | One host, or one group | web01.example.com, web |
: or , | Union — add | web:db |
:& | Intersection — and | web:&eu_west |
:! | Exclusion — remove | web:!canary |
* inside a name | Glob, over host and group names | web* |
~ prefix | Regular expression | ~web0[13] |
name[n] | The nth member of a group, zero-based | web[0] |
name[n:m] | Members n to m inclusive | web[0:2] |
name[n:] | From n to the end | web[1:] |
name[-1] | The last member | web[-1] |
, and : are interchangeable for union. Prefer , when the pattern
contains an IPv6 address, since : is ambiguous there; prefer :
otherwise for consistency with :& and :!.
Union, intersection, exclusion
The three operators, each against the same inventory.
$ ansible -i inventory/hosts.yml 'web:db' --list-hosts hosts (6):
web01.example.com
web02.example.com
web03.example.com
web04.example.com
db01.example.com
db02.example.com$ ansible -i inventory/hosts.yml 'web:&eu_west' --list-hosts hosts (1):
web01.example.com$ ansible -i inventory/hosts.yml 'web:!canary' --list-hosts hosts (3):
web02.example.com
web03.example.com
web04.example.comThey combine, and the result does not depend on where in the string each term appears:
$ ansible -i inventory/hosts.yml 'web:!canary:&us_east' --list-hosts hosts (3):
web02.example.com
web03.example.com
web04.example.comGlobs match group names too
This is the trap that catches experienced people, and it is entirely logical once seen.
$ ansible -i inventory/hosts.yml 'web*' --list-hosts hosts (6):
web01.example.com
web02.example.com
web03.example.com
web04.example.com
stage-web01.example.com
stage-web02.example.comA pattern is matched against every host name and every group name in
the inventory. web* matched the four web*.example.com hosts by name
and the web_stage group by name, and a group match contributes all its
members.
The operational consequence is direct: a glob you wrote to mean “the web servers” reached staging, and would equally reach production if the groups were named the other way round. Group naming conventions and glob patterns interact, and the interaction is invisible until you print the host list.
Regular expressions
Prefix with ~ and the rest is a Python regular expression:
$ ansible -i inventory/hosts.yml '~web0[13]' --list-hosts hosts (2):
web01.example.com
web03.example.comNote what that implies about anchoring. ~web0[13] matched
web01.example.com, which the expression does not describe in full — so
the match is applied at the start of the name and does not have to
consume all of it. The mirror test confirms it:
$ ansible -i inventory/hosts.yml '~example' --list-hosts[WARNING]: Could not match supplied host pattern, ignoring: ~example
[WARNING]: No hosts matched, nothing to do
hosts (0):$ ansible -i inventory/hosts.yml '~^web' --list-hosts hosts (6):
web01.example.com
web02.example.com
web03.example.com
web04.example.com
stage-web01.example.com
stage-web02.example.comIndexing and slicing a group
Useful for canaries: “the first host of this group”, without hardcoding which host that is.
$ ansible -i inventory/hosts.yml 'web[0]' --list-hosts hosts (1):
web01.example.com$ ansible -i inventory/hosts.yml 'web[0:2]' --list-hosts hosts (3):
web01.example.com
web02.example.com
web03.example.comAn open-ended upper bound works, and behaves as you would expect:
$ ansible -i inventory/hosts.yml 'web[1:]' --list-hosts hosts (3):
web02.example.com
web03.example.com
web04.example.com$ ansible -i inventory/hosts.yml 'web[-1]' --list-hosts hosts (1):
web04.example.comThe forms that are not supported
Verified, because “it looked like it should work” is how these get into production scripts.
$ ansible -i inventory/hosts.yml 'web[:3]' --list-hosts[WARNING]: Could not match supplied host pattern, ignoring: web[:3]
[WARNING]: No hosts matched, nothing to do
hosts (0):$ ansible -i inventory/hosts.yml 'web[-2:]' --list-hosts[WARNING]: Could not match supplied host pattern, ignoring: web[-2:]
[WARNING]: No hosts matched, nothing to do
hosts (0):$ ansible -i inventory/hosts.yml '192.0.2.*' --list-hosts[WARNING]: Could not match supplied host pattern, ignoring: 192.0.2.*
[WARNING]: No hosts matched, nothing to do
hosts (0):$ ansible -i inventory/hosts.yml 'WEB' --list-hosts[WARNING]: Could not match supplied host pattern, ignoring: WEB
[WARNING]: No hosts matched, nothing to do
hosts (0):Knowledge check
Knowledge check · 5 questions
Q1. A group web contains web01 through web04 in that order. How many hosts does the pattern web[0:2] resolve to?
Q2. An inventory has a group web with four hosts and a group web_stage with two staging hosts. The pattern web* resolves to how many hosts, and why?
Q3. Which of these patterns resolve to zero hosts in ansible-core 2.21? Select all that apply.
Q4. A typo in an exclusion term makes the run target fewer hosts than intended.
Q5. A shared playbook uses hosts: "{{ target_group | default('all') }}" so it is convenient to run. What is wrong with that?
Passing score: 75%. Answers are checked in this browser.