Automating User and Group Management with a Bash Script in Linux
Tags: devops, linux, bash
Published: 2024-07-01 14:32:16

In the fast-paced world of DevOps Engineering, automation is key to efficiency and reliability. Managing user accounts and groups manually can be a stressful, time-consuming, and error-prone task, especially in environments with numerous users. Recognizing this challenge, the [HNG Internship](https://hng.tech/internship) provided a practical task that simulates a real-world scenario, where [interns](https://hng.tech/premium) are required to automate user and group management on a Linux system.
The task is to create a Bash script that automates the creation of users and groups, securely manages passwords, and logs all actions performed. This exercise not only enhances scripting skills but also deepens the understanding of Linux system administration.

## Task
Your company has employed many new developers. As a SysOps engineer, write a bash script called `create_users.sh` that reads a text file containing the employee’s usernames and group names, where each line is formatted as user;groups.
The script should create users and groups as specified, set up home directories with appropriate permissions and ownership, generate random passwords for the users, and log all actions to `/var/log/user_management.log`. Additionally, store the generated passwords securely in `/var/secure/user_passwords.txt`.
Ensure error handling for scenarios like existing users and provide clear documentation and comments within the script.
### Requirements:
- Each User must have a personal group with the same group name as the username, this group name will not be written in the text file.
- A user can have multiple groups, each group delimited by comma ","
- Usernames and user groups are separated by semicolon ";"- Ignore whitespace
e.g.
   ```text
   light; sudo,dev,www-data
   idimma; sudo
   mayowa; dev,www-data
   ```
For the first line, light is username and groups are sudo, dev, www-data.

## My solution
To tackle this problem, I first broke down the requirements stated into clear, actionable steps:

1. **Read Input File:**
   - Read the text file containing the employee’s usernames and group names.
   - Each line is formatted as `user;groups`.

2. **Process Each Line:**
   - Split each line into the username and the corresponding groups.
   - Remove any whitespace for accurate processing.

3. **Create Users and Groups:**
   - Ensure each user has a personal group with the same name as their username.
   - Create additional groups if they do not already exist.
   - Add the user to the specified groups.

4. **Set Up Home Directories:**
   - Create home directories for each user.
   - Set appropriate permissions and ownership for the directories.

5. **Generate and Store Passwords:**
   - Generate random passwords for each user.
   - Securely store the passwords in `/var/secure/user_passwords.txt`.

6. **Log Actions:**
   - Log all actions performed (e.g., user creation, group assignment) to `/var/log/user_management.log`.

7. **Error Handling:**
   - Implement error handling to manage scenarios like existing users or groups.

From this list of requirements, I mapped out a clear plan to solve the problem. These are the steps I followed:

1. Create folders for logs and secure passwords, but do nothing if they already exist.
    ```bash
mkdir -p /var/log /var/secure
    ```

2. Create files for logs and password storage.
    ```bash
touch /var/log/user_management.log
touch /var/secure/user_passwords.txt
    ```

3. Make the password file secure, giving it read and write permission for the file owner only.
    ```bash
chmod 600 /var/secure/user_passwords.txt
    ```

4. Define a function that logs actions performed by the script to the log file.
    ```bash
log_action() {
    echo "$(date) - $1" >> "/var/log/user_management.log"
}
    ```

5. Create a function that handles user creation. This function does the following:
    - Checks if the user already exists by calling the id command and redirecting the output to `/dev/null`. If the user exists, it logs a message and returns.
    - Creates a personal group for the user using the `groupadd` command.
    - Creates the additional groups if they do not exist.
    - Creates the user with the `useradd` command, setting the home directory, shell, and primary group.
    - Logs a message indicating the success or failure of the `useradd` command.
    - Adds the user to the other groups using the `usermod` command.
    - Generates a random password for the user using `/dev/urandom` and stores the user and their respective password in `/var/secure/user_passwords.txt`, the secure password file.
    - Sets the permissions and ownership for the user's home directory.

    Here is the source code:
    ```bash
create_user() {

    # defines 3 variables passed to the function: user, groups, and password
    local user="$1"
    local groups="$2"
    local password

    # check if user already exists by logging user information with id

    if id "$user" &>/dev/null; then
        log_action "User $user already exists."
        return
    fi

    # Create personal group for the user
    groupadd "$user"

    # Create additional groups if they do not exist
    IFS=' ' read -ra group_array <<< "$groups"

    # Log the group array
    log_action "User $user will be added to groups: ${group_array[*]}"

    for group in "${group_array[@]}"; do
        group=$(echo "$group" | xargs)  # Trim whitespace
        if ! getent group "$group" &>/dev/null; then
            groupadd "$group"
            log_action "Group $group created."
        fi
    done

    # Create user with home directory and shell, primary group set to the personal group
    useradd -m -s /bin/bash -g "$user" "$user"
    if [ $? -eq 0 ]; then
        log_action "User $user created with primary group: $user"
    else
        log_action "Failed to create user $user."
        return
    fi

    # Add the user to additional groups
    for group in "${group_array[@]}"; do
        usermod -aG "$group" "$user"
    done
    log_action "User $user added to groups: ${group_array[*]}"

    # generate password and store it securely in a file
    password=$(</dev/urandom tr -dc A-Za-z0-9 | head -c 12)
    echo "$user:$password" | chpasswd

    # store user and password securely in a file
    echo "$user,$password" >> "/var/secure/user_passwords.txt"

    # set permissions and ownership for user home directory
    chmod 700 "/home/$user"
    chown "$user:$user" "/home/$user"

    log_action "Password for user $user set and stored securely."
}
    ```
6. In the main script, check if the list of users was provided in the script's arguments.
    ```bash
if [ $# -ne 1 ]; then
    echo "Usage: $0 <user_list_file>"
    exit 1
fi
    ```

7. If it was provided, check that it is a valid file:
    ```bash
filename="$1"
if [ ! -f "$filename" ]; then
    echo "Users list file $filename not found."
    exit 1
fi
    ```

8. Read the file line-by-line, extract the user and groups from each line, and call the `create_user` function passing the user and groups as an argument:
    ```bash
filename="$1"
if [ ! -f "$filename" ]; then
    echo "Users list file $filename not found."
    exit 1
fi
    ```

Here is the complete code file:
```bash
#!/bin/bash

# create folders for logs and secure passwords
mkdir -p /var/log /var/secure

# create logs file and passwords file
touch /var/log/user_management.log
touch /var/secure/user_passwords.txt

# make password file secure (read and write permissions for file owner only)

chmod 600 /var/secure/user_passwords.txt

# function to log actions to log file
log_action() {
    echo "$(date) - $1" >> "/var/log/user_management.log"
}


create_user() {

    # defines 3 variables passed to the function: user, groups, and password
    local user="$1"
    local groups="$2"
    local password

    # check if user already exists by logging user information with id

    if id "$user" &>/dev/null; then
        log_action "User $user already exists."
        return
    fi

    # Create personal group for the user
    groupadd "$user"

    # Create additional groups if they do not exist
    IFS=' ' read -ra group_array <<< "$groups"

    # Log the group array
    log_action "User $user will be added to groups: ${group_array[*]}"

    for group in "${group_array[@]}"; do
        group=$(echo "$group" | xargs)  # Trim whitespace
        if ! getent group "$group" &>/dev/null; then
            groupadd "$group"
            log_action "Group $group created."
        fi
    done

    # Create user with home directory and shell, primary group set to the personal group
    useradd -m -s /bin/bash -g "$user" "$user"
    if [ $? -eq 0 ]; then
        log_action "User $user created with primary group: $user"
    else
        log_action "Failed to create user $user."
        return
    fi

    # Add the user to additional groups
    for group in "${group_array[@]}"; do
        usermod -aG "$group" "$user"
    done
    log_action "User $user added to groups: ${group_array[*]}"

    # generate password and store it securely in a file
    password=$(</dev/urandom tr -dc A-Za-z0-9 | head -c 12)
    echo "$user:$password" | chpasswd

    # store user and password securely in a file
    echo "$user,$password" >> "/var/secure/user_passwords.txt"

    # set permissions and ownership for user home directory
    chmod 700 "/home/$user"
    chown "$user:$user" "/home/$user"

    log_action "Password for user $user set and stored securely."
}

# check if user list file is provided
if [ $# -ne 1 ]; then
    echo "Usage: $0 <user_list_file>"
    exit 1
fi

filename="$1"

if [ ! -f "$filename" ]; then
    echo "Users list file $filename not found."
    exit 1
fi

# read user list file and create users
while IFS=';' read -r user groups; do
    user=$(echo "$user" | xargs)
    groups=$(echo "$groups" | xargs | tr -d ' ')

    # Replace commas with spaces for usermod group format
    groups=$(echo "$groups" | tr ',' ' ')
    create_user "$user" "$groups"
done < "$filename"

echo "User creation process completed. Check /var/log/user_management.log for details."
```

The `create_users.sh` bash script has successfully automated user and group management on a Linux system. It reads a text file containing employee usernames and group names, creates users and groups as specified, sets up home directories with appropriate permissions and ownership, generates random passwords for the users, and logs all actions to `/var/log/user_management.log`. Additionally, it securely stores the generated passwords in `/var/secure/user_passwords.txt`. Thanks for reading!