Skip to content

Custom Modules

Ansible's custom modules allow you to extend its capabilities by implementing logic for tasks that aren't covered by built-in modules. This guide walks you through creating, testing, and using custom modules for unique workflows, such as interacting with proprietary APIs or managing custom resources.


Module Structure and Requirements

A custom Ansible module is a Python script that adheres to Ansible's module API. The core components include:
- main.py: The module's entry point, containing the main() function.
- module_utils/: Optional directory for utility functions shared across modules.
- plugins/: Directory for plugins (e.g., connection plugins), though modules typically live in a separate directory.

Modules must define an argspec (argument specification) and implement logic in the main() function.


Writing a Custom Module

1. Define the Module's Purpose

For example, create a module to interact with a hypothetical "custom_api" service.

# my_custom_module/main.py
from ansible.module_utils.basic import AnsibleModule

def main():
    module = AnsibleModule(
        argument_spec={
            "api_url": {"type": "str", "required": True},
            "resource_id": {"type": "str", "required": True},
            "state": {"type": "str", "choices": ["present", "absent"], "default": "present"},
        },
        supports_check_mode=True,
    )

    api_url = module.params["api_url"]
    resource_id = module.params["resource_id"]
    state = module.params["state"]

    # Example logic: Check if resource exists
    if state == "present":
        # Simulate API call
        result = {"changed": False, "msg": "Resource exists", "resource_id": resource_id}
    else:
        # Simulate deletion
        result = {"changed": True, "msg": "Resource deleted", "resource_id": resource_id}

    module.exit_json(**result)

if __name__ == "__main__":
    main()

2. Handle Edge Cases and Errors

Use module.fail_json() for errors and ensure all required parameters are validated.


Testing Custom Modules

Use ansible-test to validate your module:

  1. Install the module:

    ansible-galaxy install -p ./my_custom_module -r requirements.yml
    
    (Create a requirements.yml file with:
    ---
    collections:
      - src: ./my_custom_module
    

  2. Write a Test Playbook (test_playbook.yml):

    - name: Test custom module
      hosts: localhost
      tasks:
        - name: Ensure resource is present
          my_custom_module:
            api_url: "https://api.example.com"
            resource_id: "12345"
            state: present
          register: result
    
        - debug:
            var: result
    

  3. Run Tests:

    ansible-test units tests/test_playbook.yml
    


Best Practices

  • Follow Ansible's coding standards: Use AnsibleModule for input handling and consistent return values.
  • Leverage module_utils: Share utility functions across modules to avoid duplication.
  • Document parameters: Use description and required fields in argument_spec.
  • Support check mode: Implement logic to handle --check flags gracefully.

Key takeaways

  • Custom modules extend Ansible by implementing task-specific logic in Python.
  • Use AnsibleModule to handle parameters, errors, and return values.
  • Test modules with ansible-test and validate edge cases.
  • Follow best practices for maintainability and compatibility with Ansible's ecosystem.
  • Package modules for reuse via ansible-galaxy or version control.