ansible-content-capture

Ansible Content Caputre is a tool for scanning Ansible contents and generating parsed object data. Topics Resources

APACHE-2.0 License

Stars
4
Committers
3

Note: This repository is in prototype phase and under active development with subject to breaking changes.

Ansible Content Capture

Ansible Content Capture is a scanning framework for Ansible contents and it generates graph/intermediate/high-level representation of Ansible data.

It supports scanning for

  • Playbook YAML
  • Task YAML in a role
  • Role
  • Collection (including modules)
  • Any type of Ansible project (e.g. Git repository)

and generates Python objects for each of the scanned contents. These objects are JSON serializable so that other projects/users can dump/load/process them depending on their requirements.

Today, the following projects utilize Ansible Content Capture for the scanning backend.

  • Ansible Risk Insight - A risk evaluation tool for Ansible contents
  • Sage - A framework to process Ansible contents for the scanning results

Object models

Ansible Content Capture scans Ansible contents and generates representation objects and abstract syntax tree (AST) which consists of Ansible-specific nodes like

  • collections (all external dependencies)
  • modules (name, fqcn, spec)
  • playbooks (name, comment, filename)
  • plays (name, comment, calling tasks and roles)
  • projects (including playbooks, roles, dependencies, metadata)
  • roles (including taskfiles, role metadata, variables, default, etc)
  • taskfiles (calling tasks)
  • tasks (task spec, callling module)

Each node is represented by unique identifier, and links between the nodes are created by analayzing Ansible-specific code semantics. For example,

  • call hierarlchy from playbook to role --> taskfile - task --> module
  • variable assignment is inferred in a static-analytics fashion

Installation

Run the following command after git clone.

$ pip install -e .

Getting started

from ansible_content_capture.scanner import AnsibleScanner

scanner = AnsibleScanner()
scanner.run(target_dir="<PATH/TO/PROJECT>")

# Alternatively, you can run the scanning for a YAML string
# scanner.run(raw_yaml="<YAML_STRING>")

An example implementation scan.py is scanning the target directory/file and printing the loaded AST like tree command like the following.

You can refer to it as a reference of a scanning code and how to use the result objects.

# Single Playbook scanning
$ python examples/scan.py examples/single_playbook/playbook.yml

root
└──{"type": "playbook", "filepath": "examples/single_playbook/playbook.yml"}
     └──{"type": "play", "filepath": "examples/single_playbook/playbook.yml"}
          ├──{"type": "task", "filepath": "examples/single_playbook/playbook.yml", "lines": "4 - 8", "name": "Ensure apache is at the latest version", "module": "yum"}
              └──{"type": "module", "fqcn": "ansible.builtin.yum"}
          └──{"type": "task", "filepath": "examples/single_playbook/playbook.yml", "lines": "9 - 13", "name": "Ensure apache is running", "module": "service"}
               └──{"type": "module", "fqcn": "ansible.builtin.service"}
# Project scanning
$ python examples/scan.py examples/project

root
└──{"type": "role", "filepath": "roles/common", "default_variables": {"foo": "bar"}}
     └──{"type": "taskfile", "filepath": "roles/common/tasks/main.yml"}
          ├──{"type": "task", "filepath": "roles/common/tasks/main.yml", "lines": "2 - 5", "name": "Install the correct web server for RHEL", "module": "ansible.builtin.import_tasks"}
              └──{"type": "taskfile", "filepath": "roles/common/tasks/redhat.yml"}
                   └──{"type": "task", "filepath": "roles/common/tasks/redhat.yml", "lines": "2 - 6", "name": "Install web server", "module": "ansible.builtin.yum"}
                        └──{"type": "module", "fqcn": "ansible.builtin.yum"}
          ├──{"type": "task", "filepath": "roles/common/tasks/main.yml", "lines": "6 - 9", "name": "Install the correct web server for Debian", "module": "ansible.builtin.import_tasks"}
              └──{"type": "taskfile", "filepath": "roles/common/tasks/debian.yml"}
                   └──{"type": "task", "filepath": "roles/common/tasks/debian.yml", "lines": "2 - 6", "name": "Install web server", "module": "ansible.builtin.apt"}
                        └──{"type": "module", "fqcn": "ansible.builtin.apt"}
          └──{"type": "task", "filepath": "roles/common/tasks/main.yml", "lines": "10 - 13", "name": "Print a variable", "module": "debug"}
               └──{"type": "module", "fqcn": "ansible.builtin.debug"}