I have a bunch of directories containing similar template files in an ansible role.
Something like:
/ansible/
└── roles
└── webserver
├── tasks
│ └── main.yml
└── templates
└── sites
├── bar
│ ├── index.html
│ ├── script.js
│ ├── style.css
│ └── webapp.conf
├── baz
│ ├── index.html
│ ├── script.js
│ ├── style.css
│ └── webapp.conf
└── foo
├── index.html
├── script.js
├── style.css
└── webapp.conf
In the task, I want to list the directories (bar
, baz
, foo
, plus whatever new ones I create there) and apply template actions to their files.
I've tried using with_fileglob
but it can't list directories (only files, it ignores directories).
- name: Template index.html
template:
src: "sites/{{item}}/index.html"
dest: "/var/www/{{item}}/index.html"
with_fileglob: "templates/sites/*"
There is also a regression such that it can't even find the files with a glob on the directory.
- name: Template index.html
template:
src: "sites/{{item}}"
dest: "/var/www/{{item}}/index.html"
with_fileglob: "templates/sites/*/index.html"
I've tried find
, but I can't get it to work on the local templates directory (it seems to find things on the host.)
I've tried filetree
- name: Template index.html
template:
src: "sites/{{item.path}}"
dest: "/var/www/{{item.path}}/index.html"
with_filetree: "templates/sites/"
when: item.state == 'directory'
but it:
when
to limit it to directories, it returns sub-directories)Is there a way to get a list of all my sites in my local template directory?
If you want to use filetree
and make it less verbose and up to your need, you can use it as a lookup, and not as a with_*
loop.
This way you can pre-filter it with the help of the selectattr
and rejectattr
filters:
For example:
- debug:
msg: "{{ item.path }}"
loop: >-
{{
lookup('community.general.filetree', 'templates/sites')
| selectattr('state', '==', 'directory')
| rejectattr('path', 'contains', '/')
}}
loop_control:
label: "{{ item.path }}"
In this task:
selectattr('state', '==', 'directory')
will ensure you are only listing directoriesrejectattr('path', 'contains', '/')
will reject path containing a /
, so effectively excluding subdirectoriesloop_control
and its parameter label: "{{ item.path }}"
will make the loop less verbose on complex dictionariesOr your can use JMESPath, as your partially used it indeed, but the filtering can be part of the query:
- debug:
msg: "{{ item }}"
loop: >-
{{
lookup('community.general.filetree', 'templates/sites')
| json_query('[?state == `directory` && !contains(path, `/`)].path')
}}
Given the file strucure:
.
└── templates
└── sites
├── bar
│ ├── baz
│ └── index.html
└── foo
└── index.html
Where baz
is a subdirectory, the two tasks above would both yield:
ok: [localhost] => (item=bar) =>
msg: bar
ok: [localhost] => (item=foo) =>
msg: foo