Ansible set theory filters in practice

Ansible set theory filters in practice

Ansible has some really useful filters for various types of objects. A few of those are for sets of something. Recently, I had to use them in practice.

In this situation there was a set of system users in users group. Some users needed to be present and their shell to be set to /usr/sbin/nologin, so they would not have direct shell access, and some users were not supposed to be present so they needed to be removed. There were also some system users present in users group, which should be left as-is.

To start, a list with legit users was created (documented_users), along with a list of system users (system_users):

  - another.user

  - spamas
  - openvpn

To get what we needed, we first got all the users in users group on remote systems, and assign them to an Ansible variable:

- name: get users in users group
  shell: cut -d":" -f1,4 /etc/passwd | grep $(getent group users | cut -d":" -f3) | cut -d":" -f1
  register: users_users

We then create a list of legit_users by joining documented_users and system_users:

- name: set legit_users fact
    legit_users: "{{ documented_users | union(system_users) }}"

When that was done, we could determine, which users were not needed on the system (undocumented_users), and which of the documented users were actually present (present_documented_users):

- name: set undocumented_users fact
    undocumented_users: "{{ users_users.stdout_lines | difference(legit_users) }}"

- name: set present_documented_users fact
    present_documented_users: "{{ users_users.stdout_lines | intersect(documented_users) }}"

Here, difference and intersect Ansible set theory filters were really useful. It would take a bit more code to do the comparison by other means. difference returned, which elements were in the first, but not the second set, while intersect returned a list with elements in both sets.

Now that we knew, which users are in which set, we can proceed to removing the undocumented users and seting proper shell for the documented users using the user module:

- name: remove undocumented users
    name: "{{ item }}"
    state: absent
  with_items: "{{ undocumented_users }}"
    - undocumented_users is defined
    - undocumented_users | length > 0

- name: set nologin as shell for documented users
    name: "{{ item }}"
    shell: /usr/sbin/nologin
  with_items: "{{ present_documented_users }}"
    - present_documented_users is defined
    - present_documented_users | length > 0