Problem with stat module and multiple files in the register

Hello

I’m struggling with file check when there’s more than one file in the stat module.

For example, this works:

- name: Check if conf file exist.
  ansible.builtin.stat:
    path: /etc/file.conf
  register: confcheck

- name: Create the file.conf file.
  ansible.builtin.lineinfile:
    create: true
    group: root
    line: root
    mode: '0600'
    owner: root
    path: /etc/file.conf
    state: present
  when: not confcheck.stat.exists

When I would add a list of files, I’m no longer able to use when: because I don’t understand how it works.
Using debug to display the register result, I see the structure, but whatever I put in the when: block, I get this item is not in the list error every time. Ansible docs didn’t help.

What I’m trying to do here is to copy the file to /etc/app.d/ folder if it exists in /etc, if it doesn’t exist in /etc/ and /etc/app.d/, create the file in /etc/app.d/ and I should be able to determine the exists value for each file from ther registered variable.

- name: Check if conf files exist.
  ansible.builtin.stat:
    path: "{{ item }}"
  loop:
    - /etc/app1.conf
    - /etc/app.d/app1.conf
    - /etc/app2.conf
    - /etc/app.d/app2.conf
  register: confcheck

- name: Copy app1.conf to app.d directory.
  ansible.builtin.copy:
    dest: /etc/app.d/app1.conf
    group: root
    mode: '0600'
    owner: root
    remote_src: true
    src: "/etc/app1.conf"
  loop:
    - "{{ confcheck }}"
  when: item.stat.exists # This should be: exists /etc/app1.conf and not exists /etc/app.d/app1.conf. This would fail as there is no stat item, although there is when I check with the debug module.

- name: Create the file.conf file.
  ansible.builtin.lineinfile:
    create: true
    group: root
    line: root
    mode: '0600'
    owner: root
    path: /etc/file.conf
    state: present
  loop:
    - "{{ confcheck }}"
  when: not items.stat.exists # This should be: not exists /etc/app1.conf and not exists /etc/app.d/app1.conf.

I need someone to tell me how to iterate registered list vars.
Right now, I have separate registers per file, and I don’t like it.

This is nicely explained in the documentation:

Update: sorry I didn’t read you question carefully. You already mentioned docs were of no help.

I think you are missing only one thing. You should loop over confcheck['results'] like this:

- name: Copy app1.conf to app.d directory.
  ansible.builtin.copy:
    dest: /etc/app.d/app1.conf
    group: root
    mode: '0600'
    owner: root
    remote_src: true
    src: "/etc/app1.conf"
  loop:
    - "{{ confcheck['results'] }}"
  when: item.stat.exists

It’s a little bit hard to understand what are you trying to accomplish, but in the previous code either dest or src or both should also be variables dependent on {{ item['item'] }}. Possibly something like this:

- name: Copy app1.conf to app.d directory.
  ansible.builtin.copy:
    dest: "/etc/app.d/{{ item['item'] }}"
    group: root
    mode: '0600'
    owner: root
    remote_src: true
    src: "/etc/{{ item['item'] }}"
  loop:
    - "{{ confcheck['results'] }}"
  when: item.stat.exists

And another note, {{ item['item'] }} is the notation I preferably used but {{ item.item }} can also be used and is the equivalent.

1 Like

Doesn’t work.
Complains about the list object not having an attribute ‘stat’ in when.
Tried with a single condition, but the error is the same.

The error was: error while evaluating conditional (item.stat.exists and item.item is not match(\"app.d\")): 'list object' has no attribute 'stat'.
- name: Check for conffiles
  ansible.builtin.stat:
    path: "{{ item }}"
  loop:
    - /etc/app1.conf
    - /etc/app.d/app1.conf
    - /etc/app2.conf
    - /etc/app.d/app1.conf
  register: confcheck
  tags: filecheck

- name: Copy app.conf files to app.d directory.
  ansible.builtin.copy:
    dest: "/etc/app.d/"
    group: root
    mode: '0600'
    owner: root
    remote_src: true
    src: "{{ item['item'] }}"
  loop:
    - "{{ confcheck['results'] }}"
  tags: filecheck
  when: item.stat.exists and item.item is not match("app.d")

Here’s the JSON of the stat output from the debug module:

{
    "confcheck": {
        "changed": false,
        "msg": "All items completed",
        "results": [
            {
                "ansible_loop_var": "item",
                "changed": false,
                "failed": false,
                "invocation": {
                    "module_args": {
                        "checksum_algorithm": "sha1",
                        "follow": false,
                        "get_attributes": true,
                        "get_checksum": true,
                        "get_md5": false,
                        "get_mime": true,
                        "path": "/etc/app1.conf"
                    }
                },
                "item": "/etc/app1.conf",
                "stat": {
                    "exists": false
                }
            },
            {
                "ansible_loop_var": "item",
                "changed": false,
                "failed": false,
                "invocation": {
                    "module_args": {
                        "checksum_algorithm": "sha1",
                        "follow": false,
                        "get_attributes": true,
                        "get_checksum": true,
                        "get_md5": false,
                        "get_mime": true,
                        "path": "/etc/app.d/app1.conf"
                    }
                },
                "item": "/etc/app.d/app1.conf",
                "stat": {
                    "exists": false
                }
            },
            {
                "ansible_loop_var": "item",
                "changed": false,
                "failed": false,
                "invocation": {
                    "module_args": {
                        "checksum_algorithm": "sha1",
                        "follow": false,
                        "get_attributes": true,
                        "get_checksum": true,
                        "get_md5": false,
                        "get_mime": true,
                        "path": "/etc/app2.conf"
                    }
                },
                "item": "/etc/app2.conf",
                "stat": {
                    "exists": false
                }
            },
            {
                "ansible_loop_var": "item",
                "changed": false,
                "failed": false,
                "invocation": {
                    "module_args": {
                        "checksum_algorithm": "sha1",
                        "follow": false,
                        "get_attributes": true,
                        "get_checksum": true,
                        "get_md5": false,
                        "get_mime": true,
                        "path": "/etc/app.d/app2.conf"
                    }
                },
                "item": "/etc/app.d/app2.conf",
                "stat": {
                    "exists": false
                }
            }
        ],
        "skipped": false
    }
}

There are clearly stat nodes in the result array/list items.

Aaaah, sorry. I did no spot this initially.

The loops are wrong:

loop:
    - "{{ confcheck['results'] }}"

this should be:

loop: "{{ confcheck['results'] }}"

The first (wrong) case makes a nested list with a single element. The second one makes the proper list with one element per file.

2 Likes

Thanks! Finally.

All these hours wasted because of a dash :pensive_face:.

Welcome to the club :smile:

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.