Automating User and Group Management with Bash Scripting.
Tags: bash, linux
Published: 2024-07-03 20:41:46


In modern IT environments, efficient user and group management is key to maintaining security and operational integrity. Manually creating and managing users across multiple systems is time-consuming and very prone to errors due to its repetitiveness. Automation becomes a handy tool for handling repetitive tasks like creating and managing users, groups and permissions. This article will walk you through a Bash script that automates the processes of user and group creation, and group assignment based on a structured input file. The bash script also contains a logging mechanism for reviewing all operations of the script. 

## Objective
The script is named **create_users.sh**. It reads a text file containing usernames and their respective groups, creates users, assigns them to specified groups, generates random passwords securely for each user and stores them. It then logs all actions.

## Prerequisites
To successfully follow this tutorial, you need the following: 

- A Linux machine with root privileges: The script requires administrative rights to create users, groups, and manage file permissions (sudo or root access).

- Basic Knowledge of Bash commands and scripting: Familiarity with navigating the command line and editing scripts will help in understanding and customizing the provided script.

- A text editor of choice (vi, nano, vim, etc): You'll use a text editor to modify the script and prepare input files. Choose one that you're comfortable with or have experience using.

 
Overview
The script automates the creation of users and groups by reading input from a text file formatted as username;groups. In a nutshell, the script does the following: 

- Reads a text file passed as an argument to the script at runtime. 
- Validates root privileges and the existence of a secure directory for password storage (/var/secure).
- Creates usernames and assigns them to their respective groups specified in the input file.
- Generates strong (16 digit), random passwords for each user and securely stores them in /var/secure/user_passwords.txt.
- Logs all actions to /var/log/user_management.log for audit purposes.

## Writing our script. 

**Create script file**

Open your terminal and create an empty file named `create_user.sh`. 
`touch create_user.sh`

Using your preferred editor, open the `create_user.sh` file we just created. For this tutorial, we will be using vim text editor. feel free to use your editor of choice. 

`vim create_user.sh`

