Ansible Core
Ansible Core is the foundational software package that provides the core language, runtime, and built-in plugins for IT automation. It enables configuring systems, deploying software, and orchestrating advanced IT tasks. As of the current version 2.20.4, ansible-core is actively maintained with minor releases occurring approximately every four weeks, and new major versions released roughly twice a year.
Warnings
- breaking The `ansible-core 2.19` (and by extension `Ansible 12`) release introduced significant templating changes which may break existing playbooks and roles that relied on previously silently ignored incorrect behavior. You must validate content for compatibility.
- deprecated The `INJECT_FACTS_AS_VARS` feature was deprecated in `ansible-core 2.20`. Relying on injected facts as variables without explicit definition will eventually lead to breakage.
- breaking Support for Python 3.8 on control nodes was removed in `ansible-core 2.20`. Environments running older Python versions will need to be upgraded.
- gotcha The Python API of `ansible-core` is primarily intended for internal use and is not officially supported for external applications. Backward compatibility is not guaranteed, and changes may occur at any time.
- breaking Behavior of `copy`, `file`, and `template` modules regarding file permissions (`mode` vs. `umask`) changed in `Ansible 2.10` and was backported to `2.8.14` and `2.9.12`. This could lead to unexpected file permissions.
- gotcha The `ansible-core` Python API is not thread-safe due to its reliance on forking processes.
Install
-
pip install ansible-core -
pipx install ansible-core
Imports
- AnsibleModule
from ansible.module_utils.basic import AnsibleModule
- TaskQueueManager
from ansible.executor.task_queue_manager import TaskQueueManager
- Play
from ansible.playbook.play import Play
- CallbackBase
from ansible.plugins.callback import CallbackBase
Quickstart
import json
import shutil
import os
import ansible.constants as C
from ansible.executor.task_queue_manager import TaskQueueManager
from ansible.inventory.manager import InventoryManager
from ansible.parsing.dataloader import DataLoader
from ansible.playbook.play import Play
from ansible.plugins.callback import CallbackBase
from ansible.vars.manager import VariableManager
from ansible import context
# NOTE: The Ansible Python API is for internal use and not officially supported for external applications.
# For robust programmatic execution of playbooks and modules, consider using 'ansible-runner'.
# --- Minimal example to run a simple ad-hoc command ---
# Configure Ansible context (optional, but good practice)
context.CLIARGS = ['ansible', 'all', '-m', 'ping', '-i', 'localhost,'] # Mimics command line args
class ResultsCollectorJSONCallback(CallbackBase):
def __init__(self):
self.host_ok = {}
self.host_failed = {}
self.host_unreachable = {}
def v2_runner_on_ok(self, result, **kwargs):
self.host_ok[result._host.get_name()] = result._result
def v2_runner_on_failed(self, result, **kwargs):
self.host_failed[result._host.get_name()] = result._result
def v2_runner_on_unreachable(self, result, **kwargs):
self.host_unreachable[result._host.get_name()] = result._result
# initialize needed objects
loader = DataLoader()
inv_data = 'localhost ansible_connection=local'
inventory = InventoryManager(loader=loader, sources=[inv_data])
variable_manager = VariableManager(loader=loader, inventory=inventory)
# instantiate our callback plugin
results_callback = ResultsCollectorJSONCallback()
# create play with tasks
play_source = dict(
name="Ansible Ad-hoc Ping",
hosts='all',
gather_facts='no',
tasks=[
dict(action=dict(module='ping'), register='ping_result')
]
)
play = Play().load(play_source, variable_manager=variable_manager, loader=loader)
# run it
t = TaskQueueManager(
inventory=inventory,
variable_manager=variable_manager,
loader=loader,
options=context.CLIARGS,
passwords={},
stdout_callback=results_callback,
)
try:
status = t.run(play)
finally:
# cleanup after ourselves
if t is not None:
t.cleanup()
if loader: # DataLoader may delete tmpdir on exit, ensure it's removed if exists
if hasattr(loader, '_tempdir') and os.path.exists(loader._tempdir):
shutil.rmtree(loader._tempdir)
print("\n--- Ad-hoc Ping Results ---")
print(f"OK: {json.dumps(results_callback.host_ok, indent=4)}")
print(f"Failed: {json.dumps(results_callback.host_failed, indent=4)}")
print(f"Unreachable: {json.dumps(results_callback.host_unreachable, indent=4)}")