Skip to content

O-X-L/ansible-executor

Repository files navigation

Ansible Runner minimal

Support Badge (Donate, Support-Licenses)


Check Docs Lint Unit-Tests Integration-Tests

DISCLAIMER: This is an unofficial community project! Do not confuse it with the vanilla Ansible/Ansible-Runner product!

WARNING: This project is still in early development. DO NOT use it in production!


Scope

The scope of this project is it to create a simple and transparent Python3-interface that can be used to execute Ansible-playbooks.

It will focus on using docker or podman to execute ansible in an isolated environment. But local execution will also be available.

The implementation will be opinionated and will have a 'narrow' interface.

Motivation

I was not 100% happy with the official ansible-runner library.

It provides a lot more functionality than we actually need (only executing ansible-playbooks) and thus has a lot more complexity added-on. This makes it also hard to troubleshoot.

As I needed an alternative I wanted to provide it to the community to play with. (:

I will try to create a transparent documentation and a lot of unit- & integration-tests!

Feel free to give feedback as GitHub issues or email.


Roadmap

  • Ansible Execution
    • Config Object & Validation incl. inline-docs
    • Engines
      • Local Executor
      • Container Executor
        • Docker
        • Podman
    • Functionality
      • Playbook targeting local machine
      • Playbook targeting remote Linux server (SSH-Key, Connect-Pass, Become-Pass, Vault-Pass)
        • Pass secrets via one-time-read Pipes/FIFO (in-memory - does not write to disk)
      • Stopping job
      • Redirect output (stdout/stderr) to log-files
      • Playbook Status (per category, per host)
        • Track stati at runtime
  • Tests
    • Unit-Tests for all components (>85% coverage)
      • Execution Config-Validation
      • Execution
        • Before
          • Base-Executor
          • Local Executor-Engine
          • Containerized Executor-Engine
        • After
        • Execution-Status
    • Integration-Tests for many practical use-cases
      • Simple execution targeting localhost
      • Passing extra-vars
      • Passing env-vars
      • Enabling output-colors
      • User stopping execution
      • Execution reached timeout
      • Passing secrets (as value & as file)
        • SSH-Key (via ssh-agent)
        • Connect-Pass
        • Become-Pass
        • Ansible-Vault
      • SSH
        • SSH-Key usage
        • Known-Hosts file
      • tbc...
    • Integration-Tests also for containerized executor

Install

pip install oxl-ansible-executor

See: pypi.org/oxl-ansible-executor


Usage

from oxl_ansible_executor import Execution, ExecutionConfig

c = ExecutionConfig(
  playbook_dir='/home/demo/ansible/',
  playbook_file='test.yml',
  inventory_files='inv/env1/hosts.yml',
)
e = Execution(c)

e.run(blocking=True)
# [INFO] Creating log-files
# [INFO] Using executor: local
# [INFO] Creating secret-pipes
# [INFO] Using log files: /home/demo/.local/share/oxl-ansible-executor/ansible_stdout_1775500760_lhWOT.log & /home/demo/.local/share/oxl-ansible-executor/ansible_stderr_1775500760_lhWOT.log
# [INFO] Executing ansible-playbook
# [INFO] Command: ['ssh-agent', 'sh', '-c', 'ssh-add /tmp/ar_znjp4bih/.fIWqOdKaPzGioFfDljSw && /home/demo/.venv/bin/ansible-playbook -i inv/abc/hosts.yml -C -D -l srv1 --key-file /tmp/ar_znjp4bih/.fIWqOdKaPzGioFfDljSw --become-pass-file /tmp/ar_znjp4bih/.SHVTpOYOH93aaZBTAgVJ --vault-pass-file /tmp/ar_znjp4bih/.GwkiXZ7YrpEvGY8BnyF2 syslog.yml']

print(e.status)
# {
#   "finished": true,
#   "playbook_finished": true,
#   "failed": false,
#   "canceled": false,
#   "time_start": 1775500758,
#   "time_finish": 1775500783,
#   "timed_out": false,
#   "time_duration_sec": 25,
#   "log_stdout_file": "/home/demo/.local/share/oxl-ansible-executor/ansible_stdout_1775500760_lhWOT.log",
#   "log_stderr_file": "/home/demo/.local/share/oxl-ansible-executor/ansible_stderr_1775500760_lhWOT.log",
#   "ansible_command": [
#     "ansible-playbook",
#     "-i",
#     "inv/abc/hosts.yml",
#     "-C",
#     "-D",
#     "-l",
#     "srv1",
#     "--key-file",
#     "/tmp/ar_znjp4bih/.fIWqOdKaPzGioFfDljSw",
#     "--become-pass-file",
#     "/tmp/ar_znjp4bih/.SHVTpOYOH93aaZBTAgVJ",
#     "--vault-pass-file",
#     "/tmp/ar_znjp4bih/.GwkiXZ7YrpEvGY8BnyF2",
#     "syslog.yml"
#   ],
#   "process_command": [
#     "ssh-agent",
#     "sh",
#     "-c",
#     "ssh-add /tmp/ar_znjp4bih/.fIWqOdKaPzGioFfDljSw && /home/demo/.venv/bin/ansible-playbook -i inv/abc/hosts.yml -C -D -l srv1 --key-file /tmp/ar_znjp4bih/.fIWqOdKaPzGioFfDljSw --become-pass-file /tmp/ar_znjp4bih/.SHVTpOYOH93aaZBTAgVJ --vault-pass-file /tmp/ar_znjp4bih/.GwkiXZ7YrpEvGY8BnyF2 syslog.yml"
#   ],
#   "process_rc": 0,
#   "process_result": {
#     "failed": false,
#     "rc": 0,
#     "pid": null,
#     "stdout": "<OMITTED FOR DEMO>",
#     "stderr": "<OMITTED FOR DEMO>",
#     "stdout_lines": [
#       "PLAY [all] *********************************************************************",
#       "",
#       "TASK [Install rsyslog & logrotate] *********************************************",
#       "ok: [srv1]",
#       "",
#       "TASK [Add certificates] ********************************************************",
#       "ok: [srv1] => (item={'c': '-----BEGIN CERTIFICATE-----\\nMIIDdjCCAv2gAwIBAgIUUkucdMI33le6pBxGgw6WRP2yPlcwCgYIKoZIzj0EAwIw\\ngZExCzAJBgNVBAYTAkFUMQ8wDQYDVQQIDAZTdHlyaWExDTALBgNVBAcMBEdyYXox\\nGDAWBgNVBAoMD09YTCBJVCBTZXJ2aWNlczESMBAGA1UECwwJTG9nc2VydmVyMRUw\\nEwYDVQQDDAxMb2dzZXJ2ZXIgQ0ExHTAbBgkqhkiG9w0BCQEWDmNvbnRhY3RAb3hs\\nLmF0MB4XDTI0MTIyNDEzMjExOFoXDTQ0MTIxOTEzMjExOFowgZExCzAJBgNVBAYT\\nAkFUMQ8wDQYDVQQIDAZTdHlyaWExDTALBgNVBAcMBEdyYXoxGDAWBgNVBAoMD09Y\\nTCBJVCBTZXJ2aWNlczESMBAGA1UECwwJTG9nc2VydmVyMRUwEwYDVQQDDAxMb2dz\\nZXJ2ZXIgQ0ExHTAbBgkqhkiG9w0BCQEWDmNvbnRhY3RAb3hsLmF0MHYwEAYHKoZI\\nzj0CAQYFK4EEACIDYgAEUwaiRMy1OcN0j5odvoTir6LFAqAlUlDp708Y39ZdFduv\\nXMEJGKWeHUKnoMT4uaDEomlwqEUa5JcG3Z9R0PeCJsNXvns53uK1j1uMY395yIvW\\nyQ8yu1vDhD/OnnOMl6oro4IBEjCCAQ4wDAYDVR0TBAUwAwEB/zAdBgNVHQ4EFgQU\\nvk1BL8cxa/eNRSQDD/T+yTyA7sswgdEGA1UdIwSByTCBxoAUvk1BL8cxa/eNRSQD\\nD/T+yTyA7suhgZekgZQwgZExCzAJBgNVBAYTAkFUMQ8wDQYDVQQIDAZTdHlyaWEx\\nDTALBgNVBAcMBEdyYXoxGDAWBgNVBAoMD09YTCBJVCBTZXJ2aWNlczESMBAGA1UE\\nCwwJTG9nc2VydmVyMRUwEwYDVQQDDAxMb2dzZXJ2ZXIgQ0ExHTAbBgkqhkiG9w0B\\nCQEWDmNvbnRhY3RAb3hsLmF0ghRSS5x0wjfeV7qkHEaDDpZE/bI+VzALBgNVHQ8E\\nBAMCAQYwCgYIKoZIzj0EAwIDZwAwZAIwHbFj7SEsIg3dUBpAkKIfLWVrpKicxDUe\\nKqUd/hcXHX/TTKKd0yehyXhZR0HBYgHEAjAbxJb8pQIVzCowHcXmv790wOiSsaFL\\nQJuH8qil1t0JSAV73EdK9zM+IDdvBVdDlBw=\\n-----END CERTIFICATE-----\\n', 'f': 'log_ca.crt'})",
#       "ok: [srv1] => (item={'c': '-----BEGIN CERTIFICATE-----\\nMIIDgTCCAwagAwIBAgIQX7p1IkBq4b97O8k0caiMfjAKBggqhkjOPQQDAjCBkTEL\\nMAkGA1UEBhMCQVQxDzANBgNVBAgMBlN0eXJpYTENMAsGA1UEBwwER3JhejEYMBYG\\nA1UECgwPT1hMIElUIFNlcnZpY2VzMRIwEAYDVQQLDAlMb2dzZXJ2ZXIxFTATBgNV\\nBAMMDExvZ3NlcnZlciBDQTEdMBsGCSqGSIb3DQEJARYOY29udGFjdEBveGwuYXQw\\nHhcNMjQxMjI0MTQyNzA4WhcNMjkxMjIzMTQyNzA4WjCBjDELMAkGA1UEBhMCQVQx\\nDzANBgNVBAgMBlN0eXJpYTENMAsGA1UEBwwER3JhejEYMBYGA1UECgwPT1hMIElU\\nIFNlcnZpY2VzMRIwEAYDVQQLDAlMb2dzZXJ2ZXIxEDAOBgNVBAMMB2dlbmVyaWMx\\nHTAbBgkqhkiG9w0BCQEWDmNvbnRhY3RAb3hsLmF0MHYwEAYHKoZIzj0CAQYFK4EE\\nACIDYgAEF2uLX4WUd/4byt3aS59XxohILfEdnNvqdg87nG0PXC85yhtB5yKMWryT\\n+cWZeGYSucdLtZ4UxDqvosQ32eql/K+VoVRqkb4UF1Mmgq5+smhHIkVxFjJCUtsg\\ni+G9aPM/o4IBJDCCASAwCQYDVR0TBAIwADAdBgNVHQ4EFgQUmULR2FK41wLFEl/B\\nSGhYkQ0Z5v8wgdEGA1UdIwSByTCBxoAUvk1BL8cxa/eNRSQDD/T+yTyA7suhgZek\\ngZQwgZExCzAJBgNVBAYTAkFUMQ8wDQYDVQQIDAZTdHlyaWExDTALBgNVBAcMBEdy\\nYXoxGDAWBgNVBAoMD09YTCBJVCBTZXJ2aWNlczESMBAGA1UECwwJTG9nc2VydmVy\\nMRUwEwYDVQQDDAxMb2dzZXJ2ZXIgQ0ExHTAbBgkqhkiG9w0BCQEWDmNvbnRhY3RA\\nb3hsLmF0ghRSS5x0wjfeV7qkHEaDDpZE/bI+VzALBgNVHQ8EBAMCBaAwEwYDVR0l\\nBAwwCgYIKwYBBQUHAwIwCgYIKoZIzj0EAwIDaQAwZgIxAKVAFBs4EaCRx/0VEZKA\\n3/n06CaEE5I05v8kN5XBWgbJPxaDi+bcRJrzBMsn/JRjvwIxAMi4cekyGCWZYDtM\\n+8WRliIMba5dbFuR/UGAf/bIfmNWM2sSqtzseSZ/RoPdjaChlQ==\\n-----END CERTIFICATE-----\\n', 'f': 'log_client.crt'})",
#       "",
#       "TASK [Add certificate key] *****************************************************",
#       "ok: [srv1]",
#       "",
#       "TASK [Add graylog forwarding] **************************************************",
#       "ok: [srv1]",
#       "",
#       "TASK [Software log files] ******************************************************",
#       "ok: [srv1]",
#       "",
#       "TASK [Software log rotation] ***************************************************",
#       "ok: [srv1]",
#       "",
#       "TASK [Restart services] ********************************************************",
#       "changed: [srv1] => (item=rsyslog.service)",
#       "changed: [srv1] => (item=logrotate.service)",
#       "",
#       "PLAY RECAP *********************************************************************",
#       "srv1                       : ok=7    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0"
#     ],
#     "stderr_lines": [
#       "Identity added: /tmp/ar_znjp4bih/.fIWqOdKaPzGioFfDljSw (Demo)"
#     ]
#   }
# }

# to stop a running execution
e = Execution(c)
e.run(blocking=False)
# you could 'tail -f' the log files
e.stop()  # executor sends signals to subprocess running ansible

Use-Cases

See: Our simple Ansible WebUI


Contribute

We are happy to see contributions. (:

  • Report issues
  • Create feature-requests
  • Provide PR's for:
    • more Unit-Tests
    • more Integration-Tests
    • fixing bugs/errors
    • enhancing the input-validation
    • ...

Security Considerations

  • Secrets are passed to Ansible via one-time-readable FIFO/Pipes
  • SSH-agent is used to pass SSH-keys to Ansible
  • Files are created with an explicit 0600 file-mode
  • By default, the temporary runtime-directory is removed after the execution has finished (only log-files remain - see example above)
  • tbc

AI-Usage Info

The coding of this project involved minimal AI-usage.

About

A simple & transparent Python3-interface for executing ansible-playbooks

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Sponsor this project

Contributors

Languages