Advanced Ansible Part 1: Custom modules

The power of creating your own modules.

Sebastiaan avatar
  • Sebastiaan
  • 3 min read

This is my first time trying to create a Ansible custom module. I dont have much python knowledge (the very bare minimum), but I want to expand my knowledge of python and Ansible by creating a custom module.

What do we need?

  • Installed:
    • Ansible
    • Python3
    • virtual environment for python3 packages
  • A folder to place our files

Starting with the module

Activate the virtual environment and create a folder called library. Ansible knows that if there is a custom module its in the library folder. In this library folder we can create our python file, I’ve called mine pokemon_api.py

I’ve added the contents of pokemon_api.py file below;

#!/usr/bin/python3
# This is a must otherwise the module wont work.
from ansible.module_utils.basic import *

# Necessary imports for the module to use.
import requests
import json

def main():

    # The fields I want to use and the type of input.
    fields = {
        "name": {"required": True, "type": "str"}
    }

    # Making the fields available to use.
    module = AnsibleModule(argument_spec=fields)

    # Getting the parameter(s) from the playbook into the module
    name = module.params['name']


    # Default python code to use for the module
    URL = f"https://pokeapi.co/api/v2/pokemon/{name}" 
    REQ = requests.get(URL)
    OUT = REQ.text
    y = json.loads(OUT)

    def get_stats():
        return y["stats"]

    x = get_stats()
    print(x)

    # End of the module, this doesn't change the file system so it is on false
    # And Meta gives the items back to the terminal in our playbook.
    module.exit_json(changed=False, meta={"name": name, "response": x}) 
    

if __name__ == '__main__':
    main()

The playbook

Create a playbook file in the same folder where the folder library is. I’ve called it for simple reason playbook.yml the contents of the file are below;

---
- hosts: localhost        # Testing it on local machine
  tasks:
    - name: Local Module testing  # Can choose the name you want
      pokemon_api:        # This is the name of the file in library without the .py part
        name: "gengar"    # The name field is passed through the module in name = module.params['name']
      register: result    # this registered the output

    - debug:
        msg: "{{result}}" # This shows the registerd output

Running the playbook

To run the playbook we do the following:

ansible-playbook playbook.yml

The output of the playbook

sebas@pop-os:~/Desktop/custom_module$ ansible-playbook playbook.yml
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [localhost] *******************************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************
ok: [localhost]

TASK [Local Module testing] ********************************************************************************************************************************
ok: [localhost]

TASK [debug] ***********************************************************************************************************************************************
ok: [localhost] => {
    "msg": {
        "changed": false,
        "failed": false,
        "meta": {
            "name": "gengar",
            "response": [
                {
                    "base_stat": 60,
                    "effort": 0,
                    "stat": {
                        "name": "hp",
                        "url": "https://pokeapi.co/api/v2/stat/1/"
                    }
                },
                {
                    "base_stat": 65,
                    "effort": 0,
                    "stat": {
                        "name": "attack",
                        "url": "https://pokeapi.co/api/v2/stat/2/"
                    }
                },
                {
                    "base_stat": 60,
                    "effort": 0,
                    "stat": {
                        "name": "defense",
                        "url": "https://pokeapi.co/api/v2/stat/3/"
                    }
                },
                {
                    "base_stat": 130,
                    "effort": 3,
                    "stat": {
                        "name": "special-attack",
                        "url": "https://pokeapi.co/api/v2/stat/4/"
                    }
                },
                {
                    "base_stat": 75,
                    "effort": 0,
                    "stat": {
                        "name": "special-defense",
                        "url": "https://pokeapi.co/api/v2/stat/5/"
                    }
                },
                {
                    "base_stat": 110,
                    "effort": 0,
                    "stat": {
                        "name": "speed",
                        "url": "https://pokeapi.co/api/v2/stat/6/"
                    }
                }
            ]
        }
    }
}

PLAY RECAP *************************************************************************************************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

Conclusion

After some tinkering it is fun to create a custom module for something that isn’t available. With this kind of knowledge, Ansible can be used for many more things then just the “default” collections and modules.

Stay tuned for the Advanced Ansible part 2: Our own collection!

Sebastiaan

Written by : Sebastiaan

Sysadmin/Platform/Devops Engineer

Recommended for You

What is a checksum and how do I use it

What is a checksum and how do I use it

Verifying item an from internet?

Part 6: Testing Ansible code

Part 6: Testing Ansible code

Making sure our code runs!