Bash Scripting: Automating User Management in Linux

xtasy

Shem Maiwuya

Posted on July 1, 2024

Bash Scripting: Automating User Management in Linux

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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  
Enter fullscreen mode Exit fullscreen mode

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:

  1. User Creation: Checks if the user exists, creates the user if not, and sets a random password.
  2. 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.
  3. 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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  • 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[*]}
Enter fullscreen mode Exit fullscreen mode
  • 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   
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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.

💖 💪 🙅 🚩
xtasy
Shem Maiwuya

Posted on July 1, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related