Skip to main content

Command Palette

Search for a command to run...

Ansible Custom Modules

Quick guide on writing Custom Modules in Ansible

Updated
•5 min read•View as Markdown
Ansible Custom Modules
V

Hello reader, this is Vish currently working as a DevOps Engineer for the past 3+ years and a total of 9+ years of experience in the IT industry. Am passionate about learning DevOps and in the mission of enhancing my learning into container orchestration tools, monitoring and security.

In this article, I have covered how to write/understand custom modules that can be used in our Ansible Playbooks for performing certain tasks. This blog can be a kick-start for you to understand and help write your custom modules.


What is Custom Module in Ansible

Ansible is a powerful Configuration Management tool that has many built-in modules to configure target systems but there would be cases where we would need to perform tasks that are not provided by Ansible. So we have the flexibility of writing our custom modules and using them. These modules can be written in any language of our choice and I have preferred writing here with the help of Python.

Folder Structure

Here is the link to the project which I have pushed to GitHub - https://github.com/devopsvish/ansible-custom-module

Let me go step by step so you can understand the concept with ease. I have also dockerized this project so you can quickly try it out on your machine.

I have followed the below project structure and let's go thru it real quick

inventory.ini - contains the lists of our target hosts. In my case am targeting my local host.

vish_module.py - this is a custom module that I have written to walkthru the blog which outputs us a small employee information

employee_info.yml - the playbook which uses the custom module to give us the result

vishtest_playbook.yml - a test playbook to check if ansible is working on our local machine

ansible.cfg - this is the ansible configuration file where am mentioning where the custom module resides


Custom Module Explanation

Usually, Custom modules are placed inside the library folder and the same path is configured in the ansible.cfg file to pick the modules from the right location during our playbook execution.

Here is the full code of the custom module and I will have it explained line by line.

from ansible.module_utils.basic import AnsibleModule

def main():
    parameters = {
        'name': {"required": True, "type": 'str'},
        'designation': {"required": True, "type": 'str'},
        'skills': {"required": True, "type": 'str'},
        'location': {"required": False, "type": 'str'}
    }

    module = AnsibleModule(argument_spec=parameters)

    name = module.params['name']
    designation = module.params['designation']
    skills = module.params['skills']
    location = module.params['location']

    output = {
        'name': name,
        'designation': designation,
        'skills': skills,
        'location': location,
        'message': f"{name} is a {designation} with {skills} from {location}",
        'location_type': f"{type(location)}"
    }

    module.exit_json(changed=False, **output)

if __name__ == '__main__':
    main()

1. Import the module

from ansible.module_utils.basic import AnsibleModule

This line imports the AnsibleModule class from the ansible.module_utils.basic module which can be used to interact with Ansible.

2. Main function

def main():
   # logic of the code goes here

The logic of the custom module goes here which can be used on performing tasks on target systems

3. Argument Specification

parameters = {
   'name': {"required": True, "type": 'str'},
   'designation': {"required": True, "type": 'str'},
   'skills': {"required": True, "type": 'str'},
   'location': {"required": False, "type": 'str'}
}

The parameters dictionary defines the expected arguments for the module. Each argument is specified with its name, required status and datatype.

  • Let me take designation argument for example

    • Name of the argument: designation

    • Required status of the argument: True (accepts a boolean)

    • The datatype of the argument: str (accepts str, bool, int, list, dict, float)

4. Instantiate AnsibleModule

module = AnsibleModule(argument_spec=parameters)

The AnsibleModule class is instantiated with the argument specification defined earlier and this allows the module to receive and validate the arguments passed by Ansible

5. Retrieve the values of the arguments

name = module.params['name']
designation = module.params['designation']
skills = module.params['skills']
location = module.params['location']

The values of the arguments are retrieved with the help of params attribute of the module object

6. Output dictionary

output = {
    'name': name,
    'designation': designation,
    'skills': skills,
    'location': location,
    'message': f"{name} is a {designation} with {skills} from {location}",
}

Here an output dictionary is created with the desired key-value pair and the values are obtained from the arguments passed to the module. This is the dictionary am going to output during our playbook execution

7. Exit the Module

module.exit_json(changed=False, **output)
  • The module execution is completed by calling the exit_json method

  • The changed parameter is set to false as the module does not make any change to the target system. If it changes anything on the target system we can set this to True and the same will be reflected in the Ansible playbook result execution

  • The result of the module is provided as a keyword argument using the **output syntax

Playbook Execution

- name: Execute vish module for testing
  hosts: vish-localhost
  become: true
  gather_facts: true
  tasks:
    - name: Get the Employee info
      vish_module:
        name: "Vish"
        designation: "DevOps Engineer"
        skills: "Linux, Docker, etc"
        location: "Bangalore"
      register: output

    - name: Show the employee info output
      debug:
        var: output
  • Inside the playbook of the first task, the module written can be called just by its name. Here the name of the module is vish_module which I have mentioned in Get the Employee info task.

  • Inside the module, I have passed the arguments as required by the module where the required status is mentioned in the module

  • Am using the register keyword to store the output

  • In the second task, am using the debug module to output values for us to verify.

Results

so by writing custom modules the similar way we can make changes to the target system as per our requirements.

Thats it!! It's very easy, right? Try writing your own modules and start testing it. Happy Learning.

Please let me know in the comment section about any corrections/mistakes, am open to improvements and suggestions.

Please do follow me for more such content related to DevOps world

Cheers, Vish. Happy Learning!!!