ansiblefilteringansible-module

Ansible - Filter Dictionary Values


I have gathered the list of all users using getent_module:

- name: Get user info
  getent:
    database: passed

This returns this variable as getent_passwd, a dictionary like this:

{
    "uuidd": [
        "x",
        "107",
        "112",
        "",
        "/run/uuidd",
        "/usr/sbin/nologin"
    ],
    "www-data": [
        "x",
        "33",
        "33",
        "www-data",
        "/var/www",
        "/usr/sbin/nologin"
    ]
}

I'm trying to return an array of users, including some specific users, finding the key of item.value in which "/home" is a part of one of the value array items and "nologin" is not. This is the code I have written so far, but it is not correct.

- name: style the user list
  set_fact:
    my_users: "{{ item.key | default([]) }}"
  when:
    - "'nologin' not in (item.value)"
    - "'/home' search in (item.value)"
  loop: "{{ lookup('dict', getent_passwd) }}"

How should I change my conditions to get the expected result?


Solution

  • As you understood correctly, since pattern/regex matching works on strings and not on items of list, when condition won't work. While matching items from a list, we need to match the complete item, i.e.

    when:
      - "'/home/someuser' in item.value"
      - "'/usr/sbin/nologin' not in item.value"
    

    Typically, the $HOME path in Linux is /home/$USER. The resulting getent_passwd contains the username in item.key. Which means that we can use /home/{{ item.key }} to match in item.value.

    A task such as below should do the job:

        - name: save users with actual login
          set_fact:
            my_users: "{{ my_users | default([]) + [ item.key ] }}"
          when:
            - "'/home/' ~ item.key in item.value"
            - "'/usr/sbin/nologin' not in item.value"
          loop: "{{ lookup('dict', getent_passwd) }}"
    

    The other approach could be to actually join the item.value to get a string similar to the one in /etc/passwd so that we can do text search such as: '/home' search in (item.value).

    Example:

       # item.value | join(':') is the same as each line of '/etc/passwd'
        - name: save users with actual login
          set_fact:
            my_users: "{{ my_users | default([]) + [ item.key ] }}"
          when:
            - "'/home' in item.value | join(':')"
            - "'nologin' not in item.value | join(':')"
          loop: "{{ lookup('dict', getent_passwd) }}"