Why does the ansible find module work differently in a role when on localhost?

Viewed 44

I have a role, called newrole. It sits in my ansible directory:

ansible
  - roles
     - newrole
       - tasks
         main.yml
       - templates
         template.j2
   playbook1.yml

My playbook is defined quite simply:

---
- hosts: host1
  roles:
   - newrole

And the main.yml in tasks for the newrole:

- name: Find templates in the role
  find:
    path: "templates"
    pattern: "*.j2"
  register: result

- debug:
    msg: "{{ result }}"

I then invoke the playbook from the ansible directory: ansible-playbook playbook1.yml

This works as expected: it runs on host1 and matches the template.j2 file. However, if I change the playbook ever so slightly to run on localhost instead of host1 (and specify connection: local), things go off the rail. It says it can't find the path templates. If I instead put in the path from the ansible directory (that I am running from), e.g. roles/newrole/templates, then it matches the template.j2 file. However, I can't figure out why that difference is occurring.

Aren't roles supposed to be providing a insulation from the directory structure I am running them from?
Even more confusing is that even on localhost, the template module (ansible.builtin.template) will search for templates only in the templates directory of the newrole (as I would originally expect). Can someone explain this behavior difference?

Is there a way to get the find module in my example to start searching at the root of the role regardless of localhost or not invocation?

1 Answers

When Ansible runs on nodes, it does not ships the whole playbook, nor the whole role on the said node, it packages a Python script that get send to the node and executed there.

So, if you do not send your files over to a node, a task executed on a node is not going to find the said file.

Now, for what you do need, you don't need a find task, you need the fileglob lookup, as lookups do tend to execute on the controller, rather than on the nodes.

Here is an example of usage:

- debug:
    var: lookup('fileglob', 'templates/*.j2')

If we go beyond your localised issue, then, you can also use a delegation to the controller, in order to achieve the same.

In your case, that would translate in:

- find:
    path: "templates"
    pattern: "*.j2"
  delegate_to: localhost
  run_once: true
  register: result

Given the playbook:

- hosts: node1
  gather_facts: no

  tasks:
    - debug:
        var: lookup('fileglob', 'templates/*.j2')

    - find:
        path: "templates"
        pattern: "*.j2"
      delegate_to: localhost
      run_once: true
      register: result

    - debug:
        var: result.files.0.path

This yields:

PLAY [node1] ******************************************************************

TASK [debug] ******************************************************************
ok: [node1] => 
  lookup('fileglob', 'templates/*.j2'): /absolute/path/to/templates/template.j2

TASK [find] *******************************************************************
ok: [node1 -> localhost]

TASK [debug] ******************************************************************
ok: [node1] => 
  result.files.0.path: templates/template.j2
Related