Chapter 3: Modules & Ad-hoc Commands
Topic 17

ansible-doc

WorkflowReference

With thousands of modules across ansible-core and the installed collections, the skill that matters is finding the right module and its exact arguments fast, and ansible-doc does that from the terminal without a browser. ansible-doc apt prints every parameter, its type, default, and choices, plus the return values and runnable examples.

It is the same reference the website renders, pinned to the exact version you have installed — which is the version that actually matters. The docs you read are the docs your control node runs, so an argument the page shows is an argument your plays can use.

The Command

ansible-doc <module> prints the full page for one module: parameters, return values, examples. ansible-doc -l lists every available module, and ansible-doc -l | grep service is how you discover that service, systemd, and service_facts all exist before guessing at a name.

That pairing — full page for the module you know, filtered list for the one you do not — covers most lookups. Searching the list for a keyword turns "I think there's a module for this" into the module's real name and the collection it lives in.

Reading the Parameter Table

Each parameter shows its type, whether it is required, its default, and its allowed choices. This is where you learn that apt takes state with choices present, absent, and latest, so you stop guessing argument names and read them.

Reading the table beats memory. The difference between a task that works and an Unsupported parameters error is usually one argument name you half-remembered, and the table has the exact spelling, type, and default in front of you.

Return Values

The doc lists exactly what the module returns — changed, stdout, dest, and module-specific keys — which is precisely what you register and branch on. Reading the return section before you write the dependent task saves the trial-and-error of discovering the key shape at run time.

This closes the loop with the module model: a later task branches on what the module emits, so knowing the return shape up front means you write the conditional once, correctly, instead of running the play to find out which key carries the value you need.

FQCN in the Docs

The doc header shows the fully-qualified collection name — ansible.builtin.apt, community.general.timezone. The short name works when the collection is installed, but the FQCN is what you write in production plays and what tells you which collection a module lives in.

A play written with short names breaks on a control node where the collection is not installed, with an error that points at the missing module rather than the missing dependency. The FQCN the doc shows makes that dependency explicit, so the failure mode becomes obvious or never happens.

The Examples Block

ansible-doc ends with copy-ready task examples that show the common argument combinations. Reading them is faster than assembling a task from the parameter table alone, especially for a module you are meeting for the first time and do not yet have a feel for.

The examples encode the idioms — which arguments usually appear together, what a realistic invocation looks like. You start from a working task and adjust it, rather than building one argument at a time and hoping the combination is valid.

Snippet and Plugin Docs

ansible-doc -s <module> prints a terse, paste-ready task stub with every argument commented, so you delete what you do not need rather than building the task from scratch. It is the fastest path from "I want to use this module" to a task skeleton in your playbook.

ansible-doc -t <type> documents lookup, filter, and inventory plugins too, not just modules. The same tool covers the whole plugin surface, so the reference habit you build for modules carries straight over to the plugins you met in Chapter 2 and the ones still ahead.

Common Mistakes
  • Guessing module argument names from memory and getting Unsupported parameters errors, when ansible-doc <module> lists the exact names, types, and defaults in seconds.
  • Reading the website docs for the latest release while running an older ansible-core, then hitting an argument that does not exist in your version — ansible-doc is pinned to what you actually have installed.
  • Writing a play with short module names and having it break on a control node where the collection is not installed, because the FQCN that ansible-doc shows would have made the dependency explicit.
  • Inventing a module that does not exist, when ansible-doc -l would have shown it is not there and pointed you at the real module's name and collection instead.
  • Branching on a return value the module does not actually emit, because the return-values section of ansible-doc was never read and the key was assumed.
Best Practices
  • Run ansible-doc <module> before writing any task with a module you do not know cold, reading the parameter and return sections rather than guessing.
  • Discover modules with ansible-doc -l | grep <keyword> instead of inventing names, so you find the real module and the collection it lives in.
  • Read and write the FQCN — ansible.builtin.service — that ansible-doc shows in production plays, making the collection dependency explicit and unambiguous.
  • Use ansible-doc -s <module> to generate a paste-ready task stub with every argument, then delete what you do not need rather than building the task from scratch.
  • Read the return-values section before writing a task that branches on a module's result, so you register and check keys the module actually emits.
Comparable tools terraform providers schema / registry docs, the provisioning-side reference man pages the Unix analog for binaries puppet describe / Forge docs, the same role for Puppet types

Knowledge Check

What does each of ansible-doc -l, ansible-doc <module>, and ansible-doc -s <module> give you?

  • A list of every module, the full page for one, and a paste-ready stub with every argument commented
  • The very same full documentation page rendered three separate times, differing only in their colour scheme
  • A pre-flight syntax check, then a no-write dry run, and finally a live run of the named module
  • The module's full Python source code, its accompanying test suite, and its version changelog

Why do version-pinned local docs beat reading the website?

  • ansible-doc reflects the exact ansible-core installed, so it never shows an argument your version lacks
  • The docs website is frequently offline for maintenance, while your local docs stay always reachable
  • Local docs bundle in the module's full source code, which the public website quietly hides away from you
  • The website only ever documents the community-maintained collections, never any built-in core modules

Why does the FQCN in the doc header matter?

  • It names the collection the module lives in, so a play declares its dependency and won't break on an unresolved short name
  • It is now strictly required syntax everywhere, since the short module names were removed from Ansible entirely a while back
  • It makes the targeted module run measurably faster by skipping all name resolution work at run time
  • It quietly encrypts the underlying module reference so that the whole play can then be shared around safely

Where are a module's return values documented, and why read them first?

  • In the return-values section of ansible-doc — reading it first lets you branch on keys the module emits, not guess the shape
  • Only buried away in the public website changelog, which is precisely why the local docs end up insufficient here
  • In the play recap printed out at the very end of the run, so they simply cannot be known reliably in advance
  • In the parameter table itself, since the input arguments and the return values all share one single combined section in there

You got correct