Automating User Management with Bash Scripting
Tags: bash, linux, authorization
Published: 2024-07-05 13:26:20

Managing users and groups is a fundamental aspect of system administration in Linux environments. As systems grow in complexity and the number of users increases, manual user management becomes tedious, error-prone, and difficult to scale. 

This article will guide you through creating a bash script to automate user and group management based on a provided text file.

## Problem Statement
The challenge lies in streamlining the process of creating new users with specific group memberships, generating secure passwords, and meticulously logging all actions for auditing and troubleshooting. 

Imagine a scenario where a company onboards multiple new employees requiring immediate access to system resources. Manually creating each user account, setting appropriate permissions, and assigning them to the correct groups would be time-consuming and prone to human error.

## Requirements
To address this challenge, the following requirements were identified:

- **Automated User and Group Creation**: The script should create new user accounts and groups based on information provided in a structured input file.
- **Password Generation and Secure Storage**: The script must generate strong, random passwords for each user and store them securely to prevent unauthorized access.
- **Group Membership Management**: The script should dynamically assign users to existing or newly created groups as specified in the input file.
- **Comprehensive Logging**: All actions performed by the script, including user and group creation, password settings, and any errors encountered, should be meticulously logged for auditing and debugging.

## Creating the Project
To tackle the user management automation task, we'll develop a Bash script. Let's break down the script creation process step by step:

### 1. Defining Essential Variables and Functions
First, we need to set up some essential variables and functions that our script will use throughout its execution. We'll define where to store log information, where to store generated passwords, how to log actions taken by the script, and how to generate random passwords.

```bash
#!/bin/bash

# Log file path
LOG_FILE="/var/log/user_management.log"

# Password file path
PASSWORD_FILE="/var/secure/user_passwords.csv"

# Ensure the secure directory exists
mkdir -p /var/secure
chmod 700 /var/secure

# Log function
log_action() {
    echo "$(date +'%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}

# Function to generate a random password
generate_password() {
    tr -dc A-Za-z0-9 </dev/urandom | head -c 12
}
```

In this code, we define the `LOG_FILE` variable to hold the path to our log file, recording every action taken by the script. Similarly, `PASSWORD_FILE` stores the path to a file where we'll securely store the generated usernames and passwords. To ensure the security of the password file, we create a directory `/var/secure` (if it doesn't exist) and set appropriate permissions using `chmod 700`.

Next, we define the `log_action` function. This function streamlines the logging process by automatically adding a timestamp before each message written to the log file. The `generate_password` function provides a mechanism to generate random **12-character alphanumeric passwords**, which we'll use to create secure user accounts.

### 2. Input Handling and Validation
Now that our script has its basic tools, we need to equip it to handle input. We'll design the script to accept the name of a file containing user information as an argument.

Here's how we implement this:

```bash
# Check if the input file is provided
if [ $# -ne 1 ]; then
    echo "Missing Argument: <name-of-text-file>. Only one argument is required."
    exit 1
fi

INPUT_FILE="$1"

# Ensure the log and password files exist
touch "$LOG_FILE"
touch "$PASSWORD_FILE"
chmod 600 "$PASSWORD_FILE"

```

This section ensures that the script receives exactly one argument—the name of the text file. We use an `if` statement with the `[ $# -ne 1 ]` condition to check the number of arguments. If the script is executed without the required input file, it will display an error message and gracefully exit with an error code of 1 `exit 1`.

The provided file name is then stored in the `INPUT_FILE` variable. Lastly, we use the `touch` command to create both the log file (`$LOG_FILE`) and the password file (`$PASSWORD_FILE`) if they don't exist. We also set secure permissions (read and write only for the owner) on the `PASSWORD_FILE` using `chmod 600 `to protect sensitive information.

### 3. Processing the Input File
With the input mechanism in place, we're ready to process the input file line by line. Each line in the file represents a user, containing their username and group memberships separated by a semicolon. 

```bash

# Read the input file line by line
while IFS=';' read -r username groups; do
    # Trim leading/trailing whitespace
    username=$(echo "$username" | xargs)
    groups=$(echo "$groups" | xargs)

    # Check if the username is empty
    if [ -z "$username" ]; then
        continue 
    fi

    # ... (We'll add user creation and group assignment logic here in the next step) 

done < "$INPUT_FILE"
```

This code block uses a while loop to iterate through each line of the `$INPUT_FILE`. We set the `IFS` (Internal Field Separator) to a semicolon, so each line is split into fields wherever a semicolon (;) appears. The read command then assigns the first field (the username) to the username variable and the remaining fields (the groups) to the groups variable.