**Add the shebang line**
At the top of the script, add the [shebang line](https://en.wikipedia.org/wiki/Shebang_(Unix)). 

```
#!/bin/bash
```


**Check if the script is run with root privileges.**
In a linux system, creating users and groups, setting permissions, etc typically involve using administrative privileges. After the shebang line, add the following code. 

```bash
# Check if running as root
if [[ $(id -u) -ne 0 ]]; then
    echo "This script must be run as root."
    exit 1
fi
```

The code check above `if [[ $(id -u) -ne 0 ]]; then` retrieves the user's id then it checks if the retrieved id does not equal to 0. If it does, it means the user running the script has admin privileges and continues to the next phase of the script. It terminates if the user does not have root privileges. 




## Ensure /var/secure directory exists for storing passwords securely
The "/var/secure" directory is used for typically used as a secure directory where sensitive information, such as user passwords, can be stored with restricted access.

```bash

if [[ ! -d "/var/secure" ]]; then
    mkdir -p /var/secure
    chown root:root /var/secure
    chmod 700 /var/secure
fi
```

If the `"/var/secure"` directory does not exist, it is immediately created with all permissions (read, write, execute) set for the user.

## Setting Up Our Variables 

Before proceeding, we need to set up some variables that would be reused in other parts of the script. 
```bash
# Variables
INPUT_FILE="$1"
LOG_FILE="/var/log/user_management.log"
PASSWORD_FILE="/var/secure/user_passwords.txt"
```
- **INPUT_FILE** variable is set to $1 which is the text file that is passed as an argument to our script. 
- **LOG_FILE** variable is to the `user_management.log` in `/var/log/` and used for storing logs from our script. 

- **PASSWORD_FILE** variable is set to the `user_passwords.txt` file in `/var/secure/` directory which was created earlier by our script after initial checks if the directory doesn't exist. 


## Creating The Generate Password Utility Function

The script contains a utility function that act as a helper function for performing the task of generating a secure password which we would use in the core part of the script. 

**generate_password**

This helper function helps to generate a strong a strong random password using the `/dev/urandom` linux kernel interface for generating random passwords. 

```bash
generate_password() {
    tr -dc 'A-Za-z0-9!@#$%^&*()_+=-[]{}|;:<>,.?/~' </dev/urandom | head -c 16
}
```

The above code generates a 16-digit random password using the Kernel's built-in random number generator. The generated password consists of uppercase letters (A-Z), lowercase letters (a-z), digits (0-9), and some special characters.

User and Group Creation:

This is the core part of the script. It reads each line from the input file ($1) to create users and assign them to specified groups and logs all action to the 


```bash
while IFS=";" read -r user groups; do
    # Remove whitespaces from user and groups
    user=$(echo "$user" | xargs)
    groups=$(echo "$groups" | xargs)

    # Validate username before creating user
    if [[ -z "$user" ]]; then
        echo "Invalid username. Skipping user creation."
        continue
    fi

    # Check if user already exists
    if id "$user" >/dev/null 2>&1; then
        echo "User $user already exists. Skipping user creation."
        continue
    fi

    # Create a personal group for the user
    groupadd "$user" 2>/dev/null
    usermod -aG "$user" "$user"

    # Add user to specified groups
    IFS=',' read -ra group_list <<< "$groups"
    for group in "${group_list[@]}"; do
        group=$(echo "$group" | xargs)
        if [ -n "$group" ]; then
            groupadd "$group" 2>/dev/null
            usermod -aG "$group" "$user"
        fi
    done

    # Generate and store password securely
    password=$(generate_password)
    echo "$user:$password" >> "$PASSWORD_FILE"

    # Log actions
    echo "$(date +'%Y-%m-%d %H:%M:%S') Created user '$user' and added to groups: $groups" >> "$LOG_FILE"

done < "$INPUT_FILE"
```

Detailed breakdown of the above code blocks: 
- **Read in the file**
Firstly, we begin a while loop which is used to read in the file and set our IFS (Internal Field Seperator) to the semi-colon ";". 
`while IFS=";" read -r user groups; do`. The content of the text file is read in and it is then separated by ";" to denote which part of the content are user names and group names.

- **Trim whitespaces**
Next, we trim all whitespaces around the user names and group names using the xargs command-line utility. 
```bash
 user=$(echo "$user" | xargs)
 groups=$(echo "$groups" | xargs)
```
- **Validate users**

We validate user names using two criteria
 1. The user name is not empty:  `if [[ -z "$user" ]];` 
 2. The user name does not already exist: `if id "$user" >/dev/null 2>&1; then` 

if the user name is empty or exists, it is skipped and the script continues. 

- Create user own groups
We then create a group specifically for the user and adds the user to their groups. 

```bash
groupadd "$user" 2>/dev/null
usermod -aG "$user" "$user"
```

- Add user to specified groups
Here, we read in the text file again passed as an argument to the script but this time, the IFS is set to ','. This way, we can get the group names even if the user belongs to more than one specified groups.
```bash
IFS=',' read -ra group_list <<< "$groups"
    for group in "${group_list[@]}"; do
        group=$(echo "$group" | xargs)
        if [ -n "$group" ]; then
            groupadd "$group" 2>/dev/null
            usermod -aG "$group" "$user"
        fi
    done
```

- Generate and Store passwords for each user

Recall that we earlier created a `generate_password` helper function for our script and we can now use it to generate and store each user's password to the **$PASSWORD_FILE**.

```bash 
password=$(generate_password)
echo "$user:$password" >> "$PASSWORD_FILE"
```

- Log actions
After all operations, we add a new log entry to our log file for each user operation. 

```bash
echo "$(date +'%Y-%m-%d %H:%M:%S') Created user '$user' and added to groups: $groups" >> "$LOG_FILE"
```

- End the Loop
`done < "$INPUT_FILE"`

## Run Our Script
set execute permission for the `create_user.sh` file.

```bash
   chmod +x create_user.sh
```

Here is an sample user input file you can use for testing our bash script: `vim user_groups.txt` and paste the below text: 
```bash
nelson;cloudeng,sre
victor;backend
john;qa
jane;dev,manager
robert;marketing
emily;design,research
michael;devops
olivia;design,research
william;support
sophia;content,marketing
daniel;devops,sre
ava;dev,qa
```

now run the script using: 

```bash 
./create_users.sh user_groups.txt
```

Checking Our Script Logs: 

```bash
cat /var/log/user_management.log
```

## Conclusion

Thanks for following me on this tutorial. This technical article was created as part of my Stage 1 task for HNG Internship. 

HNG is an internship that helps people hone and improve their tech skills. To learn more, visit the HNG Internship website: https://hng.tech/internship