- Python 100%
| PyAnsible | ||
| .gitignore | ||
| pyproject.toml | ||
| README.md | ||
PyAnsible
A library for integrating Ansible roles into Python
Author: August Knisley
Date: 15 December, 2025
Introduction
Architecture
PyAnsible is built around
Ansible Runner,
specifically the ansible_runner.run method. Which allows for running Ansible
roles, playbooks, commands, etc. PyAnsible abstracts these features into a class
based layout, which simplifies usages and importantly allows Ansible roles to be
packaged as Python Wheels and imported as any other library.
The PyAnsible package is comprised of three components:
-
The
Inventoryclass: models the Ansible inventory and storesHostobjects, and manages global variables. It is used exclusively as the singleton object accessible asPyAnsible.inventory. -
The
Hostclass: models a host in Ansible inventory. Stores hostname and IP address as attributes, and manages hostvars available to the host. The class can be used as-is or as a base class to extend functionality for more specific host types -
The
RoleWrapclass: A base-class that when extended wraps an single Ansible role allowing it to be used as a callable object within Python.
Basic Usage
Wrapping a Role
class MyWrappedRole(RoleWrap):
'''
A basic example of the use of the RoleWrap class
'''
def __init__(self):
super().__init__('MyWrappedRole')
def __call__(self, host: Host, var1, var2):
'''
Runs the wrapped `MyWrappedRole` Ansible role
'''
## Setup role inputs
self.role_vars['ca_fqdn'] = var1
self.role_vars['ca_fingerprint'] = var2
## Run role with Ansible on given host
super().run(host.hostname)
## Return selected variables to caller
## NOTE: To be able to collect a variable, `cacheable: yes` must be
## added to the set_fact task
return (
self.output[host.hostname]['my_output_var1'],
self.output[host.hostname]['my_output_var2'],
)
Running a Role
import PyAnsible
from .MyWrappedRole import MyWrappedRole
## Create host
host1 = PyAnsible.Host('host1.example.com', '1.1.1.1')
## Add host to PyAnsible inventory
PyAnsible.inventory.add_host(host1)
## Create a role object
my_wrapped_role = MyWrappedRole()
## Run the role on host1, with inputs and collect outputs
foo, bar = my_wrapped_role(host1, 'baz', 'quux')
Packaging a Wrapped Role
There are currently two supported ways to package a wrapped Ansible role.
The first is where all the Ansible role folders are inside a roles directory, and the wrapper classes are within the the normal Python structure.
MyProject/
├── __init__.py
├── MyWrappedRole1.py
├── MyWrappedRole2.py
└── roles/
├── MyWrappedRole1/
│ ├── tasks/
│ │ └── main.yml
│ └── files/
│ └── foo.conf
└── MyWrappedRole2/
└── tasks/
└── main.yml
This would only require adding the following to the pyproject.toml file:
[Rest of PyProject.toml file]
...
[tool.setuptools.package-data]
"MyProject.roles" = ["**"]
Or each role is a submodule of a parent class with something like the following directory structure:
MyProject/
├── __init__.py
├── MyWrappedRole1/
│ ├── __init__.py
│ ├── tasks/
│ │ └── main.yml
│ └── files/
│ └── foo.conf
└── MyWrappedRole2/
├── __init__.py
└── tasks/
└── main.yml
[Rest of PyProject.toml file]
...
[tool.setuptools.package-data]
"MyProject.MyWrappedRole1" = ["**"]
"MyProject.MyWrappedRole2" = ["**"]