Developing Modules
Relevant source files
The following files were used as context for generating this wiki page:- lib/ansible/module_utils/basic.py
- lib/ansible/executor/powershell/module_wrapper.ps1
- test/integration/targets/ansiballz_python/library/custom_module.py
- test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py
- lib/ansible/cli/doc.py
- lib/ansible/modules/apt.py
- AGENTS.md
- lib/ansible/modules/uri.py
Ansible modules are reusable, standalone scripts that can be executed by Ansible. They are the fundamental units of work that manage system resources, interact with services, or execute arbitrary commands on a target node. The design philosophy emphasizes simplicity, agentless operation via SSH, and human-friendly infrastructure description. Modules can be written in any dynamic language, but the most common and well-supported language is Python, utilizing the AnsibleModule class from the provided module utilities.
This document outlines the core concepts, structure, and best practices for developing Ansible modules, based on the utilities and validation logic within the ansible-core repository. It covers both Python and PowerShell modules, focusing on the structure, documentation, argument handling, and execution flow required for a robust and compliant module.
Core Module Structure
All Ansible modules, regardless of language, must adhere to a specific structure for documentation, execution, and returning results. Python modules have a well-defined structure centered around the AnsibleModule class.
Python Module Anatomy
A standard Python module consists of several key components: a shebang, license information, documentation strings, module imports, and an execution block.
- Shebang and License: Modules must start with
#!/usr/bin/pythonand include a GPLv3 license header within the first 20 lines.
Sources: test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py:315-325, 360-375 - Documentation Strings: Three static string variables—
DOCUMENTATION,EXAMPLES, andRETURN—must be defined. These are parsed byansible-docand must be valid YAML.
Sources: lib/ansible/cli/doc.py:720-726, lib/ansible/modules/apt.py:18-217 - Imports: All Python imports must appear after the documentation strings. Modules should only import from the Python standard library and
ansible.module_utils.
Sources: AGENTS.md:129-130, test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py:488-511 - Main Execution Block: The module's logic is typically contained within a
main()function, which is called from anif __name__ == '__main__':block. This is where theAnsibleModuleclass is instantiated and used.
Sources: lib/ansible/modules/uri.py:539-586
The following diagram illustrates the required order of components in a Python module file.
PowerShell Module Structure
PowerShell modules follow a similar pattern but use PowerShell-specific conventions.
- Shebang: Must start with
#!powershell. - Module Utilities: Instead of Python imports, PowerShell modules use
#Requiresstatements to include utilities fromAnsible.ModuleUtils. - Documentation: Documentation is typically not embedded directly. It is provided in a separate
.pyor.yml"sidecar" file with the same base name as the PowerShell script. The validation logic actively looks for these files.
Sources: lib/ansible/executor/powershell/module_wrapper.ps1:1-10, test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py:309-314, 552-570, 608-617
The AnsibleModule Class
The AnsibleModule class, found in lib/ansible/module_utils/basic.py, is the cornerstone of Python module development. It handles parameter parsing, validation, check mode, and returning results to the Ansible engine.
Initialization and Argument Specification
The module's entry point is the instantiation of AnsibleModule. The constructor accepts several arguments that define the module's behavior and its accepted parameters.
# lib/ansible/modules/uri.py:540-571
def main():
argument_spec = url_argument_spec()
# ... argument_spec updates ...
module = AnsibleModule(
argument_spec=argument_spec,
add_file_common_args=True,
mutually_exclusive=[['body', 'src']],
)
# ... module logic ...
The argument_spec dictionary is the most critical part, defining the parameters the module accepts.
AnsibleModule Parameter | Description |
|---|---|
argument_spec | A dictionary defining the module's 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 parameters that cannot be used together. |
required_together | A list of lists, where each inner list contains parameters that must be used together. |
required_one_of | A list of lists, where each inner list contains parameters of which at least one must be provided. |
required_if | A list defining conditional parameter 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:270-293, lib/ansible/modules/apt.py:534-546
argument_spec Dictionary
Each key in the argument_spec dictionary is a parameter name, and its value is another dictionary defining its properties.
argument_spec Key | Description |
|---|---|
type | The expected data type (e.g., str, int, bool, list, dict, path). |
default | The default value if the parameter is not provided. |
required | A boolean (True/False) indicating if the parameter is mandatory. |
choices | A list of valid values for the parameter. |
no_log | A boolean. If True, the parameter's value will be masked in logs. |
aliases | A list of alternative names for the parameter. |
options / suboptions | A nested argument_spec for complex dictionary parameters. |
Sources: lib/ansible/module_utils/basic.py:1260-1480, lib/ansible/modules/uri.py:302-311
Module Execution Flow
The AnsibleModule class standardizes the execution flow from parameter parsing to returning a result.
The following diagram shows the typical lifecycle of an Ansible module.
Returning State
Modules must communicate their result back to the Ansible engine by printing a JSON string to standard output. The AnsibleModule class provides two methods for this:
exit_json(**kwargs): Call this upon successful completion. Thechangedboolean argument is mandatory to indicate whether a change was made on the target system. This is crucial for idempotency and reporting.fail_json(msg, **kwargs): Call this when an error occurs. It terminates the module and reports a failure. Themsgargument provides a human-readable error message.
Sources: lib/ansible/module_utils/basic.py:789-831
Common Helper Methods
AnsibleModule provides a rich set of helper methods to interact with the target system.
| Method | Description |
|---|---|
run_command(args, ...) | Executes a command on the target system. It is preferred over subprocess as it handles environment setup and sanitizes output. |
get_bin_path(arg, required=False) | Finds the full path of an executable in the system's PATH. |
atomic_move(src, dest) | Atomically moves a file, preserving permissions and SELinux context where possible. This is the recommended way to update files. |
set_fs_attributes_if_different(...) | A wrapper to set file owner, group, mode, and SELinux context if they differ from the desired state. |
deprecate(msg, version, ...) | Issues a deprecation warning that will be displayed to the user. |
warn(msg) | Issues a general warning. |
Sources: lib/ansible/module_utils/basic.py:355-364, 366-377, 1022-1033, 1482-1678
Module Documentation and Validation
High-quality documentation is a requirement for Ansible modules. The ansible-doc command and the validate-modules sanity test rely on correctly formatted documentation blocks.
Documentation Structure
The DOCUMENTATION string is a YAML document that describes the module's purpose, options, author, and other metadata.
Key fields include:
module: The name of the module.short_description: A one-line summary.description: A more detailed explanation.version_added: The Ansible version in which the module was introduced.author: The author(s) of the module.options: A dictionary describing each parameter. The structure of each option mirrors theargument_specdefinition.notes: Important usage notes.seealso: Links to related modules or documentation.extends_documentation_fragment: Includes documentation from a shared fragment.
The EXAMPLES block provides playbook snippets, and the RETURN block documents the structure of the data returned by the module on success.
Sources: lib/ansible/cli/doc.py:720-745, lib/ansible/modules/blockinfile.py:10-101, test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py:730-974
Validation
The validate-modules sanity test enforces many rules for module development:
- Documentation Schema:
DOCUMENTATION,EXAMPLES, andRETURNmust be valid YAML and conform to a specific schema. - Argument Spec Consistency: The
argument_specin the code must match theoptionssection in theDOCUMENTATION. This includes types, defaults, required status, and aliases. - Import Order: Imports must be placed after documentation blocks.
- Licensing: A GPLv3 header must be present.
- Forbidden Calls: Direct use of
sys.exit()is forbidden;fail_json()must be used instead.