For my ansible playbooks, I wanted to be able to add several new templates to be copied to target system, and additionally be able to perform some commands for them without having to specify each individual file/template to copy.

My approach:

Define for the hosts I want to find templates/playbooks define a var named extras for the relevant hosts:

extras:
  - ntp
  - certificates

The names defined (in above example ntp and certificates) are just name of folders laying inside tasks/templates/${folder} that are searched and included or excluded based on extras values.

---
- name: Find candidate templates
  find:
    paths:
      - "{{playbook_dir}}/tasks/templates/"
    recurse: yes
    patterns: "*.jinja"
  register: templates
  delegate_to: localhost
  when: extras is defined

- name: Copy templates from folder into path
  template:
    mode: "{{ item[1].mode }}"
    src: "{{ item[1].path }}"
    dest:
      "{{ item[1].path | replace(playbook_dir,'') | replace('/tasks/templates','') | replace('.jinja','') |replace('/' +  item[0] + '/','/')|replace('//','/')\
      \     }}"
  with_nested:
    - "{{ extras }}"
    - "{{ templates.files }}"
  loop_control:
    label:
      "{{ item[1].path | replace(playbook_dir,'') | replace('/tasks/templates','') | replace('.jinja','') |replace('/' +  item[0] + '/','/')|replace('//','/')\
      \     }}"
  when: extras is defined and templates != False and item[0] in item[1].path
  notify:
    - Restart systemd

And we’ll do something similar for the playbooks inside those folders

- name: Find candidate playbooks
  find:
    paths:
      - "{{playbook_dir}}/tasks/templates/"
    recurse: yes
    patterns: "*.yaml"
  register: playbooks
  delegate_to: localhost
  when: extras is defined

# Create empty array that we'll be filling
- name: Filter candidate playbooks
  set_fact:
    playbook: []
  when: extras is defined and playbooks != False

# Build an array of all the playbooks we're going to use
- name: Filter candidate playbooks
  set_fact:
    playbook: "{{ playbook + [item[1].path] }}"
  with_nested:
    - "{{ extras }}"
    - "{{ playbooks.files }}"
  loop_control:
    label: "{{ item[1].path    | replace(playbook_dir,'') | replace('/tasks/templates','') }}"
  when: extras is defined and playbooks != False and item[0] in item[1].path

- name: Load tasks from playbook
  include_tasks: "{{ item }}"
  loop: "{{ playbook }}"
  when: extras is defined and playbook != False

With this approach, putting files in a tree structure like:

./tasks
./tasks/templates
./tasks/templates/ntp
./tasks/templates/ntp/etc
./tasks/templates/ntp/etc/ntp.conf.jinja
./tasks/templates/ntp/tasks.yaml

./tasks/templates/certificates
./tasks/templates/certificates/etc
./tasks/templates/certificates/etc/pki/
./tasks/templates/certificates/etc/pki/ca-trust/source/
./tasks/templates/certificates/etc/pki/ca-trust/source/anchors/
./tasks/templates/certificates/etc/pki/ca-trust/source/anchors/mycert.jinja
./tasks/templates/certificates/tasks.yaml

This will make first part of the task to copy the templates (ending in .jinja) to the target location, but removing the .jinja suffix.

For the second part, it will include the relevant .yaml files, and load the tasks defined within and execute as part of the playbook.

Enjoy! (and if you do, you can Buy Me a Coffee )