We then trim any extra spaces from the `username` and `groups` variables using `xargs`. This ensures consistency and prevents issues that might arise from unintended whitespace. An empty username signifies an invalid entry, prompting us to skip to the next line using the `continue` statement.

### 4. User and Group Management
This section is the heart of our script, where we'll implement the logic for creating users, creating their personal groups, and assigning them to the appropriate groups.

```bash
# Create the user's personal group
    if ! getent group "$username" > /dev/null; then
        groupadd "$username"
        log_action "Created group $username"
    fi

    # Create the user with the user's personal group
    if ! id -u "$username" > /dev/null 2>&1; then
        useradd -m -g "$username" -s /bin/bash "$username"
        log_action "Created user $username with group $username"
    else
        log_action "User $username already exists"
    fi

    # Set up home directory permissions
    chmod 700 /home/"$username"
    chown "$username":"$username" /home/"$username"

    # Assign the user to additional groups
    if [ -n "$groups" ]; then
        IFS=',' read -ra GROUP_ARRAY <<< "$groups"
        for group in "${GROUP_ARRAY[@]}"; do
            group=$(echo "$group" | xargs)
            if ! getent group "$group" > /dev/null; then
                groupadd "$group"
                log_action "Created group $group"
            fi
            usermod -aG "$group" "$username"
            log_action "Added user $username to group $group"
        done
    fi
```

Let's examine the code closely. First, we use `getent` group to check if a group with the user's name already exists. If it doesn't, we create the group using `groupadd` and log the action.

Next, we check for the existence of the user using `id -u`. If the user doesn't exist, the `useradd` command comes into play. The `-m` flag tells `useradd` to create the user's home directory, -g "`$username`" assigns the user's personal group as their primary group, and -s /bin/bash sets their default shell to `/bin/bash`. Of course, we diligently log this successful user creation. If a user already exists, the script logs that information.

After creating the user, we set the appropriate permissions on the user's home directory using `chmod 700`, giving the owner (the user) full control (read, write, execute). We also ensure the user is the owner of their home directory using `chown`.

The final part of this section handles additional group assignments. If the groups variable is not empty (meaning additional groups are specified), we split the comma-separated group names into an array called `GROUP_ARRAY`. We then iterate through each group in the array; for each group, it checks if the group exists and creates it if needed (just like we did for the personal group). Finally, it adds the user to the group using `usermod -aG`.

### 5. Password Management
Our final step involves generating a secure password for each user, setting that password for the user's account, and then securely storing this sensitive information. 

```bash
# Generate a random password and set it
    password=$(generate_password)
    echo "$username:$password" | chpasswd
    log_action "Set password for user $username"

    # Store the username and password securely
    echo "$username,$password" >> "$PASSWORD_FILE"
```

We first call our `generate_password` function to obtain a random password, storing it in the `password` variable. Next, we use the chpasswd command to set this password for the user. The echo "`$username:$password`" construct pipes the username and password in the required format to `chpasswd`. As always, we record this successful password setting in our log file.

Finally, we append the newly created username and password pair (separated by a comma) to our secure `PASSWORD_FILE`.

