Introduction
Ansible, an open-source automation tool, offers a wide range of built-in modules and plugins to simplify infrastructure management. However, sometimes, you may need to extend its functionality by creating custom plugins tailored to your specific needs. In this article, we’ll explore the creation of a custom Ansible lookup plugin in Python.
What is a Lookup Plugin?
Ansible lookup plugins are used to retrieve data dynamically during playbook execution. They allow you to fetch information from various sources, such as databases, APIs, or external files, and use that data in your Ansible tasks.
Links
- https://docs.ansible.com/ansible/latest/dev_guide/developing_plugins.html#lookup-plugins
- https://docs.ansible.com/ansible/latest/plugins/lookup.html#lookup-plugins
- https://docs.ansible.com/ansible/latest/reference_appendices/config.html#default-lookup-plugin-path
Step by Step
The following steps explain how to create a custom Ansible lookup plugin in Python. It begins by introducing lookup plugins in Ansible, which are used to fetch data dynamically during playbook execution. The provided Python script is an example of a plugin designed to retrieve an API token from a specific URL. The following steps break down the script into its components: Python headers, documentation, imports, initialization, and the custom lookup module with its ‘run’ method.
Setting the Stage
Let’s consider the example of retrieving a token via an API request with a POST specifying email and password. This is a common behavior. In the following example, we are using the following endpoint https://reqres.in/api/login
with the credentials: “email”: “[email protected]”
and “password”: “cityslicka”
. A successful connection to the API returns the token QpwL5tke4Pnpja7X4
.
You can find the full code at the end of the article. Let’s take a closer look at the Python script provided at the beginning of this article. This script is an example of a custom Ansible token.py
lookup plugin designed to fetch an API token from a specific URL. Here’s a breakdown of its components:
1. Python 3 Headers
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
These lines specify Python 3 headers required for compatibility when submitting this plugin to Ansible.
2. Documentation
DOCUMENTATION = r"""
name: test
author: Luca Berton <[email protected]>
version_added: "0.1" # same as collection version
short_description: read API token
description:
- This lookup returns the token from the provided API.
"""
This block provides metadata about the plugin, including its name, author, version, and a short description. It’s essential for documentation and readability.
3. Imports
from ansible.errors import AnsibleError, AnsibleParserError
from ansible.plugins.lookup import LookupBase
from ansible.utils.display import Display
import requests
The script imports necessary modules and classes from Ansible and external libraries. These imports enable the plugin to handle errors, access Ansible utilities, and make HTTP requests using the requests library.
4. Initialization
display = Display()
The Display class is used for displaying messages and debugging information within Ansible. An instance of this class is created for logging purposes.
5. Custom Lookup Module
class LookupModule(LookupBase):
Here, we define our custom lookup module, named LookupModule, which inherits from LookupBase, a base class for creating lookup plugins in Ansible.
6. Custom Method (run)
def run(self, terms, variables=None, **kwargs):
# Plugin logic goes here
The run method is the heart of the lookup plugin. It defines how the plugin operates when invoked in an Ansible playbook. In this example, it makes an HTTP POST request to a specified URL, extracts an API token from the response, and handles errors gracefully.
Please note that by design, Ansible Lookup plugins are historically usually used to pipe results into loops. Ansible expects a list as a return type. A common walkaround is to wrap our return value into a list, like:
return [ret]
The full code of the token.py
Ansible lookup plugin looks like the:
# python 3 headers, required if submitting to Ansible
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
name: test
author: Luca Berton <[email protected]>
version_added: "0.1" # same as collection version
short_description: read API token
description:
- This lookup returns the token from the provided API.
"""
from ansible.errors import AnsibleError, AnsibleParserError
from ansible.plugins.lookup import LookupBase
from ansible.utils.display import Display
import requests
display = Display()
class LookupModule(LookupBase):
_URL_ = "https://reqres.in/api/login"
def run(self, terms, variables=None, **kwargs):
payload = {
"email": "[email protected]",
"password": "cityslicka"
}
try:
res = requests.post(self._URL_, data=payload)
res.raise_for_status()
ret = res.json()['token']
except requests.exceptions.HTTPError as e:
raise AnsibleError('There was an error getting a token. The lookup API returned %s', response.status_code)
except Exception as e:
raise AnsibleError('Unhandled exception is lookup plugin. Origin: %s', e)
return [ret]
Enabling lookup plugins
Ansible automatically enables all available lookup plugins. To activate a custom lookup plugin, you can do so by placing it in one of the following locations:
Adjacent to Your Playbook: You can include your custom lookup plugin in a directory called
lookup_plugins
located next to your playbook file.Inside a Collection: If you’ve installed an Ansible collection, you can place your custom lookup plugin in the
plugins/lookup/
directory within that collection.Within a Standalone Role: Custom lookup plugins can also be included within a standalone Ansible role. Place the plugin in the appropriate
lookup_plugins
directory within the role.Configured Directory Sources: If you have configured custom directory sources in your
ansible.cfg
configuration file, you can place the lookup plugin in one of those directories. The setting key islookup_plugins
under the[defaults]
section orANSIBLE_LOOKUP_PLUGINS
environmental variable
By following these methods, you can activate and use custom lookup plugins in Ansible to extend its functionality according to your specific automation requirements.
Using the Custom Lookup Plugin
Now that we’ve dissected the token.py
script let’s discuss using this custom lookup plugin in an Ansible playbook. Here are the key steps:
Place the Plugin File: Save the Python script containing your custom lookup plugin in a directory recognized by Ansible (e.g., ./lookup_plugins/ within your Ansible project directory). Include the Lookup in a Task:
- name: Fetch API Token
ansible.builtin.debug:
msg: "{{ lookup('token') }}"
In your playbook, use the lookup function to invoke your custom plugin. In this example, we call the plugin named ’token’ and display the returned token using the Ansible debug module.
Ansible Playbook
The full Ansible Playbook code is the following:
- exec.yml
---
- name: Exec lookup plugin
hosts: all
tasks:
- name: Retrieve token from plugin
ansible.builtin.debug:
msg: "{{ lookup('token') }}"
Execution
ansible-playbook -i inventory exec.yml
PLAY [Exec lookup plugin] *******************************************************************************
TASK [Gathering Facts] **********************************************************************************
ok: [demo.example.com]
TASK [Retrieve token from plugin] ***********************************************************************
ok: [demo.example.com] => {
"msg": "QpwL5tke4Pnpja7X4"
}
PLAY RECAP **********************************************************************************************
demo.example.com : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
As expected, we obtained the QpwL5tke4Pnpja7X4
token on the screen due to the API request.
Conclusion
Creating custom lookup plugins in Ansible allows you to extend its capabilities and interact with various data sources dynamically. By understanding the structure of a lookup plugin and the provided Python script, you can craft your own custom plugins to streamline your automation workflows and meet your specific requirements. Whether it’s fetching API tokens or retrieving data from other sources, custom lookup plugins empower you to harness the full potential of Ansible for your infrastructure automation needs.
Subscribe to the YouTube channel, Medium, and Website, X (formerly Twitter) to not miss the next episode of the Ansible Pilot.Academy
Learn the Ansible automation technology with some real-life examples in my Udemy 300+ Lessons Video Course.
My book Ansible By Examples: 200+ Automation Examples For Linux and Windows System Administrator and DevOps
Donate
Want to keep this project going? Please donate