Skip to main content
RunBook Academy

AnsibleXXVIII · Plugins, Lookups and FiltersPlugins, lookups and filters

The plugin types and how they load

Intermediate⏱ ~19 minansible-docansible-config

What you'll learn

  • Name the plugin types and state what each one hooks into
  • Enumerate what a controller actually has, per type, with ansible-doc
  • Identify which plugin type governs a behaviour you want to change
  • Say where plugins may live in a role, a collection or a project

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

Not yet marked complete on this device.

Almost every “can Ansible do X” question has the same answer: yes, and the thing that does it is a plugin. Which kind of plugin is the part worth knowing, because it tells you where to look and what you are allowed to change.

This lesson is a map, not a development course. You will very probably never write a connection plugin. You will, several times a year, need to know that the behaviour annoying you is a connection plugin’s behaviour and not a module’s.

The types, and what each hooks into

Read-only / Safewhat this controller actually has
$ for t in connection inventory lookup filter test callback cache strategy become shell vars; do printf '%-12s %s\n' "$t" "$(ansible-doc -t $t -l | wc -l)"; done
connection   4
inventory    9
lookup       25
filter       122
test         82
callback     5
cache        2
strategy     4
become       3
shell        3
vars         1

Those numbers are the whole ansible-core extension surface. Everything else arrives with collections.

TypeHooks intoWhen you care
connectionHow the controller reaches a targetContainers, network devices, Windows, anything that is not SSH
inventoryWhere the host list comes fromCloud APIs, a CMDB, a script — Part XXIX
lookupFetching data on the controller during templatingReading files, environment, secret stores — lesson 2
filterTransforming a value in a Jinja expressionEvery day — lesson 5
testAnswering a yes/no question in Jinjais defined, is match — lesson 6
callbackReacting to run events; all outputChanging what a run reports — lesson 7
cacheStoring gathered facts between runsFleet-scale runs — Part XXXIV
strategyThe order tasks execute across hostslinear versus free
becomeHow privilege escalation is performedsudo, su, doas, runas
shellWhich shell dialect commands are built forWindows targets, unusual shells
varsInjecting variables from a sourcegroup_vars/host_vars loading itself
actionController-side work wrapped around a moduleExplained in Part VIII
terminal / cliconf / httpapi / netconfNetwork device interactionOnly with network collections installed

Two entries in that table are worth pausing on because they are usually assumed to be built-in machinery rather than plugins.

vars has exactly one plugin, and it is how group_vars works.

Read-only / Safethe mechanism you thought was hard-coded
$ ansible-doc -t vars -l
ansible.builtin.host_group_vars In charge of loading group_vars and host_va...

group_vars/ and host_vars/ are not a special case in the engine. They are a plugin, which is why a collection can add another source of variables that behaves the same way — and why Part XII’s precedence list has an entry for it rather than treating it as the ground truth.

shell is not command versus shell. The shell plugin decides how Ansible builds the command line it sends: quoting, path separators, temporary directory syntax. sh for POSIX targets, powershell and cmd for Windows. It is invisible until you target Windows, and then it explains a whole category of confusion at once.

Where plugins live

Four places, in the order they are searched:

1. Adjacent to a playbook. A directory named for the plugin type beside the playbook: filter_plugins/, lookup_plugins/, callback_plugins/, and library/ for modules. Historical, still works, appropriate for a one-repository helper.

2. Inside a role. The same directories at the top of a role directory. A plugin here is loaded while that role is in play — useful for a role-private helper, awkward for anything shared, because its availability depends on execution context.

3. Inside a collection, under plugins/<type>/. plugins/filter/, plugins/lookup/, plugins/modules/. This is the modern answer and the only one that gives the plugin a fully qualified name, a version and a distribution mechanism.

4. On a configured plugin path. Each type has its own setting — DEFAULT_FILTER_PLUGIN_PATH, DEFAULT_CALLBACK_PLUGIN_PATH, and so on — each defaulting to a per-user directory plus a system-wide one.

Read-only / Safethe configured paths, per type
$ ansible-config dump | grep -E '_PLUGIN_PATH'
DEFAULT_ACTION_PLUGIN_PATH(default) = ['/home/opsuser/.ansible/plugins/action', '/usr/share/ansible/plugins/action']
DEFAULT_CALLBACK_PLUGIN_PATH(default) = ['/home/opsuser/.ansible/plugins/callback', '/usr/share/ansible/plugins/callback']
DEFAULT_FILTER_PLUGIN_PATH(default) = ['/home/opsuser/.ansible/plugins/filter', '/usr/share/ansible/plugins/filter']
DEFAULT_LOOKUP_PLUGIN_PATH(default) = ['/home/opsuser/.ansible/plugins/lookup', '/usr/share/ansible/plugins/lookup']

Illustrative output

Note the per-user directory again. Everything Part XXVII said about per-user collection paths applies identically to plugins, with one extra wrinkle: plugins outside a collection have no version. A filter in ~/.ansible/plugins/filter/ cannot be pinned, cannot be verified and does not appear in any manifest. It is the least reproducible thing you can put on a controller.

Which plugin governs which behaviour

The practical use of the map. A behaviour you want to change, and the plugin type that owns it:

“I want to…”Plugin type
…run tasks against a container instead of over SSHconnection
…build the host list from the cloud provider’s APIinventory
…read a value from a secret store into a variablelookup
…reshape a list of dictionaries into a dictionaryfilter
…check whether a string matches a pattern in when:test
…emit per-task timings, or JUnit XML for CIcallback
…keep facts between runs so the next run is fastercache
…let fast hosts move ahead instead of waiting for the batchstrategy
…escalate with doas rather than sudobecome
…keep the output of a task for later usenone — that is register

The last row is the useful one. Not every behaviour is a plugin. register, when, loop, delegate_to and serial are keywords — play, block and task directives handled by the engine, documented under a different command:

Read-only / Safekeywords are not plugins
$ ansible-doc -t keyword -l | wc -l
109

If ansible-doc -t <type> -l cannot find what you are looking for, try ansible-doc -t keyword -l before concluding it does not exist.

Knowledge check

Knowledge check · 4 questions

  1. Q1. Which statement about where plugin code executes is correct?

  2. Q2. You want a run to emit per-task timings without editing any playbook. Which plugin type governs that?

  3. Q3. Which are true about plugins that live in a collection rather than a filter_plugins/ directory beside a playbook? Select all that apply.

  4. Q4. group_vars/ and host_vars/ loading is implemented as a vars plugin rather than being hard-coded in the engine.

Passing score: 75%. Answers are checked in this browser.