Advanced Ansible: Custom modules ================================ Published : 12 April 2025 Reading : 4 min Tags : Ansible URL : https://ctrl-find.nl/posts/ansible_custom_modules/ Plain text : https://ctrl-find.nl/posts/ansible_custom_modules/index.txt ------------------------------------------------------------ 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; ```bash #!/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; ```bash --- - 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: ```bash ansible-playbook playbook.yml ``` The output of the playbook ```bash 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**! ------------------------------------------------------------ NAVIGATION [index] https://ctrl-find.nl/posts/index.txt [<< prev] Containers?! https://ctrl-find.nl/posts/containers/index.txt [next >>] Advanced Ansible: Collections+ https://ctrl-find.nl/posts/ansible_collections/index.txt ------------------------------------------------------------ CTRL-Find — Debugging all systems https://ctrl-find.nl/