I'm planning to use Ansible to manage a fairly large set of Linux servers with a lot of variation among them.
The hosts will be grouped into roles with a hierarchy between them, there's a common
role, on which every other depends, child-roles which depend on on common
, and there may be grandchild-roles which depend on them, with no limit on dependency depth.
common
|___ role1
|___ role2
|___ role3
|___ role3.1
Each role has its own subset of templates for system configuration files.
roles/
common/
templates/
etc/
hosts
resolv.conf
tasks/
main.yml
system-settings.yml # imported in main.yml
role1/
templates/
etc/
hosts # role-specific version, overrides 'common'
httpd/
conf/
httpd.conf
tasks/
main.yml
system-settings.yml # imported in main.yml
role2/
role3/
role3.1/
The tricky part is that I'd like to apply these templates hierarchically, similarly to the way Ansible resolves variable scopes. So, for a given child-role, the most specific version of a template file is applied, if a template does not exist in that role then apply the parent's version, searching up to the root level.
I have (sort of) achieved this by putting the following into the system-settings.yml
for each role:
- name: Create directories
file:
path: /{{ item.path }}
state: directory
mode: '{{ item.mode }}'
with_filetree: templates/
when: item.state == 'directory'
- name: Template files
template:
src: '{{ item.src }}'
dest: /{{ item.path }}
mode: '{{ item.mode }}'
with_filetree: templates/
when: item.state == 'file'
- name: Recreate symlinks
file:
src: '{{ item.src }}'
dest: /{{ item.path }}
state: link
force: yes
mode: '{{ item.mode }}'
with_filetree: templates/
when: item.state == 'link'
It works, but unfortunately it applies all of the parent role's templates, then all of the child role's templates. This is a waste of work and dangerous in case a playbook execution aborts for some reason, since the hosts may be left with generic versions of the system files.
So, for the above example tree, when applying role1
to a group of hosts Ansible:
- applies
/etc/hosts
and/etc/resolv.conf
from thecommon
role - applies
/etc/hosts
and/etc/httpd/conf/httpd.conf
from therole1
role
But I want it to do:
- apply
/etc/resolv.conf
from thecommon
role - apply
/etc/hosts
from therole1
role (the most specific version) - apply
/etc/httpd/conf/httpd.conf
from therole1
role
The template files should not be specifically listed in the tasks/playbooks due to management overhead, when adding a new file, or a role-specific version to override a generic one we don't want to have to change any playbooks or other configurations.
Is this achievable with Ansible?
I can see a possible solution using external scripts (as described in https://docs.ansible.com/ansible/2.4/playbooks_loops.html#iterating-over-the-results-of-a-program-execution), but I'm not sure how to pass down the role hierarchy into the script.