Using include to nest loops is the only viable solution when you have several loops in the include task.
Meanwhile on most scenario including yours, you can reduce the work to a single loop. The basic idea: rather than nesting loops, you create a list with all elements to loop over. In this case, the product filter should do the job.
The other "trick" (since there are no real examples in the doc for filetree...) is to remember that a with_<some_lookup> loop can generally be transformed to loop: {{ lookup|query('some_lookup', args....) }}.
Putting it all together, here is a (not fully tested) example that should meet your requirement:
- name: Templating from filetree and instances
vars:
file: "{{ item.0 }}"
instance: "{{ item.1.value }}"
template:
src: "config-templates/{{ file.path }}"
dest: "{{ install_dir }}/{{ instance.name }}/"
loop: "{{ lookup('filetree', 'templates/config') | product(instances | dict2items) | list }}"
when: file.state == 'file'
Edit: not trying to push towards "MY !!!" solution :), but if you don't have too many tasks, here is a possible scenario to keep everything in the same file.
Edit2: You actually don't even need a task, especially if your play targets several hosts. Updated with a full example pseudo playbook.
- name: Deploy
hosts: all
connection: ssh
become: true
vars:
my_files: "{{ lookup('filetree', 'templates/config') | selectattr('state', 'eq', 'file') | list }}"
action1_list: ['some', 'list']
action2_list: ['other', 'list']
tasks:
- name: action 1
debug:
msg: action1
vars:
file: item.0
action1_item: item.1
loop: "{{ my_files | product(action1_list) | list }}"
- name: action 2
debug:
msg: action2
vars:
file: item.0
action2_item: item.1
loop: "{{ my_files | product(action2_list) | list }}"