How to Automate User and Group Creation and Management Using a Simple Bash Script
Tags: linux, devops, learning
Published: 2024-07-03 19:23:17

One of the telling signs someone's a DevOps engineer is their unrivaled urge to automate process. It isn't just for automation sake, but to:

1. Enhance productivity
2. Reduce human error

One such task is onboarding new developers to the team. This includes creating user accounts, assigning them to appropriate groups, and setting secure passwords. 

This article will walk you how you can streamline this process using a bash script. You don't have to be a pro at this, it shows you why each step is important.

What does this script do?
The script automates the following tasks:
* Prompts the user for the path to an employee configuration file.
* Validates the provided file path.
* Creates necessary directories and sets appropriate permissions.
* Checks for and installs the `makepasswd` tool if not already present.
* Generates secure passwords for new users.
* Creates user accounts and assigns them to specified groups.
* Logs all actions for auditing purposes.

Let's break down each section of the script.

## Color-Coded Output Functions
```
bash
#!/bin/bash

#--------------------------
# Function to echo in green
#--------------------------
green_echo() {
    echo -e "\e[32m$1\e[0m"
}

#------------------------
# Function to echo in red
#------------------------
red_echo() {
    echo -e "\e[31m$1\e[0m"
}
```

These functions, `green_echo` and `red_echo`, are used to print messages in green and red. The idea is to help you differentiate between successful and error messages, making your script's output more readable. Typically, output messages from running scripts are in white (against the set background of your terminal) making it difficult to see errors among the sea of output messages. Red and green-codded outputs make it easier to scan your script output.


## Checking if a File Argument Was Provided
```bash
#---------------------------------------------------------
# Check if a file path argument is provided and validate it
#---------------------------------------------------------
if [ -z "$1" ]; then
    red_echo "Error: No file path provided. Please provide the employee config file path as the first argument."
    exit 1
fi

EMPLOYEE_CONFIG_FILE="$1"

if [ ! -f "$EMPLOYEE_CONFIG_FILE" ]; then
    red_echo "Error: The file '$EMPLOYEE_CONFIG_FILE' does not exist or is not a regular file."
    exit 1
fi

green_echo "File path is valid."
```
This portion of the script confirms the argument the user attaches to the script is a valid file and exists. If the file path is invalid, it will prompt the user to try again, ensuring an argument is provided and it exists before proceeding.

## Defining File Paths
```bash
#-----------------------------------------
# Variables for the password and log files
#-----------------------------------------
PASSWORD_FILE="/var/secure/user_passwords.txt"
LOG_FILE="/var/log/user_management.log"
```

These variables define the paths for storing generated passwords and logs. It is standard DevOps practice to store passwords in a file. And for even better practice, ensures these files are secure and only readable by appropriate personnel. 

Another standard practice is logging actions. It helps you in auditing and troubleshooting should you be stuck. Defining the variables for the paths to these files early on makes it easier to maintain the script. 

## Creating and Securing Directories
```bash
#----------------------------------------------------------
# Create necessary directories with appropriate permissions
#----------------------------------------------------------
sudo mkdir -p /var/secure
sudo mkdir -p /var/log
sudo chmod 600 /var/secure
```

This script block creates the directories to the files for storing passwords and logs if they do not already exist. It also sets strict permissions (`chmod 600`) on the `/var/secure` directory so that only the user can read and write to it, enhancing security.

## Ensuring `makepasswd` is Installed
```bash
#----------------------------------------------
# Checking and ensuring makepasswd is installed
#----------------------------------------------
if ! command -v makepasswd &>/dev/null; then
    sudo apt-get update && sudo apt-get install -y makepasswd
fi
```

`makepasswd` is a tool used to generate random passwords. It doesn't come preinstalled in most Linux/Unix systems. So, you'd have to check if it is installed before moving on to other parts. This block checks if `makepasswd` is installed and installs it if necessary. Ensuring this dependency is met is crucial for the script to function correctly.

## Password Generation Function
```bash
#--------------------------------------------
# Generate a random password of 16 characters
#--------------------------------------------
generate_password() {
    makepasswd --chars 16
}
```

This function uses `makepasswd` to generate a random password of 16 characters. Strong passwords are essential for security, and automating their generation helps maintain consistency and complexity.

## Clearing Previous Logs and Passwords
```bash
#--------------------------------------
# Clear previous log and password files
#--------------------------------------
sudo truncate -s 0 "$LOG_FILE"
sudo truncate -s 0 "$PASSWORD_FILE"
```

Before starting the onboarding process, the script clears previous log and password files. This ensures that the new run starts with clean files, preventing confusion with old data.

