From 4905156d3acd317172d537893fa4a67beab2ccf0 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Tue, 30 Sep 2025 07:53:04 -0500 Subject: [PATCH] Add playbook to delete a user and remove all associated data --- playbooks/delete-user.yml | 181 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 playbooks/delete-user.yml diff --git a/playbooks/delete-user.yml b/playbooks/delete-user.yml new file mode 100644 index 0000000..33aad8b --- /dev/null +++ b/playbooks/delete-user.yml @@ -0,0 +1,181 @@ +--- +- name: Delete a user and remove all associated data + hosts: all + become: true + gather_facts: true + + vars: + username: "{{ username }}" + force_removal: "{{ force_removal | default(false) }}" + backup_home: "{{ backup_home | default(false) }}" + backup_location: "{{ backup_location | default('/tmp/user_backups') }}" + + tasks: + - name: Validate username is provided + ansible.builtin.fail: + msg: "Username must be provided" + when: username is not defined or username == "" + + - name: Check if user exists + ansible.builtin.getent: + database: passwd + key: "{{ username }}" + register: user_info + failed_when: false + + - name: Display user not found message + ansible.builtin.debug: + msg: "User '{{ username }}' does not exist on this system" + when: user_info.failed + + - name: Block for user deletion when user exists + block: + - name: Get user home directory + ansible.builtin.set_fact: + user_home: "{{ user_info.ansible_facts.getent_passwd[username][4] }}" + when: not user_info.failed + + - name: Check for running processes owned by user + ansible.builtin.shell: "ps -u {{ username }} -o pid= | wc -l" + register: user_processes + changed_when: false + failed_when: false + + - name: Display warning about running processes + ansible.builtin.debug: + msg: "Warning: User '{{ username }}' has {{ user_processes.stdout.strip() }} running processes" + when: + - user_processes.stdout is defined + - user_processes.stdout.strip() | int > 0 + + - name: Terminate user processes (if force_removal is true) + ansible.builtin.shell: "pkill -u {{ username }}" + register: kill_result + failed_when: false + when: + - force_removal | bool + - user_processes.stdout is defined + - user_processes.stdout.strip() | int > 0 + + - name: Wait for processes to terminate + ansible.builtin.pause: + seconds: 3 + when: + - force_removal | bool + - kill_result is changed + + - name: Force kill remaining processes (if force_removal is true) + ansible.builtin.shell: "pkill -9 -u {{ username }}" + register: force_kill_result + failed_when: false + when: + - force_removal | bool + - user_processes.stdout is defined + - user_processes.stdout.strip() | int > 0 + + - name: Create backup directory + ansible.builtin.file: + path: "{{ backup_location }}" + state: directory + mode: '0755' + when: backup_home | bool + + - name: Backup user home directory + ansible.builtin.archive: + path: "{{ user_home }}" + dest: "{{ backup_location }}/{{ username }}_home_{{ ansible_date_time.epoch }}.tar.gz" + format: gz + when: + - backup_home | bool + - user_home is defined + - user_home != "" + + - name: Remove user from additional groups (if any) + ansible.builtin.user: + name: "{{ username }}" + groups: "" + append: false + failed_when: false + + - name: Remove user account and home directory + ansible.builtin.user: + name: "{{ username }}" + state: absent + remove: true + force: "{{ force_removal | bool }}" + + - name: Verify home directory removal + ansible.builtin.stat: + path: "{{ user_home }}" + register: home_check + when: user_home is defined + + - name: Force remove home directory if it still exists + ansible.builtin.file: + path: "{{ user_home }}" + state: absent + when: + - user_home is defined + - home_check.stat.exists is defined + - home_check.stat.exists + + - name: Remove user's mail spool + ansible.builtin.file: + path: "/var/mail/{{ username }}" + state: absent + failed_when: false + + - name: Remove user's cron jobs + ansible.builtin.file: + path: "/var/spool/cron/crontabs/{{ username }}" + state: absent + failed_when: false + + - name: Remove user's at jobs + ansible.builtin.shell: "find /var/spool/at -name '{{ username }}*' -delete" + failed_when: false + changed_when: false + + - name: Check for user-specific log files + ansible.builtin.find: + paths: /var/log + patterns: "*{{ username }}*" + file_type: file + register: user_logs + + - name: Remove user-specific log files + ansible.builtin.file: + path: "{{ item.path }}" + state: absent + loop: "{{ user_logs.files }}" + when: user_logs.files is defined + + - name: Remove user from sudoers if present + ansible.builtin.file: + path: "/etc/sudoers.d/{{ username }}" + state: absent + failed_when: false + + - name: Display deletion summary + ansible.builtin.debug: + msg: | + User deletion completed: + - Username: {{ username }} + - Home directory: {{ user_home }} (removed) + - Mail spool: removed + - Cron jobs: removed + - At jobs: removed + - Log files: removed + {% if backup_home | bool %} + - Home backup: {{ backup_location }}/{{ username }}_home_{{ ansible_date_time.epoch }}.tar.gz + {% endif %} + + when: not user_info.failed + + handlers: + - name: Update nscd cache + ansible.builtin.service: + name: nscd + state: restarted + failed_when: false + listen: "refresh user cache" \ No newline at end of file