Linux Users and Groups Management using Bash Script
Tags: bash, hng, linux, sysops
Published: 2024-07-01 16:44:52

## INTRODUCTION
User and group management is a fundamental task for any Linux SysOps engineer. Handling these tasks manually can be error-prone and time-consuming, especially in larger environments. It is critical when dealing with multiple users and groups to ensure each user has the correct permissions and access within the system. To streamline this process, I have developed a Bash script, `create_users.sh`, which automates the creation of users and groups based on a supplied file. This script not only creates users and their corresponding home directories but also ensures they are assigned to the correct groups and generates secure random passwords for them. Additionally, the script logs all actions for auditing purposes.

## Script Overview
The create_users.sh script automates the following tasks:

- Reading user and group information from a file.
- Creating users and their personal groups.
- Setting up home directories with proper permissions.
- Generating random passwords for each user.
- Logging all actions to a log file.
- Storing generated passwords securely in a CSV file.

Let's break down the script step by step to understand its functionality.

**0. Shebang: Specifying the Interpreter**

The script begins with a shebang line:
`#!/bin/bash`
The importance of this line is;
- Interpreter Directive: It specifies the path to the interpreter that should be used to execute the script. In this case, #!/bin/bash tells the system to use the Bash shell to run the script. The interpreter is responsible for parsing and executing the commands within the script.
- Executable Scripts: When the shebang is present, the script can be executed directly as a standalone program. This means the script can be run by simply typing its name, provided it has execute permissions. Without the shebang, you would need to explicitly invoke the interpreter and pass the script as an argument (e.g., bash create_users.sh).

**1. Checking Script Arguments**

The script starts by verifying if a file name argument is provided alongside the bash script during runtime. This file should contain the username and group information in a specific format.

```
if [ "$#" -ne 1 ]; then
    echo "Usage: $0 <user_file>"
    exit 1
fi
```
If the argument is missing, the script exits with a usage message, ensuring the user knows how to correctly run the script.

**2. Assigning user file argument supplied to a variable**

```
USER_FILE="$1"
```
`USER_FILE` is a variable that I defined within the script. By assigning `USER_FILE="$1"`, I am storing the value of the first argument passed to the script in this variable which is then used throughout the script to refer to the file containing the user and group information. By assigning the argument to a variable, the script can easily reference the input file without directly using $1 multiple times, making the code more readable and maintainable.

**3. Checking Input File Existence**

The below command checks if the specified user file exists. If not, it exits with an error message.

```
if [ ! -f "$USER_FILE" ]; then
    echo "Error: File $USER_FILE does not exist."
    exit 1
fi
```
This is to prevent the script from running without the necessary input data, avoiding potential errors.

**4. Defining File Paths for logs and passwords**

```
LOG_FILE="/var/log/user_management.log"
PASSWORD_DIR="/var/secure"
PASSWORD_FILE="$PASSWORD_DIR/user_passwords.csv"
```
These assigned paths to variables `LOG_FILE` and `PASSWORD_FILE` help in organizing the script's output and ensuring logs and passwords are stored securely.

**5. Ensuring Log and Password Files Exist**

```
touch $LOG_FILE

if [ ! -d "$PASSWORD_DIR" ]; then
    mkdir -p "$PASSWORD_DIR"
    chmod 700 "$PASSWORD_DIR"
fi

if [ ! -f "$PASSWORD_FILE" ]; then
    echo "username,password" > $PASSWORD_FILE
fi
chmod 600 $PASSWORD_FILE
```
This section of the script ensures that the log file and password directory exist, creating them if necessary. It also sets appropriate permissions for security. Creating the password directory with chmod 700 and the password file with chmod 600, ensures that only the root user can read and write these files, protecting sensitive information.

**6. Logging Function**

```
log_action() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> $LOG_FILE
}
```
This simple logging function records actions with timestamps, helping in troubleshooting and auditing. It appends log entries to the log file, providing a history of all script actions.

**7. Reading and Processing the Input File**

```
while IFS=';' read -r username groups; do
    username=$(echo "$username" | xargs)
    groups=$(echo "$groups" | xargs)
```
The script reads the input file line by line at this stage, processing each entry to extract the usernames and groups. It removes any leading or trailing whitespace from the entries.This ensures that any extraneous spaces in the input file do not cause issues.

**8. Checking and Creating Users and Groups**

```
    if id -u "$username" >/dev/null 2>&1; then
        log_action "User $username already exists."
        continue
    fi
```
For each user, the script checks if the user already exists. If the user exists, it logs this information and skips it to the next user. If the user does not exist, the script creates a personal group for the user.

```
    if ! getent group "$username" >/dev/null 2>&1; then
        groupadd "$username"
    fi
```
It then creates the user with their personal group and sets up their home directory with the correct permissions.

```
    useradd -m -g "$username" -s /bin/bash "$username"
    chmod 700 /home/"$username"
    chown "$username:$username" /home/"$username"
```
This ensures that the user's home directory is private and owned by the user.

**9. Generating and Setting Passwords**

```
    password=$(openssl rand -base64 12)
    echo "$username:$password" | chpasswd
    echo "$username,$password" >> $PASSWORD_FILE
```
The script section generates a random password using openssl, sets this password for the user, and logs it securely in the password file. Using openssl rand -base64 12 generates a secure 12-character password for each user.

**10. Assigning Additional Groups**

```
    if [ -n "$groups" ]; then
        IFS=',' read -r -a group_array <<< "$groups"
        for group in "${group_array[@]}"; do
            group=$(echo "$group" | xargs)
            if ! getent group "$group" >/dev/null 2>&1; then
                groupadd "$group"
            fi
            usermod -aG "$group" "$username"
        done
    fi
```
If additional groups are specified for the user, the script processes each group, ensuring it exists before adding the user to the group. This ensures that users are correctly assigned to all specified groups, creating groups if they do not already exist.

**11. Logging Completion**

```
    log_action "User $username created with groups: $username, $groups"
done < $USER_FILE

log_action "User creation process completed."
```
After processing all users, the script logs the completion of the user creation process.

Thanks to the HNG team for their brilliant initiative. This technical article fulfills stage 1 to stage 2 of the HNG11 internship program. For those looking to enhance their skills, consider joining the [HNG Internship program](https://hng.tech/internship). This initiative offers valuable hands-on experience and mentorship from industry experts. Additionally, for more advanced resources and opportunities, the [HNG Premium](https://hng.tech/premium) platform provides exclusive access to premium content and professional development tools.