'A method for listing Ansible modules used by playbooks

I'm creating a requirements.yml in an Ansible project, and I want to identify all of the modules that need to be installed from ansible-galaxy that are used by project playbooks. ansible-doc --list --playbook-dir foo seems like the right tool for the job, but it lists all locally available modules, not just the ones which are actually used in the foo directory. ansible-galaxy list doesn't account for any which are needed but not installed.

Is there a way to do this where I don't end up writing a shell script to sed|awk|grep the info I want?


The best approach I've been able to come up with so far is to ansible-playbook --syntax-check each of the playbooks. This will throw errors such as

ERROR! the role 'foo' was not found ...
ERROR! couldn't resolve module/action 'bar'. This often indicates a misspelling, missing collection, or incorrect module path.

but this is not ideal because it exits as soon as any error occurs. I have to fix each one and run the syntax check again.



Solution 1:[1]

FWIW, as a concept, the playbook below lists the modules used in a role

- hosts: localhost

  vars:
    keywords:
      - always
      - become
      - block
      - loop
      - loop_control
      - name
      - notify
      - register
      - tags
      - vars
      - when

  tasks:

    - name: The variable my_role_path is mandatory
      assert:
        that: my_role_path|d('')|length > 0

    - name: Find tasks files
      find:
        path: "{{ my_role_path }}/tasks"
        patterns: '*.yml,*.yaml'
        recurse: true
      register: result

    - name: Create list of tasks
      set_fact:
        lft: "{{ lft|d([]) + lookup('file', item)|from_yaml }}"
      loop: "{{ result.files|map(attribute='path')|list }}"

    - name: Get list of keys
      set_fact:
        lfk: "{{ lfk|d([]) + item.keys()|list }}"
      loop: "{{ lft }}"

    - name: Get list of keys from block/rescue/always
      set_fact:
        lfk: "{{ lfk|d([]) + item.keys()|list }}"
      loop: "{{ lft|json_query('[].[block, rescue, always]')|flatten }}"

    - name: Display list of modules
      debug:
        var: lfk|unique|sort|difference(keywords)

For example, analyze the role systemd

shell> ansible-playbook pb.yml -e my_role_path=roles/ansible-role-systemd

...

  lfk|unique|sort|difference(keywords):
  - command
  - file
  - include_tasks
  - meta
  - systemd
  - template

Complete the list of the keywords.


Use the tasks below to analyze a playbook

  tasks:

    - name: The variable my_playbook_path is mandatory
      assert:
        that: my_playbook_path|d('')|length > 0

    - name: Create list of tasks
      set_fact:
        lft: "{{ _playbook|map(attribute='tasks')|flatten }}"
      vars:
        _playbook: "{{ lookup('file', my_playbook_path)|from_yaml }}"

    - name: Get list of keys
      set_fact:
        lfk: "{{ lfk|d([]) + item.keys()|list }}"
      loop: "{{ lft }}"

    - name: Get list of keys from block/rescue/always
      set_fact:
        lfk: "{{ lfk|d([]) + item.keys()|list }}"
      loop: "{{ lft|json_query('[].[block, rescue, always]')|flatten }}"

    - name: Display list of modules
      debug:
        var: lfk|unique|sort|difference(keywords)

For example, analyzing the first playbook gives

  lfk|unique|sort|difference(keywords):
  - assert
  - debug
  - find
  - set_fact

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1