Search code examples
ansiblejinja2

Ansible use Find Module on Registered Variable


My goal is to create a registered variable that I can use to search through and register other variables based on it. Mostly it is a find module that is searching for files which then, based upon the filename, should be registered in other variables. Here is an example of what I have but it is not filtering out the variables properly:

- name: Register all files
  find:
    paths: ./files/
    patterns: '*.pkg,*.dmg'
    file_type: file
  register: installers
- name: Find Slack Installers
  find:
    paths: "{{ item }}"
    patterns: "*slack*"
  loop: "{{ installers.files|map(attribute='path')|map('basename')|list }}"
  register: slack_installers
- debug:
    msg: "{{ slack_installers }}"

This then outputs the following:

TASK [Find Slack Installers] *******************************************************************************************************************************
ok: [localhost] => (item=slack.19.5.dmg)
ok: [localhost] => (item=slack.19.6.pkg)
ok: [localhost] => (item=box.1.2.3.dmg)

(ignore the versions of things, these are just fake names/versions but I'm just testing the find functionality)

As you can see the "Find Slack Installers" task is just registering everything within the "installers" variable rather than actually finding only things with the slack pattern.

EDIT FOR extended question:

set_fact:
  "{{ item.item|basename|lower }}_installers": "{{ installers.files|map(attribute='path')|map('basename')|list|select('search', '{{ item.item|basename|lower }}') }}"
loop: "{{ selected_installers.results }}"
loop_control:
  label: "{{ item.item|basename }}"
when: item.user_input == 'y' and
      item.item == ( item.item|basename )

Solution

  • Using the find module twice in a row does not make sense.

    The find module is to find files in a folder and its subfolders, but not to filter a list (even if file names are in this list), as you tried here. Have a look at the examples for find in the Ansible docs.

    However, you have two possibilities.

    Using find with using the search word slack in pattern:

    Add the search word slack directly as pattern in the find module: *slack*.pkg,*slack*.dmg.

    - name: Register all files
      find:
        paths: ./files/
        patterns: '*slack*.pkg,*slack*.dmg'
        file_type: file
      register: installers
    - debug:
        msg: "{{ installers.files|map(attribute='path')|map('basename')|list }}"
    

    Result:

    TASK [Register all files] **************
    ok: [localhost]
    
    TASK [debug] ***************************
    ok: [localhost] => {
        "msg": [
            "slack.19.5.dmg",
            "slack.19.6.pkg"
        ]
    }
    

    Using find to search only for the packages, then filtering your list:

    Using select('regex', 'slack') you can filter the list by specific words in regex syntax, in your case specifying slack is enough.

    - name: Register all files
      find:
        paths: ./files/
        patterns: '*.pkg,*.dmg'
        file_type: file
      register: installers
    - debug:
        msg: "{{ installers.files|map(attribute='path')|map('basename')|list|select('regex', 'slack') }}"
    

    Result:

    TASK [Register all files] **************
    ok: [localhost]
    
    TASK [debug] ***************************
    ok: [localhost] => {
        "msg": [
            "slack.19.5.dmg",
            "slack.19.6.pkg"
        ]
    }
    

    EDIT: Extended Question

    Nested jinja expressions are not allowed and not required.

    If you want to specify a variable or a filter expression, just write it without using another jinja expression with {{ and }}.

    Try the following select:

    select('search', item.item|basename|lower )
    

    Tip

    If you use several operators and are not sure which operator binds stronger, you can also use parentheses like in mathematics.
    The parenthesis should not be necessary at this point, but it does not interfere.

    select('search', (item.item|basename|lower) )
    

    But if you want to get e.g. the second element (index=1) of your list, then the term looks like this:

    ( installers.files|map(attribute='path')|map('basename')|list )[1]
    

    At this point, the parentheses are important so that the inner expression is evaluated first, and then the corresponding element can be accessed.