Modules
Relevant source files
The following files were used as context for generating this wiki page:
- lib/ansible/module_utils/basic.py
- test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py
- lib/ansible/cli/doc.py
- AGENTS.md
- README.md
- lib/ansible/modules/apt.py
- lib/ansible/plugins/doc_fragments/template_common.py
- lib/ansible/modules/replace.py
- lib/ansible/modules/uri.py
Ansible modules are the fundamental units of work that are executed in a playbook task. They are reusable, standalone scripts that can be written in any language, though Python is the most common. Modules are designed to be idempotent, meaning they can be run multiple times without changing the result beyond the initial application. They handle a wide variety of tasks, including configuration management, application deployment, cloud provisioning, and ad-hoc task execution.
The core design principle of modules is to be agentless, leveraging existing daemons like SSH to execute on remote machines. This simplifies setup and management, as no custom agents or additional open ports are required on the target nodes.
Sources: README.md, lib/ansible/modules/replace.py:27-30
Module Anatomy
A standard Ansible module written in Python has a specific structure that is enforced by validation tools. The file must contain several key components in a particular order.
This diagram illustrates the required and conventional ordering of components within a Python module file.
Core Components
- Shebang: For Python modules, the interpreter line should be
#!/usr/bin/python. For new-style modules, this is optional, but if present, it must be correct. PowerShell modules must start with#!powershell.
Sources: test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py:364-383 - License Header: All core modules must include a GPLv3 license header within the first 20 lines.
Sources: test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py:403-418, AGENTS.md - Documentation Strings: Three YAML-formatted string variables are required for metadata, examples, and return value descriptions.
DOCUMENTATION: Contains metadata about the module, including its description, options, author, and version.
EXAMPLES: Provides example usage of the module in a playbook.RETURN: Describes the data structure returned by the module on success.
Sources: test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py:613-649
- Imports: All Python imports, including
from ansible.module_utils.basic import AnsibleModule, must appear after theDOCUMENTATION,EXAMPLES, andRETURNblocks.
Sources: test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py:498-531, AGENTS.md main()function: This function contains the primary logic of the module. It typically starts by instantiating theAnsibleModuleclass.
Sources: lib/ansible/modules/replace.py:221, lib/ansible/modules/apt.py:1034
The AnsibleModule Helper Class
The AnsibleModule class, found in lib/ansible/module_utils/basic.py, is a cornerstone of Python-based module development. It simplifies argument parsing, result formatting, and interaction with the Ansible controller.
Instantiation
A module typically instantiates this class at the beginning of its main() function, passing the argument_spec and other metadata.
# lib/ansible/modules/replace.py:222-237
def main():
module = AnsibleModule(
argument_spec=dict(
path=dict(type='path', required=True, aliases=['dest', 'destfile', 'name']),
regexp=dict(type='str', required=True),
replace=dict(type='str', default=''),
after=dict(type='str'),
before=dict(type='str'),
backup=dict(type='bool', default=False),
validate=dict(type='str'),
encoding=dict(type='str', default='utf-8'),
),
add_file_common_args=True,
supports_check_mode=True,
)
The constructor accepts several arguments to define the module's behavior:
| Parameter | Description |
|---|---|
argument_spec | A dictionary defining the module's accepted parameters, their types, defaults, and other constraints. |
supports_check_mode | A boolean indicating if the module can run in "check mode" without making changes. |
mutually_exclusive | A list of lists, where each inner list contains arguments that cannot be used together. |
required_together | A list of lists, where each inner list contains arguments that must be used together. |
required_one_of | A list of lists, where each inner list contains arguments of which at least one is required. |
required_if | A list of lists defining conditional argument requirements. |
add_file_common_args | A boolean to automatically add common file-related arguments like owner, group, and mode. |
Sources: lib/ansible/module_utils/basic.py:503-520
Execution Flow
The following diagram shows the typical execution flow of a Python-based Ansible module using the AnsibleModule helper.
Key Methods
exit_json(**kwargs): Exits the module successfully, returning a JSON object to the controller. It automatically includes achangedkey (defaulting toFalse).fail_json(msg, **kwargs): Exits the module with a failure status, returning a JSON object with afailed: truekey and an error message.run_command(args, ...): Executes a command on the target system and returns the return code, stdout, and stderr. This is the preferred way to run external commands.get_bin_path(arg, required=False): Finds the full path to an executable in the system'sPATH.load_file_common_arguments(params): A helper to process common file-related parameters (owner,group,mode, etc.).atomic_move(src, dest): Atomically moves a file from a source to a destination, preserving permissions and SELinux context where possible.
Sources: lib/ansible/module_utils/basic.py
Documentation and Metadata
Module documentation is not just for humans; it's machine-parsable metadata used by ansible-doc and other tools.
Documentation Sections
Documentation is defined in three top-level string variables within the module file. These are parsed as YAML.
DOCUMENTATION: The main metadata block.EXAMPLES: Usage examples in YAML format.RETURN: Describes the structure and meaning of the data returned by the module.
Sources: test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py:613-649
Doc Fragments
To avoid repetition, common documentation for options can be shared using "doc fragments". A module can inherit a fragment by setting the extends_documentation_fragment key.
# lib/ansible/modules/replace.py:12-16
DOCUMENTATION = r"""
---
module: replace
author: Evan Kaufman (@EvanK)
extends_documentation_fragment:
- action_common_attributes
The fragment itself is a Python class containing a DOCUMENTATION string.
# lib/ansible/plugins/doc_fragments/template_common.py:10-13
class ModuleDocFragment(object):
# Standard template documentation fragment, use by template and win_template.
DOCUMENTATION = r"""
description:
- Templates are processed by the L(Jinja2 templating language,https://jinja.palletsprojects.com/en/stable/).
...
"""
Sources: lib/ansible/plugins/doc_fragments/template_common.py, lib/ansible/modules/replace.py:12-16
Semantic Markup
Documentation strings use a special markup format to create rich, cross-referenced text. This is parsed by ansible-doc.
| Markup | Description | Example |
|---|---|---|
M(module_name) | Link to another module. | M(ansible.builtin.copy) |
P(plugin_name#type) | Link to a plugin of a specific type. | P(ansible.builtin.shuffle#filter) |
U(url) | A raw URL. | U(https://docs.ansible.com) |
L(text,url) | A named link. | L(the docsite,https://docs.ansible.com) |
I(text) | Italicized text. | I(apt) |
B(text) | Bold text. | B(not) |
C(text) | Monospaced text for code/constants. | C(utf-8) |
O(option_name) | Link to an option within the same module. | O(dest) |
V(value) | A literal value for an option. | V(present) |
E(env_var) | An environment variable. | E(TEST_ENV) |
RV(return_value) | A return value. | RV(m_middle.suboption) |
Sources: lib/ansible/cli/doc.py:123-138, test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/modules/randommodule.py
Module Validation
The validate-modules sanity test enforces a strict set of rules to ensure module quality and consistency. Key checks include:
- GPLv3 License: The GPLv3 license header must be present.
- Documentation Presence:
DOCUMENTATION,EXAMPLES, andRETURN(for new modules) must exist. - Documentation Schema: The YAML in the documentation blocks must conform to a specific schema. For example, all options must have a
description. - Import Order: Python imports must appear after the documentation blocks.
- Forbidden Calls: Direct use of
sys.exit()is forbidden; modules must useAnsibleModule.fail_json()orAnsibleModule.exit_json(). - Argument Spec Consistency: The
argument_specin the code must match theoptionsdocumented in theDOCUMENTATIONblock. This includes types, defaults, and required status. - Semantic Markup: The markup used in documentation strings is validated for correctness.
Sources: test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py
Module Types and Execution
While most modules are written in Python for convenience, Ansible can execute any script that adheres to its simple contract: read a JSON string from standard input and print a JSON string to standard output.
- Python Modules: The most common type. They use the
AnsibleModulehelper class fromansible.module_utils.basicto handle argument parsing, logging, and returning results. - PowerShell Modules: Used for managing Windows targets. These are
.ps1files that must begin with a#!powershellshebang line. They often use PowerShell-specific module utils. - JSON-only Modules: Any executable that can process JSON arguments and produce JSON output can be an Ansible module.
Modules are packaged and transferred to the remote node for execution. A key constraint is that modules can only import from lib/ansible/module_utils/, as this is the shared code that gets packaged with them.
Sources: AGENTS.md, README.md:19-22, test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py:364-371, 437-440
Licensing
Licensing is a critical requirement for all contributions.
- ansible-core code (including modules): Must be GPLv3 compatible.
lib/ansible/module_utils/: Defaults to BSD-2-Clause, which is more permissive.
New dependencies must be compatible with these licenses.
Sources: AGENTS.md