## Testing the Script
It's time to put it to the test. We'll execute the script, providing it with a sample input file containing user information. You can find a sample text file in this [GitHub Gist](https://gist.github.com/Mobey-eth/e8f23208a215e14afebaa2f3f515245e)

Make sure you have saved the script as `create_users.sh` and made it executable using:

```bash
sudo chmod +x create_users.sh
```

Now, run the script by passing the name of your input file as an argument:

```bash
sudo ./create_users.sh users.txt
```

Replace `users.txt` with the actual name of your input file if it's different.

### Verify the Results
After running the script, it's essential to confirm that everything worked as expected.

- **Check the Log File**: To review the actions taken by our script, open the log file located at `/var/log/user_management.log`. This file serves as a detailed record of the script's execution. Carefully examine the log entries to confirm that users and groups were created as intended and that passwords were successfully set. 

```bash
ubuntu@mobi:~$ cat /var/log/user_management.log 
2024-07-04 21:31:19 - Created group light
2024-07-04 21:31:19 - Created user light with group light
2024-07-04 21:31:19 - Added user light to group sudo
2024-07-04 21:31:19 - Created group dev
2024-07-04 21:31:19 - Added user light to group dev
2024-07-04 21:31:19 - Added user light to group www-data
2024-07-04 21:31:19 - Set password for user light
2024-07-04 21:31:19 - Created group idimma
2024-07-04 21:31:19 - Created user idimma with group idimma
2024-07-04 21:31:19 - Added user idimma to group sudo
2024-07-04 21:31:19 - Set password for user idimma
2024-07-04 21:31:19 - Created group mayowa
2024-07-04 21:31:19 - Created user mayowa with group mayowa
2024-07-04 21:31:20 - Added user mayowa to group dev
2024-07-04 21:31:20 - Added user mayowa to group www-data
2024-07-04 21:31:20 - Set password for user mayowa
2024-07-04 21:31:20 - Created group alice
2024-07-04 21:31:20 - Created user alice with group alice
2024-07-04 21:31:20 - Added user alice to group sudo
2024-07-04 21:31:20 - Added user alice to group dev
2024-07-04 21:31:20 - Set password for user alice
2024-07-04 21:31:20 - Created group bob
2024-07-04 21:31:20 - Created user bob with group bob
2024-07-04 21:31:20 - Added user bob to group dev
2024-07-04 21:31:20 - Added user bob to group www-data
2024-07-04 21:31:20 - Set password for user bob
2024-07-04 21:31:20 - Created group charlie
2024-07-04 21:31:20 - Created user charlie with group charlie
2024-07-04 21:31:20 - Added user charlie to group sudo
2024-07-04 21:31:20 - Set password for user charlie
2024-07-04 21:31:20 - Created group daniel
2024-07-04 21:31:20 - Created user daniel with group daniel
2024-07-04 21:31:20 - Added user daniel to group dev
2024-07-04 21:31:20 - Added user daniel to group www-data
2024-07-04 21:31:20 - Set password for user daniel
2024-07-04 21:31:20 - Created group eve
2024-07-04 21:31:20 - Created user eve with group eve
2024-07-04 21:31:20 - Added user eve to group sudo
2024-07-04 21:31:20 - Added user eve to group www-data
2024-07-04 21:31:21 - Set password for user eve
2024-07-04 21:31:21 - Created group frank
2024-07-04 21:31:21 - Created user frank with group frank
2024-07-04 21:31:21 - Added user frank to group dev
2024-07-04 21:31:21 - Set password for user frank
2024-07-04 21:31:21 - Created group george
2024-07-04 21:31:21 - Created user george with group george
2024-07-04 21:31:21 - Added user george to group sudo
2024-07-04 21:31:21 - Added user george to group dev
2024-07-04 21:31:21 - Added user george to group www-data
2024-07-04 21:31:21 - Set password for user george
2024-07-04 21:31:21 - Created group henry
2024-07-04 21:31:21 - Created user henry with group henry
2024-07-04 21:31:21 - Added user henry to group sudo
2024-07-04 21:31:21 - Set password for user henry
2024-07-04 21:31:21 - User bob already exists
2024-07-04 21:31:21 - Added user bob to group sudo
2024-07-04 21:31:21 - Added user bob to group dev
2024-07-04 21:31:21 - Added user bob to group www-data
2024-07-04 21:31:21 - Set password for user bob

```

- **Verify Password Storage**: Next, let's ensure our passwords are stored securely. Verify that the password file exists at the path specified in our script: `/var/secure/user_passwords.csv`. Open this file and examine its contents. It should contain the generated usernames and their corresponding passwords, formatted as `username,password` for each entry.

```bash
ubuntu@mobi:~$ sudo cat /var/secure/user_passwords.csv
light,Q5RvEhh65dZo
idimma,nKXhAtai7T97
mayowa,HRMmZ6nkIda6
alice,UMZiNsT02NQM
bob,2d1ZfdYZbldF
charlie,K4esdb8BC9Xt
daniel,p0iq7Cstgn4c
eve,DJQB3grtcFQQ
frank,Nh6JxwRJ8azq
george,3VC2ya1b41Xl
henry,DF9FL9HxPYq0
bob,nA4JQvk3skAk

```

## Conclusion

This article has walked you through automating user and group creation in Linux using a Bash script. With this script, system administrators can efficiently onboard new users within their organization, eliminating repetitive manual steps.

The complete script can be found in this [GitHub repository](https://github.com/Mobey-eth/HNG-Devops-Stage1-Task/).

Huge Thanks to [HNG](https://hng.tech/) for providing this opportunity. If you're eager to level up your technical skills and embark on a rewarding tech career, be sure to explore the opportunities offered by the HNG Internship program.

Visit the [HNG Internship](https://hng.tech/internship) page to learn more about upcoming internship opportunities. If you're looking for top-tier talent for your next project, you can find exceptional individuals within the HNG network at [HNG Hire](https://hng.tech/hire).