Part 25 — Module Architecture¶
Chapter status: outline
This chapter is scoped but not yet written in full prose. The sections below define what each part will cover.
This chapter turns "modules are Python scripts that return JSON" (Volume 3) into a full implementation guide, ending with a custom module built from scratch.
Why This Exists¶
- Every playbook is ultimately a sequence of module calls — understanding module internals is what separates "consumes Ansible" from "can fix or extend it."
Problem Statement¶
- A module has to satisfy a strict contract (accept arguments a specific way, return JSON a specific way, support
--check/--diff) so that the execution engine, callback plugins, and-C/--diffflags all work uniformly across thousands of different modules written by different authors.
Internal Architecture¶
AnsibleModule(the Python API): the base class handling argument parsing/validation,no_logredaction, exit/fail helpers (exit_json,fail_json), and check-mode awareness.- Ansiballz packaging: how a module's source, plus the
AnsibleModuleboilerplate and anymodule_utilsimports, get assembled into a single self-contained zipapp shipped to the managed node (previewed in Volume 3, Part 17). - Argument spec: the
argument_specdict declaring each parameter's type, required-ness, defaults, choices, and mutual exclusivity — the same spec both validates input and generatesansible-docdocumentation. - Return values: the
RETURNdocstring block and the actual JSON keys a module emits (changed,failed, module-specific data) — contract that downstream tasks'register:results depend on.
Step-by-Step Explanation — Writing a Module From Scratch¶
- Define
DOCUMENTATION,EXAMPLES, andRETURNYAML docstrings (source of truth foransible-doc). - Define
argument_specand instantiateAnsibleModule(argument_spec=..., supports_check_mode=True). - Implement the check-current-state-then-act pattern: read current state, compare to desired state from arguments, only change what differs.
- Respect
module.check_mode: compute and report what would change without actually changing it. - Call
module.exit_json(changed=bool, **result_data)on success ormodule.fail_json(msg=...)on failure. - Handle diff mode by populating a
before/afterstructure when--diffis requested.
Production Best Practices¶
- Writing modules to be idempotent by construction (check-then-act), not by accident — this is the single most important property a module must have (ties directly to Volume 1, Part 5).
- Using
module_utilsto share logic across a family of related modules instead of duplicating code. - Never logging sensitive argument values — using
no_log=Trueon any password/token/secret argument in the spec.
Common Mistakes¶
- Writing a module that always reports
changed=Trueregardless of actual state, breaking idempotency and handler-notification behavior for consumers. - Not implementing check-mode support (
supports_check_mode=Truecombined with actually honoringmodule.check_mode), silently making the module unsafe to use with--check. - Swallowing exceptions instead of calling
fail_jsonwith a clear message, producing confusing downstream failures.
Performance Considerations¶
- Expensive current-state lookups (e.g., large API calls) inside a module run on every task execution — caching or scoping those lookups matters at scale.
Security Considerations¶
no_logfor sensitive arguments; validating and sanitizing any values passed to subprocess calls inside a module to avoid command injection when a module shells out.
Interview Questions¶
- "What does a module have to do to correctly support check mode?"
- "How does argument_spec relate to both validation and ansible-doc's generated documentation?"
- "Why is the check-then-act pattern central to writing an idempotent module?"
Hands-On Lab¶
- Write a minimal custom module (e.g., one that ensures a line exists in a file) supporting
argument_spec, check mode, and diff mode, place it in a project'slibrary/directory, and call it from a playbook.
Summary¶
- A well-built module is defined by its contract (
DOCUMENTATION/EXAMPLES/RETURN,argument_spec, JSON exit contract) and its behavior (check-then-act idempotency, check-mode and diff-mode support) — both halves matter equally.
Next¶
Continue to Part 26 — Plugin Architecture.