Advanced Ansible Part 1: Custom modules
The power of creating your own modules.
- 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!