Ensure only specific list of users exist with Ansible

ansible

We use Ansible to create users with this task:

- name: Create adm users
  user:
    name: "{{ item.name }}"
    group: "{{ common_adm_group }}"
    createhome: yes
    password: "!!"
    update_password: always
    state: present
  with_items: "{{ common_adm_users }}"

And common_adm_users is like this:

  - name: user1
    comment: "First Last"
    ssh_key: "ssh-rsa AAAAB3Nza..."

This works fine for creating users, but when someone leaves the team, just removing their user from common_adm_users doesn't cause it to be removed from the servers when we re-apply the role that includes this task.

What is the best practice to maintain an exact list of users?

Best Answer

This is a similar answer to @Konstantin Suvorov's but a bit more refined, with some nuances clearly spelled out, and actually tested and in-use.

Using the following, you only need maintain a single list of usernames you wish to have access. Any additions or removals from that list will be reflected in host access.

Optional helpful tip: We actually choose to use our GitHub usernames for this list, as then we can easily add SSH keys as well. See the end for an example.

First, in our group_vars we define our list of developers we wish to have access (note that this can be defined in multiple ways, not just group_vars):

developers:
  - user1
  - user2
  - etc...

Second, we create a group for which we will assign our developers to:

- name: Create "developers" group
  group:
    name: developers
    state: present

Third, we add our developers to this group. NOTE that we add them via the groups and not group property. This adds it as a secondary group and ensures they appear in /etc/group. This is critical because if you add them as a primary group, they will not appear as a member of that group in /etc/group:

- name: Add user accounts
  user:
    name: "{{ item }}"
    shell: /bin/bash
    groups: [developers]
    state: present
  with_items: "{{ developers }}"

Fourth, we then get all users that currently belong to the developers group on the host (Note that this is dependent on the primary/secondary assumption mentioned in the prior step) :

- name: Determine existing users
  shell: 'grep developers /etc/group | cut -d: -f4 | tr "," "\n"'
  changed_when: false 
  register: existing_users

Fifth, determine which of those users should be removed (e.g. those that are not defined in our developers group_vars list):

- name: Determine removed users
  set_fact:
    removed_users: "{{ existing_users.stdout_lines | difference(developers) }}"

Sixth and last, remove them:

- name: Delete removed user accounts
  user:
    name: "{{ item }}"
    state: absent
  with_items: "{{ removed_users }}"

Optional helpful step - If adding users by GitHub username, you can easily grant them SSH access from the public key they have available on GitHub:

- name: Add public ssh keys of users
  authorized_key:
    user: "{{ item }}"
    exclusive: yes
    key: https://github.com/{{ item }}.keys
    state: present
  with_items: "{{ developers }}"