Note that if you use `echo "" | sudo tee "$LOGFILE" > /dev/null` or `echo "" | sudo tee "$PASSWORD_FILE" > /dev/null` instead, what you get instead will be a file with an empty line. Any lines appended to the file will begin from the next line, leaving an empty first line. Using truncate sets the file to whatever size you specify. And in this case, it's zero bytes which effectively clears the file"

## Reading and Processing the Configuration File
```bash
while IFS=';' read -r username groups; do

    #----------------------------------------
    # Remove leading and trailing whitespaces
    #----------------------------------------
    username=$(echo "$username" | xargs)
    groups=$(echo "$groups" | xargs)

    #-----------------
    # Skip empty lines
    #-----------------
    [ -z "$username" ] && continue

    #---------------------------------
    # Split the groups field by commas
    #---------------------------------
    IFS=',' read -ra group_array <<<"$groups"
```

The script reads the configuration file line by line, splitting each line into a username and groups. It goes further to remove any leading or trailing whitespace to ensure clean data processing. There's the added contingency to skip empty lines to prevent errors.

## Creating Users and Setting Passwords
```bash
    #---------------------------------
    # Check if the user already exists
    #---------------------------------
    if id "$username" &>/dev/null; then
        red_echo "The user $username already exists." | sudo tee -a "$LOG_FILE"
    else
        sudo useradd -m -s /bin/bash "$username" &&
            green_echo "The user $username has been created." | sudo tee -a "$LOG_FILE"

        #---------------------------
        # Generate a random password
        #---------------------------
        password=$(generate_password)

        #------------------------
        # Set the user's password
        #------------------------
        echo "$username:$password" | sudo chpasswd
        echo "$username:$password" | sudo tee -a "$PASSWORD_FILE"
    fi
```

This block checks if the user already exists. If the user does not exist, it creates the user with a home directory and Bash shell. A random password is generated and set for the user. The username and password are logged for record-keeping.

## Creating Primary Groups
```bash
    #--------------------------------------------------------
    # Create a primary group for the user if it doesn't exist
    #--------------------------------------------------------
    if ! getent group "$username" >/dev/null; then
        sudo groupadd "$username" &&
            green_echo "Primary group $username created." | sudo tee -a "$LOG_FILE"
    fi
```

For each new user, the script checks if a primary group with the same name exists. If not, it creates the group. This ensures that each user has a corresponding group, which is a common practice in Unix-like systems.

## Assigning Users to Additional Groups

```bash
    for group in "${group_array[@]}"; do
        if ! getent group "$group" >/dev/null; then
            sudo groupadd "$group" &&
                green_echo "Group $group created." | sudo tee -a "$LOG_FILE"
        fi
        sudo usermod -aG "$group" "$username" &&
            green_echo "User $username added to group $group." | sudo tee -a "$LOG_FILE"
    done
```

The script processes each group specified for the user. It checks if the group exists and creates it if necessary. It then adds the user to each group, ensuring they have the required permissions and access.

## Setting Home Directory Permissions

```bash
    #-------------------------------
    # Set home directory permissions
    #-------------------------------
    sudo chown -R "$username":"$username" "/home/$username"
    sudo chmod 700 "/home/$username"

done <"$EMPLOYEE_CONFIG_FILE"

green_echo "User onboarding script completed. See $LOG_FILE for details."
```

Finally, the script sets the ownership and permissions for the user's home directory. This ensures that the user has the necessary access to their files while also securing the directory from unauthorized access.

To run the script, make it executable.

```bash
chmod +x create_users.sh
```
Then execute it:

```bash
./create_users.sh new_developers.txt
```
So the script runs as intended, ensure that the file the script reads is written and formatted this way (username to the left of the semi-colon and user groups to the right):

```
light; sudo,dev,www-data
idimma; sudo
mayowa; dev,www-data
```

The assumption behind this example is that a user can have multiple groups, each group delimited by a comma ",".

Here's the [link to the GitHub repo](https://github.com/GideonIsBuilding/hng-task_01) if you want to take a look at it closely.

# Final Words...
When you automate the user onboarding process with a Bash script ensures consistency, accuracy, and security. By following the steps outlined in this article, you can streamline the creation of user accounts, assignment of groups, and setting of secure passwords, all while maintaining detailed logs for auditing purposes. 

To improve the security of the script, you can:

1. Hash the passwords after generation
2. Configure the script to send each employee their login details to their emails

Tweak and improve as desired. 

Looking for projects like this to help you better understand DevOps concepts, enroll for [HNG Internship](https://hng.tech/internship). If you are already a professional looking for a community of professionals to collaborate with and have recruiters review your resumes, then opt for the [HNG Premium](https://hng.tech/premium) package.