Bash Scripting: Automating User Management in Linux
Shem Maiwuya
Posted on July 1, 2024
Why do we automate?
Managing users and groups on a Linux system can be a tedious task, especially if you have to handle multiple accounts. Fortunately, with a bit of scripting knowledge, you can automate this process. In this article, I will walk you through a simple Bash script designed to create users, assign them to groups, and set their passwords automatically.
Why Automate User Management?
- Efficiency: Automating repetitive tasks saves time.
- Consistency: Ensures the same process is followed every time.
- Error Reduction: Minimizes the risk of human error.
New to Bash scripting?
For those trying to learn about bash scripting, kindly visit here).
Script Overview
This project had me creating a simple script that performs the following functions:
- Reads the users and groups supplied in the .txt file
- Creates the users and groups from read data
- Assigns randomly generated password to each user. Passwords are saved in
/var/secure/user_passwords.csv
- Adds users to appropriate groups
- Creates a
$HOME
directory for each user with appropriate permission - Logs all script actions in
/var/log/user_management.log
Each step will be explained.
The project's repository can be found here.
Prerequisites
Before running the script, ensure you have:
- Root privileges (the script needs to be run as root).
- An input file containing user information (e.g., employees.txt).
Detailed Explanation
Checking for Root Privileges
The script starts by checking if it is being run as root. This is crucial because user and group management operations require root permissions.It does this by verifying the$EUID
variable is 0.
if [[ "$EUID" -ne 0 ]]; then
echo "Error: This script needs to run as root. Use sudo"
exit 1
fi
Creating Necessary Directories
If the required directories (/var/log and /var/secure) do not exist, the script creates them.
# Create necessary directories if they don't exist
if [ ! -d /var/log ]; then
mkdir -p /var/log
fi
if [ ! -d /var/secure ]; then
mkdir -p /var/secure
fi
Logging Function
A logging function log_message is defined to log messages with timestamps to a log file.
log_message() {
local message="$1"
echo "$(date +"%Y-%m-%d %H:%M:%S") - $message" >> $log_file
}
Validating Input Arguments
The script expects exactly one argument, the path to the employee file. If the argument is not provided or the file does not exist, it logs an error and exits.
# Check for correct number of arguments
if [ "$#" -ne 1 ]; then
arg_err="Usage: $0 <file_path>"
echo "$arg_err"
log_message "$arg_err"
exit 1
fi
employee_file=$1
# Check if the employee file exists
if [ ! -f "$employee_file" ]; then
file_missing_err="Error: File '$employee_file' not found."
echo "$file_missing_err"
log_message "$file_missing_err"
exit 1
fi
Securing the Password File
The password file is created and secures by setting correct permissions so that only the owner can read the file created for users and passwords. Permission 600 only gives the owner read privileges
# Secure password file
log_message "Securing $password_file"
if [ ! -f $password_file ]; then
echo "User,password" >> $password_file
chmod 600 $password_file
fi
Processing Users and Groups
The snippets below read the employee file line by line, processes each user and their associated groups, and performs the following operations:
- User Creation: Checks if the user exists, creates the user if not, and sets a random password.
- Group Creation: Creates a group with the same name as the user and adds the user to it. Also, adds the user to additional specified groups.
- Home Directory: Ensures the user has a home directory.
Let's break it down
- Reading the Input File The snippet below reads each line from a file, splits the line into user and groups based on the semicolon, and then trims any extra whitespace from these variables.
while IFS=';' read -r user groups; do
# Read users and groups from the .txt file
user=$(echo "$user" | xargs)
groups=$(echo "$groups" | xargs)
It also checks if there is a blank line in between entries. The condition checks if either user or groups is empty (-z checks for a zero-length string).
if [[ -z "$user" || -z "$groups" ]]; then
newline_err="Skipping invalid line in the input file"
echo "$newline_err"
log_message "$newline_err"
continue
fi
- Creating user and group array The snippet below logs and prints the user being processed, splits their groups into an array, trims any whitespace from each group, and then logs and prints the cleaned-up list of group
echo "=> Processing user: $user"
log_message "Processing user: $user"
IFS=',' read -r -a group_array <<< "$groups"
for i in "${!group_array[@]}"; do
group_array[$i]=$(echo "${group_array[$i]}" | xargs)
done
echo "=> Groups for $user: ${group_array[*]}"
log_message "Groups for $user: ${group_array[*]}
- Creating group with the same name as the user This snippet checks if a group exists, creates the group if it does not exist, and logs the actions taken
# Create group with the same name as the user
if getent group "$user" &>/dev/null; then
echo "Group $user already exists."
log_message "Group $user already exists."
else
if groupadd "$user"; then
echo "=> Group $user created."
log_message "Group $user created."
else
echo "Error creating group $user."
log_message "Error creating group $user."
continue
fi
fi
If a user exists our script logs it and moves on else the user is created and then logged.
NOTE: > /dev/null
suppresses the response so that we don't have to see the output
- Creating
$HOME
directory for user and generating passwords This snippet handles the creation of users, including setting up their home directory and assigning a password, while logging all actions and errors appropriately
# Creating user, user's home directory and assigning a randomly generated password
echo "=> Creating user: $user..."
log_message "Creating user: $user"
if id "$user" &>/dev/null; then
echo "User $user already exists."
log_message "User $user already exists."
else
if useradd -m -s "$shell" -g "$user" "$user"; then
echo "=> User $user created with home directory /home/$user."
log_message "User $user created with home directory /home/$user."
password=$(head /dev/urandom | tr -dc A-Za-z0-9 | fold -w 16 | head -n 1)
if echo "$user:$password" | chpasswd; then
echo "$user,$password" >> $password_file
echo "=> Password set for $user"
log_message "Password set for $user"
else
echo "Error setting password for $user."
log_message "Error setting password for $user."
continue
fi
else
echo "Error creating user $user."
log_message "Error creating user $user."
continue
fi
fi
The if useradd -m -s "$shell" -g "$user" "$user"
attempts to create a new user with a home directory (-m), specified shell (-s "$shell"), and primary group (-g "$user").
password=$(head /dev/urandom | tr -dc A-Za-z0-9 | fold -w 16 | head -n 1)
generates a random 16-character password using urandom
.
if echo "$user:$password" | chpasswd; then
sets the generated password for the user using the chpasswd
command.
Finally, echo "$user,$password" >> $password_file
appends the user and their password to the specified password file.
- Adding user to specified groups This snippet processes each user and adds them to the specified groups, creating the groups if they don't already exist
for group in "${group_array[@]}"; do
if getent group "$group" &>/dev/null; then
echo "Group $group already exists."
log_message "Group $group already exists."
else
if groupadd "$group"; then
echo "Group $group created."
log_message "Group $group created."
else
echo "Error creating group $group."
log_message "Error creating group $group."
continue
fi
fi
if usermod -aG "$group" "$user"; then
echo "=> Added $user to group $group."
log_message "Added $user to group $group."
else
echo "Error adding $user to group $group."
log_message "Error adding $user to group $group."
continue
fi
done
It loops through the group, groups that don't exist are created with the groupadd
command.
if usermod -aG "$group" "$user"
attempts to add the user to the group. It throws an error if the user is already a member of the group
- End of Loop and File Processing
echo "--------------------"
done < "$employee_file"
echo "--------------------"
prints a separator line for clarity in the output.
done < "$employee_file"
continues processing the next user and group pairs from the employee_file.
The complete script looks like this
while IFS=';' read -r user groups; do
# Read users and groups from the .txt file
user=$(echo "$user" | xargs)
groups=$(echo "$groups" | xargs)
if [[ -z "$user" || -z "$groups" ]]; then
newline_err="Skipping invalid line in the input file"
echo "$newline_err"
log_message "$newline_err"
continue
fi
echo "=> Processing user: $user"
log_message "Processing user: $user"
IFS=',' read -r -a group_array <<< "$groups"
for i in "${!group_array[@]}"; do
group_array[$i]=$(echo "${group_array[$i]}" | xargs)
done
echo "=> Groups for $user: ${group_array[*]}"
log_message "Groups for $user: ${group_array[*]}"
# Create group with the same name as the user
if getent group "$user" &>/dev/null; then
echo "Group $user already exists."
log_message "Group $user already exists."
else
if groupadd "$user"; then
echo "=> Group $user created."
log_message "Group $user created."
else
echo "Error creating group $user."
log_message "Error creating group $user."
continue
fi
fi
# Creating user, user's home directory and assigning a randomly generated password
echo "=> Creating user: $user..."
log_message "Creating user: $user"
if id "$user" &>/dev/null; then
echo "User $user already exists."
log_message "User $user already exists."
else
if useradd -m -s "$shell" -g "$user" "$user"; then
echo "=> User $user created with home directory /home/$user."
log_message "User $user created with home directory /home/$user."
password=$(head /dev/urandom | tr -dc A-Za-z0-9 | fold -w 6 | head -n 1)
if echo "$user:$password" | chpasswd; then
echo "$user,$password" >> $password_file
echo "=> Password set for $user"
log_message "Password set for $user"
else
echo "Error setting password for $user."
log_message "Error setting password for $user."
continue
fi
else
echo "Error creating user $user."
log_message "Error creating user $user."
continue
fi
fi
# Add the user to other specified groups
for group in "${group_array[@]}"; do
if getent group "$group" &>/dev/null; then
echo "Group $group already exists."
log_message "Group $group already exists."
else
if groupadd "$group"; then
echo "Group $group created."
log_message "Group $group created."
else
echo "Error creating group $group."
log_message "Error creating group $group."
continue
fi
fi
if usermod -aG "$group" "$user"; then
echo "=> Added $user to group $group."
log_message "Added $user to group $group."
else
echo "Error adding $user to group $group."
log_message "Error adding $user to group $group."
continue
fi
done
echo "--------------------"
done < "$employee_file"
Conclusion
This project has taught me a lot about Linux permission system, user and group management, loops, conditionals and file manipulation in bash. For those interested in putting their skills to the test, you can head over to HNG Internship where they accept DevOps interns. It is totally free but a premium version is available, visit here for more info.
P.S: Any feedback will be appreciated. Thank you for reading.
Posted on July 1, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.