Search code examples
ansibleyamljinja2

Define variable as list of contents of multiple files


I am trying to declare a host variable in an Ansible YAML defaults file of a role, that should contain the content of multiple files as lists entries.

Consider the files dir/1.txt with the content one and dir/2.txt with the content two, so the variable should equal to [ "one", "two" ].

To achieve that I am trying to combine the fileglob and file lookups, but I can't get it working.


My first idea was to just take the output of the fileglob lookup and take it as input for the file lookup like this, but that throws an error:

myvar: "{{ lookup('file', lookup('fileglob', 'dir/*.txt')) }}"

fatal: [localhost]: FAILED! => {"msg": "An unhandled exception occurred while templating '{{ lookup('file', lookup('fileglob', 'dir/*.txt')) }}'. Error was a <class 'ansible.errors.AnsibleError'>, original message: An unhandled exception occurred while running the lookup plugin 'file'. Error was a <class 'ansible.errors.AnsibleError'>, original message: could not locate file in lookup: /path/to/dir/1.txt,/path/to/dir/2.txt. could not locate file in lookup: /path/to/dir/1.txt,/path/to/dir/2.txt"}


The fileglob lookup does not seem to return an actual list of files, but a comma delimited string, as the output of this test playbook demonstrates:

- hosts: localhost
  vars:
    files: "{{ lookup('fileglob', 'dir/*.txt') }}"
  tasks:
    - ansible.builtin.debug:
        var: files
ok: [localhost] => {
    "files": "/path/to/dir/1.txt,/path/to/dir/2.txt"
}

The documentation of the file lookup suggests that the module can take a list of files as input, but as separate terms. So if I only run the lookup on the two example files, it returns a comma delimited string of contents:

- hosts: localhost
  vars:
    contents: "{{ lookup('file', 'dir/1.txt', 'dir/2.txt') }}"
  tasks:
    - ansible.builtin.debug:
        var: contents

ok: [localhost] => {
    "contents": "one,two"
}

Now I don't know how to convert the output of the fileglob lookup to multiple input terms for the file lookup. Also I am worried what happens if a file contains a , since I can't simply run split(",") on the output of the file lookup to receive a list, then.

Is it possible to achieve what I'm trying within the variables definition or do I have to resort to ansible logic and set_facts with_fileglob?


Solution

  • What you are asking is basically the difference between lookup and query, as explained in the documentation:

    The difference between lookup and query is largely that query will always return a list. The default behavior of lookup is to return a string of comma separated values. lookup can be explicitly configured to return a list using wantlist=True.

    Source: Forcing lookups to return lists: query and wantlist=True


    So, here, you want to use a query instead of a lookup:

    - debug:
        var: query('fileglob', 'dir/*.txt')
    

    Giving:

    ok: [localhost] => 
      query('fileglob', 'dir/*.txt'):
      - /path/to/dir/1.txt
      - /path/to/dir/2.txt
    

    As for passing the list of files from the fileglob lookup to the file one, you can unpack the list returned by fileglob with a star *, like in Python.

    So, your task ends up being

    - debug:
        var: "query('file', *query('fileglob', 'dir/*.txt'))"
    

    Which would yield a list of the content of your files, as expected:

    ok: [localhost] => 
      query('file', *query('fileglob', 'dir/*.txt')):
      - one
      - two