#!/bin/bash
# user_manage.sh - User and Group Management Script
#
# Copyright (C) 2025 LINUXexpert.org
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, version 3 of the License.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see .
#
# Usage: user_manage.sh [arguments...]
# Subcommands: adduser , deluser , addgroup , delgroup ,
# addtogroup , lock , unlock ,
# listusers, listgroups
# Description: Automates user/group creation, deletion, and modifications.
# Requires root privileges for most operations.
set -euo pipefail
subcmd="${1:-}"
# The read-only subcommands work for anyone; everything else edits
# /etc/passwd, /etc/group or /etc/shadow. Without this the script ran
# useradd/userdel/usermod as an ordinary user and reported "Failed to
# create user." with no hint that privilege was the problem.
usage() {
echo "Usage: $0 {adduser|deluser|addgroup|delgroup|addtogroup|lock|unlock|listusers|listgroups}"
}
case "$subcmd" in
# Read-only: anyone may run these.
listusers|listgroups) ;;
# Mutating: gate on root. Checked after the subcommand is recognised,
# so `user_manage.sh` with no arguments still prints usage instead of
# complaining about privileges.
adduser|deluser|addgroup|delgroup|addtogroup|lock|unlock)
if [ "$EUID" -ne 0 ]; then
echo "This action requires root. Re-run with sudo." >&2
exit 1
fi
;;
*)
usage >&2
exit 1
;;
esac
# Usernames and group names reach useradd/userdel directly. Keep them to
# what a POSIX account name may contain, so nothing surprising is passed
# through as an option or a path.
valid_name() {
[[ "$1" =~ ^[a-z_][a-z0-9_-]{0,31}$ ]]
}
case "$subcmd" in
adduser)
user="${2:-}"
if [ -z "$user" ]; then echo "Username required. Usage: $0 adduser "; exit 1; fi
valid_name "$user" || { echo "Not a valid username: $user" >&2; exit 1; }
# Create user with a home directory (-m) and default settings
useradd -m "$user" && echo "User '$user' created." || echo "Failed to create user."
;;
deluser)
user="${2:-}"
if [ -z "$user" ]; then echo "Username required. Usage: $0 deluser "; exit 1; fi
valid_name "$user" || { echo "Not a valid username: $user" >&2; exit 1; }
if ! id -u "$user" >/dev/null 2>&1; then echo "No such user: $user" >&2; exit 1; fi
# -r removes the home directory and mail spool. That is unrecoverable,
# so show what is about to go and confirm.
echo "About to delete user '$user' and remove:"
echo " home: $(getent passwd "$user" | cut -d: -f6)"
if [ "${ASSUME_YES:-}" != "1" ]; then
if [ ! -t 0 ]; then
echo "Refusing without confirmation; set ASSUME_YES=1 for unattended use." >&2
exit 1
fi
read -r -p "Delete user '$user' and their home directory? (yes/NO): " reply
[ "$reply" = "yes" ] || { echo "Cancelled."; exit 0; }
fi
userdel -r "$user" && echo "User '$user' deleted." || { echo "Failed to delete user." >&2; exit 1; }
;;
addgroup)
group="${2:-}"
if [ -z "$group" ]; then echo "Group name required. Usage: $0 addgroup "; exit 1; fi
valid_name "$group" || { echo "Not a valid group name: $group" >&2; exit 1; }
groupadd "$group" && echo "Group '$group' created." || echo "Failed to create group."
;;
delgroup)
group="${2:-}"
if [ -z "$group" ]; then echo "Group name required. Usage: $0 delgroup "; exit 1; fi
groupdel "$group" && echo "Group '$group' deleted." || echo "Failed to delete group."
;;
addtogroup)
user="${2:-}"; group="$3"
if [ -z "$user" ] || [ -z "$group" ]; then
echo "Usage: $0 addtogroup "; exit 1;
fi
usermod -aG "$group" "$user" && echo "Added user '$user' to group '$group'." || echo "Failed to modify group membership."
;;
lock)
user="${2:-}"
if [ -z "$user" ]; then echo "Username required. Usage: $0 lock "; exit 1; fi
usermod -L "$user" && echo "User '$user' account locked." || echo "Failed to lock account."
;;
unlock)
user="${2:-}"
if [ -z "$user" ]; then echo "Username required. Usage: $0 unlock "; exit 1; fi
usermod -U "$user" && echo "User '$user' account unlocked." || echo "Failed to unlock account."
;;
listusers)
cut -d: -f1 /etc/passwd
;;
listgroups)
cut -d: -f1 /etc/group
;;
*)
usage >&2
exit 1
;;
esac