DEV Community

Cover image for Write a Ansible module with Python
Austin Cunningham
Austin Cunningham

Posted on • Updated on

Write a Ansible module with Python

Ansible has a number of modules available here. But I wished to write my own. Ansible has integration with Python to enable this. I will start with a hello world module and work on from there.

Hello World Module

Create a playbook to call your new module with hello_world.yml, note we call the module with hello_world: the module will return the result and debug will display result.

- hosts: localhost

  tasks:
  - name: Test that my hello_world module works
    hello_world: 
    register: result

  - debug: var=result  
Enter fullscreen mode Exit fullscreen mode

Create library directory with a python file in the root of your project

hello_world.yml
[library]
   |_ hello_world.py
Enter fullscreen mode Exit fullscreen mode

Add the following content to the hello_world.py

#!/usr/bin/python

from ansible.module_utils.basic import *

def main():
    module = AnsibleModule(argument_spec={})
    theReturnValue = {"hello": "world"}
    module.exit_json(changed=False, meta=theReturnValue)

if __name__ == '__main__':
    main()
Enter fullscreen mode Exit fullscreen mode

You need #!/usr/bin/python

You need to import the ansible.module_utils.basic to use AnsibleModule

You need a main()

AnsibleModule is used to pass parameters in and out of the module it this case we are only passing back meta with the value of theReturnValue.

Note: The argument_spec is an empty object to show nothing is being passed into the AnsibleModule.

That’s it the hello_world playbook can be run with

$ ansible-playbook hello_world.yml
Enter fullscreen mode Exit fullscreen mode

This will return a hello world when run as the meta data

That is it your hello world module finished.

Pass variables in and out of a Python Module

I was working on an upgrade script and I needed to bump the Semantic Versioning i.e. bumping the minor version of 1.1.1, Ansible handles this poorly as it is not a float or a int. So writing a small module to handle this makes sense. To do this I need to pass a variable to the python module and then bump the version and pass a value back.

Again create a file version_change.yml in the root directory and a library directory with your python file called version_change.py

- hosts: localhost

  tasks:
  - name: Test that my change_version module works
    version_change: 
      version_name: "Before"
      version_no:  1.1.1 
      unchanged_value: "This will pass through"
    register: result

  - debug: var=result
Enter fullscreen mode Exit fullscreen mode

So as an example I am passing three variable to the module called version_change (version_name, version_no and unchanged_value) in the version_changed.py file

#!/usr/bin/python

from ansible.module_utils.basic import *

def main():

    fields = {
        "version_no": {"default": True, "type": "str"},
        "version_name": {"default": True, "type": "str"},
        "unchanged_value": {"default": True, "type": "str"}
    }

    module = AnsibleModule(argument_spec=fields)
    # change the name
    module.params.update({"version_name": "After"})
    # bump minor and patch version
    mylist = module.params["version_no"].split('.')
    mylist[2] = str(int(mylist[2]) + 2)
    mylist[1] = str(int(mylist[1]) + 1)
    mystr= '.'.join(mylist)
    module.params.update({"version_no": mystr})


    module.exit_json(changed=True, meta=module.params)

Enter fullscreen mode Exit fullscreen mode

We handle the incoming variables with the fields dictionary we can use “default” or “required” Boolean and a “type” in this case we use “str”

All variables need to be declared in the fields dict, weather you use them or not

The fields are passed in as argument_spec to the AnsibleModule

We can then access the module.params using python dictionary methods

We then pass the module.params back as meta to the playbook

We can then run the playbook with

$ ansible-playbook version_change.yml
Enter fullscreen mode Exit fullscreen mode

And you get the result , as you can see the unchanged_value remains unchanged and , the version_name and version_no are changed

Not needed but as a final step you can add string for documentation and examples at the top of the module. See the following Git repo for the code for this basic demo.

Myblog

Top comments (0)