Creating your own custom Ansible module allows you to extend Ansible’s functionality to meet specific automation needs that aren’t covered by the existing modules. Below is a comprehensive guide to creating your own Ansible module, including an example to help you get started.
1. Understanding Ansible Modules
Ansible modules are reusable, standalone scripts that Ansible executes on remote nodes to perform specific tasks. While Ansible provides numerous built-in modules, custom modules are essential when you need to perform unique tasks tailored to your environment.
2. Prerequisites
Before creating a custom Ansible module, ensure you have the following:
- Basic Knowledge of Ansible: Understanding of how playbooks, tasks, and modules work.
- Programming Skills: Proficiency in Python (the most common language for Ansible modules) is highly recommended.
- Ansible Development Environment: Ansible installed on your machine, along with a suitable text editor or IDE.
3. Steps to Create a Custom Ansible Module
Step 1: Define the Module’s Purpose
Before writing any code, clearly outline what your module will do. For this example, let’s create a simple module named greet
that prints a greeting message.
Step 2: Set Up the Directory Structure
Organize your custom module within your Ansible project. It’s common to place custom modules in a library
directory within your project.
your-ansible-project/
├── library/
│ └── greet.py
├── playbook.yml
└── inventory
Step 3: Write the Module Code
Here’s a step-by-step guide to writing the greet
module in Python.
a. Basic Module Structure
Create a file named greet.py
inside the library
directory and add the following boilerplate code:
#!/usr/bin/python
from ansible.module_utils.basic import AnsibleModule
def run_module():
# Define available arguments/parameters that a user can pass to the module
module_args = dict(
name=dict(type='str', required=True),
greeting=dict(type='str', required=False, default='Hello')
)
# Seed the result dictionary
result = dict(
changed=False,
original_message='',
message=''
)
# Create the AnsibleModule object
module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True
)
# If in check mode, exit without making changes
if module.check_mode:
module.exit_json(**result)
# Retrieve parameters
name = module.params['name']
greeting = module.params['greeting']
# Generate the greeting message
message = f"{greeting}, {name}!"
# Populate the result
result['original_message'] = f"{greeting}, {name}!"
result['message'] = message
# Since this module does not change anything, 'changed' remains False
module.exit_json(**result)
def main():
run_module()
if __name__ == '__main__':
main()
b. Explanation of the Code
- Shebang Line: Specifies the interpreter to execute the script.
- Imports: Imports the
AnsibleModule
class from Ansible’s module utilities. run_module
Function: Encapsulates the module logic.module_args
: Defines the arguments the module accepts (name
andgreeting
).result
: A dictionary to store the output and status.AnsibleModule
: Initializes the module with the defined arguments and supports check mode.- Check Mode Handling: If Ansible runs in check mode, the module exits without making changes.
- Parameter Retrieval: Gets the values of
name
andgreeting
from the user. - Message Generation: Creates the greeting message.
- Result Population: Sets the
original_message
andmessage
in the result. - Exit: Returns the result to Ansible.
main
Function: Entry point of the module.
Step 4: Make the Module Executable
Ensure your module has executable permissions:
chmod +x library/greet.py
Step 5: Create a Playbook to Use the Custom Module
Create a playbook named playbook.yml
to utilize the greet
module.
---
- name: Test Custom Greet Module
hosts: localhost
gather_facts: no
tasks:
- name: Send a greeting
greet:
name: "Alice"
greeting: "Hi"
register: greet_result
- name: Display the greeting message
debug:
msg: "{{ greet_result.message }}"
Step 6: Run the Playbook
Execute the playbook using the ansible-playbook
command.
ansible-playbook -i inventory playbook.yml
Expected Output:
PLAY [Test Custom Greet Module] ************************************************
TASK [Send a greeting] ********************************************************
ok: [localhost]
TASK [Display the greeting message] *******************************************
ok: [localhost] => {
"msg": "Hi, Alice!"
}
PLAY RECAP ********************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0
Step 7: Testing the Module
Testing ensures your module works as expected. You can run the playbook multiple times with different parameters or use Ansible’s --check
mode to verify behavior without applying changes.
ansible-playbook -i inventory playbook.yml --check
4. Enhancing Your Custom Module
Once you’re comfortable with creating basic modules, you can enhance them by:
- Adding More Parameters: Include additional arguments to handle more complex scenarios.
- Implementing State Management: Allow your module to handle different states (e.g., present/absent).
- Error Handling: Use
module.fail_json()
to handle and report errors gracefully. - Idempotency: Ensure running the module multiple times doesn’t produce unintended side effects.
Example: Enhanced Greet Module with State
Let’s modify the greet
module to handle different states, such as creating or deleting a greeting.
#!/usr/bin/python
from ansible.module_utils.basic import AnsibleModule
def run_module():
module_args = dict(
name=dict(type='str', required=True),
greeting=dict(type='str', required=False, default='Hello'),
state=dict(type='str', required=False, choices=['present', 'absent'], default='present')
)
result = dict(
changed=False,
original_message='',
message=''
)
module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True
)
if module.check_mode:
module.exit_json(**result)
name = module.params['name']
greeting = module.params['greeting']
state = module.params['state']
if state == 'present':
message = f"{greeting}, {name}!"
result['original_message'] = message
result['message'] = message
result['changed'] = True # Assuming this action represents a change
elif state == 'absent':
result['message'] = f"Goodbye, {name}!"
result['changed'] = True # Assuming this action represents a change
module.exit_json(**result)
def main():
run_module()
if __name__ == '__main__':
main()
Updated Playbook (playbook.yml
):
---
- name: Test Enhanced Greet Module
hosts: localhost
gather_facts: no
tasks:
- name: Create a greeting
greet:
name: "Bob"
greeting: "Hello"
state: "present"
register: greet_result_present
- name: Display the greeting message
debug:
msg: "{{ greet_result_present.message }}"
- name: Remove the greeting
greet:
name: "Bob"
state: "absent"
register: greet_result_absent
- name: Display the farewell message
debug:
msg: "{{ greet_result_absent.message }}"
Running the Updated Playbook:
ansible-playbook -i inventory playbook.yml
Expected Output:
PLAY [Test Enhanced Greet Module] ***********************************************
TASK [Create a greeting] *******************************************************
changed: [localhost]
TASK [Display the greeting message] ********************************************
ok: [localhost] => {
"msg": "Hello, Bob!"
}
TASK [Remove the greeting] *******************************************************
changed: [localhost]
TASK [Display the farewell message] ********************************************
ok: [localhost] => {
"msg": "Goodbye, Bob!"
}
PLAY RECAP *********************************************************************
localhost : ok=4 changed=2 unreachable=0 failed=0
5. Best Practices for Creating Custom Ansible Modules
- Use Python Best Practices:
- Follow PEP 8 style guidelines.
- Write clear, maintainable, and well-documented code.
- Provide Comprehensive Documentation:
- Include a
DOCUMENTATION
string at the top of your module with details about parameters, usage, and examples. - Provide
EXAMPLES
andRETURN
documentation sections.
- Include a
Example: Adding Documentation to greet.
py
#!/usr/bin/python
from ansible.module_utils.basic import AnsibleModule
DOCUMENTATION = '''
---
module: greet
short_description: Generates a greeting message.
description:
- This module creates a greeting message based on the provided name and greeting.
options:
name:
description:
- The name of the person to greet.
required: true
type: str
greeting:
description:
- The greeting phrase.
required: false
default: "Hello"
type: str
state:
description:
- The state of the greeting.
required: false
choices: ['present', 'absent']
default: 'present'
type: str
author:
- Your Name (@yourhandle)
'''
EXAMPLES = '''
- name: Send a greeting
greet:
name: "Alice"
greeting: "Hi"
- name: Remove the greeting
greet:
name: "Alice"
state: "absent"
'''
RETURN = '''
original_message:
description: The original greeting message.
type: str
returned: success
message:
description: The processed greeting message.
type: str
returned: success
'''
def run_module():
# Module implementation remains the same
...
if __name__ == '__main__':
main()
- Ensure Idempotency:
- Design modules so that running them multiple times produces the same result without unintended side effects.
- Handle Errors Gracefully:
- Use
module.fail_json()
to return meaningful error messages when something goes wrong.
- Use
- Include Unit Tests:
- Implement tests to verify your module’s functionality and handle edge cases.
- Leverage Ansible’s Module Utilities:
- Utilize Ansible’s helper functions and utilities for common tasks, such as argument validation.
- Follow Naming Conventions:
- Use clear and descriptive names for your modules to indicate their functionality.
6. Publishing Your Custom Module
If you believe your module could benefit others, consider publishing it:
- Ansible Galaxy: Share your module on Ansible Galaxy to make it accessible to the community.
- Version Control: Host your module on platforms like GitHub for version control and collaboration.
7. Additional Resources
- Ansible Module Development Guide: Official Documentation
- Ansible Module Utilities: Module Utilities
- Writing Reusable Ansible Content: Ansible Reusable Content
By following this guide, you can create robust and reusable custom Ansible modules tailored to your specific automation needs. Happy automating!
Empower Your Automation Journey: Build Your Own Ansible Modules Today
Leave a Reply