title: Ansible: applying role templates hierarchically
body: <p>I'm planning to use Ansible to manage a fairly large set of Linux servers with a lot of variation among them.</p>
  
  <p>The hosts will be grouped into roles with a hierarchy between them, there's a <code>common</code> role, on which every other depends, child-roles which depend on on <code>common</code>, and there may be grandchild-roles which depend on them, with no limit on dependency depth.</p>
  
  <pre><code>common
     |___ role1
     |___ role2
     |___ role3
            |___ role3.1
  </code></pre>
  
  <p>Each role has its own subset of templates for system configuration files.</p>
  
  <pre><code>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/
  </code></pre>
  
  <p>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.</p>
  
  <p>I have (sort of) achieved this by putting the following into the <code>system-settings.yml</code> for each role:</p>
  
  <pre><code>- 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'
  </code></pre>
  
  <p>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.</p>
  
  <p>So, for the above example tree, when applying <code>role1</code> to a group of hosts Ansible:</p>
  
  <ol>
  <li>applies <code>/etc/hosts</code> and <code>/etc/resolv.conf</code> from the <code>common</code> role</li>
  <li>applies <code>/etc/hosts</code> and <code>/etc/httpd/conf/httpd.conf</code> from the <code>role1</code> role</li>
  </ol>
  
  <p>But I want it to do:</p>
  
  <ol>
  <li>apply <code>/etc/resolv.conf</code> from the <code>common</code> role</li>
  <li>apply <code>/etc/hosts</code> from the <code>role1</code> role (the most specific version)</li>
  <li>apply <code>/etc/httpd/conf/httpd.conf</code> from the <code>role1</code> role</li>
  </ol>
  
  <p>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.</p>
  
  <p>Is this achievable with Ansible?</p>
  
  <p>I can see a possible solution using external scripts (as described in <a href="https://docs.ansible.com/ansible/2.4/playbooks_loops.html#iterating-over-the-results-of-a-program-execution" rel="nofollow noreferrer">https://docs.ansible.com/ansible/2.4/playbooks_loops.html#iterating-over-the-results-of-a-program-execution</a>), but I'm not sure how to pass down the role hierarchy into the script.</p>