Automating user creation with Bash Scripting
Tags: bash, linux, devops, sysops
Published: 2024-07-04 11:23:41

Managing users and groups is a fundamental aspect of system administration. Whether you're overseeing a small personal server or a large enterprise network, having a streamlined process for user creation is essential for maintaining security, organization, and efficiency. One of the most powerful tools for automating this task is Bash scripting.

This article walks you through solving user management issues with a Bash script. The script will cover creating accounts, assigning them to personal and general groups, and managing and securing their passwords.

This project is available on GitHub. Check out the [repository for the complete script](https://github.com/Iheanacho-ai/User-creation-script).

## The problem statement

You are presented with a problem.

Your company has hired many new developers, and you need to automate the creation of user accounts and passwords for each of them.

As a SysOps engineer, write a Bash script that reads a text file containing the employees’ usernames and group names, where each line is formatted as `username;groups`.

The text file can also specify multiple groups for the user, formatted as `username; group1, group2`.

In addition to the multiple groups specified by the text file, each user must have a personal group named after their username.

The script should create users and groups as specified, set up home directories with appropriate permissions and ownership, and generate random user passwords.

Additionally, store the generated passwords securely in `/var/secure/user_passwords.csv`, and log all actions to `/var/log/user_management.log`.

How do you automate this workflow with Bash scripting?

## Setting up the project

Before diving head first into creating the script itself, let's define what it needs to automate. The script must:

1. **Read User and Group Information:** The script will rely on a text file containing user and group information
2. **Create User and Group:** For each user specified in the file, the script will create a user account and a personal group
3. **Assign Additional Groups:** If additional groups are listed for a user (e.g., `user;group1,group2`), the script will also assign the user to those groups
4. **Create Home Directories:** Each user will have a dedicated home directory created.
5. **Generate Random Passwords:** Secure random passwords will be generated for each user and stored in `/var/secure/user_passwords.csv`
6. **Log Actions:** The script will log all its activities to `/var/log/user_management.log`


**Creating the user and group text file**
The Bash script relies on a text file to define the users and groups it needs to create.

To create this text file, navigate to your project’s root directory and create a file named `text_file.txt`. This file should contain lines formatted as follows:

```
    villanelle; sudo, dev
    eve; dev, www-data
```

You can replace the usernames and groups with any names you’d like.

Now that you have the text file prepared let's create the Bash script that interacts with it.

**Creating the Bash script**
Within your project's root directory, create a new file named `create_user.sh`. This script will handle user creation, group assignment, activity logging, and more.

This script will be made up of different parts, and these are:


1.**Ensuring root privileges**

The script requires elevated privileges to perform actions like creating users, groups, and modifying permissions. So, you will begin by checking if the user running the script has root access.
In your `create_user.sh` file, add the following commands to check if the user is root:

```bash
    #!/bin/bash
    # Check if the current user is a superuser, exit if the user is not
    if [ "$EUID" -ne 0 ]; then
      echo "Please run as root"
      exit 1
    fi
```

This code ensures only users with root access can execute the script. If you're not logged in as root, running the script will display an error message and exit.

2.**Check if the text file was passed in as an argument**

Next, you need to ensure the script receives the text file (`text_file.txt`) as an argument. To perform this check, add the following lines of code to your `create_user.sh` file:

```bash
    # Check if the file was passed into the script
    if [ -z "$1" ]; then 
        echo "Please pass the file parameter"
        exit 1
    fi
```

In the code block above, these commands:

- `[ -z "$1" ]`: This checks if the first argument ($1) is empty.
- `echo "Please pass the file parameter"`: This message informs the user if the script is missing the required file.
- `exit 1`: Exits the script with an error code if the check fails.

By including this code, you guarantee the script receives the necessary text file to function correctly.


3.**Create environment variables for the file**

Next, you will create environment variables to hold the paths for the input text file (`text_file.txt`), the log file (`/var/log/user_management.log`), and the password file (`/var/secure/user_passwords.csv`).
To create these variables, add the following lines of code to your `create_user.sh` file:

```bash
    # Define the file paths for the log file, and the password file
    INPUT_FILE="$1"
    LOG_FILE="/var/log/user_management.log"
    PASSWORD_FILE="/var/secure/user_passwords.csv"
```

4.**Create the log and password files**

Next, create the log and password files and give them the necessary permissions with this command:

```bash
    # Generate logfiles and password files and grant the user the permissions to edit the password file
    touch $LOG_FILE
    mkdir -p /var/secure
    chmod 700 /var/secure
    touch $PASSWORD_FILE
    chmod 600 $PASSWORD_FILE
```

The commands in the code block above are:

- `touch $LOG_FILE`: Creates a `/var/log/user_management.log` file
- `mkdir -p /var/secure`: Creates a `/var/secure` directory that will hold the password file
- `chmod 700 /var/secure`: Sets the permissions so that only the user has read, write, and execute permissions for the `/var/secure` directory
- `touch $PASSWORD_FILE`: Creates a `/var/secure/user_passwords.csv` file
- `chmod 600 $PASSWORD_FILE`: Sets the permissions so that only the user has read and write permissions for the `/var/secure/user_passwords.csv` file


5.**Generate the passwords**

Next, create the `log_message()` and `generate_password()` functions. These functions will handle creating log messages for each action and generating user passwords:

```bash
    # Generate logs and passwords 
    log_message() {
        echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> $LOG_FILE
    }
    
    generate_password() {
        openssl rand -base64 12
    }
```

Here's what each function does:

- `log_message()`: This function appends a log message with the current date and time (formatted as `%Y-%m-%d %H:%M:%S`) to the `$LOG_FILE`. It takes a positional parameter `$1 representing` the message to be logged.
- `generate_password()`: This function uses OpenSSL to generate a random password each time it is called, which is then printed to standard output.

To learn more about the OpenSSL library, refer to the [official OpenSSL documentation](https://www.openssl.org/docs/)


6.**Creating the users and groups**

You have verified that the user is running this script as a superuser. Additionally, you have created variables that hold different file paths pointing to the log, password, and input files.

Next, the script should loop through each entry in the input text file, split these entries by usernames and groups, and then create the users and their respective groups.

To achieve this, include the following commands in your `create_user.sh` file:

```bash
    # Read the input file line by line and save them into variables
    while IFS=';' read -r username groups || [ -n "$username" ]; do
        username=$(echo "$username" | xargs)
        groups=$(echo "$groups" | xargs)
        # Check if the personal group exists, create one if it doesn't
        if ! getent group "$username" &>/dev/null; then
            echo "Group $username does not exist, adding it now"
            groupadd "$username"
            log_message "Created personal group $username"
        fi
    
        # Check if the user exists
        if id -u "$username" &>/dev/null; then
            echo "User $username exists"
            log_message "User $username already exists"
        else
    
            # Create a new user with the created group if the user does not exist
            useradd -m -g $username -s /bin/bash "$username"
            log_message "Created a new user $username"
        fi
    
        # Check if the groups were specified
        if [ -n "$groups" ]; then
            # Read through the groups saved in the groups variable created earlier and split each group by ','
            IFS=',' read -r -a group_array <<< "$groups"
            # Loop through the groups 
            for group in "${group_array[@]}"; do
    
                # Remove the trailing and leading whitespaces and save each group to the group variable
                group=$(echo "$group" | xargs) # Remove leading/trailing whitespace
                # Check if the group already exists
                if ! getent group "$group" &>/dev/null; then
                    # If the group does not exist, create a new group
                    groupadd "$group"
                    log_message "Created group $group."
                fi
    
                # Add the user to each group
                usermod -aG "$group" "$username"
                log_message "Added user $username to group $group."
            done
        fi
    
        # Create and set a user password
        password=$(generate_password)
        echo "$username:$password" | chpasswd
        # Save user and password to a file
        echo "$username,$password" >> $PASSWORD_FILE
    done < "$INPUT_FILE"
    
    log_message "User created successfully"
    
    echo "Users have been created and added to their groups successfully"

```
Let’s break down the code snippet to understand what each part does:


- **Read the Input File Line by Line:**

```bash    
   while IFS=';' read -r username groups || [ -n "$username" ]; do
       # Code to create the user groups
    done < “$INPUT_FILE”
```
**Explanation:**

- `while …; do … done`: This loop iterates over each line in the input field and executes the commands within the `do` block for each of the line
    - `IFS=';'`: Sets the [Internal Field Separator (IFS)](https://unix.stackexchange.com/questions/184863/what-is-the-meaning-of-ifs-n-in-bash-scripting) to a semicolon (;). This tells the read command to split each line in the input file based on semicolons.
    - `read -r username groups`: Reads each line of the input file and splits it into two variables: `username` and `groups`. You will need these variables for creating usernames and groups
    - `|| [ -n "$username" ]`: This ensures that the loop continues processing the last line even if it doesn't end with a newline character.

    
- **Trimming Whitespace:**

```bash
    username=$(echo "$username" | xargs)
    groups=$(echo "$groups" | xargs)
```

**Explanation:**
    - This step removes any leading or trailing spaces from the `username` and `groups` variables. Extra spaces can cause issues with user and group creation.
    - `xargs`: This command removes any whitespace at the beginning or end of the variable values.
    
- **Checking and Creating the Personal Group:**

```bash
    if ! getent group "$username" &>/dev/null; then
      echo "Group $username does not exist, adding it now"
      groupadd "$username"
      log_message "Created personal group $username"
    fi
```

**Explanation:**
    - `getent group "$username" &>/dev/null`: This checks if a group with the username exists. If it doesn't, the `groupadd` command creates the group, and a log message is recorded.
    
- **Checking and Creating the User:**

```bash
    if id -u "$username" &>/dev/null; then
      echo "User $username exists"
      log_message "User $username already exists"
    else
      useradd -m -g $username -s /bin/bash "$username"
      log_message "Created a new user $username"
    fi
```

**Explanation:**

- `id -u "$username" &>/dev/null`: This checks if a user with the specified username exists. If the user does not exist, the useradd function is used to create one, and a log message is recorded.


- `useradd -m -g "$username" -s /bin/bash "$username"`: This command creates a new user with the specified name (`$username`), a home directory (`-m`), the user's name as the primary group (`-g $username`), and the Bash shell as the default login shell (`-s /bin/bash`). For more details, check out [how to create users in Linux using the `useradd` command](https://linuxize.com/post/how-to-create-users-in-linux-using-the-useradd-command/).



- **Checking and Assigning Groups:**

```bash
     # Check if the groups were specified
    if [ -n "$groups" ]; then
        # Read through the groups saved in the groups variable created earlier and split each group by ','
        IFS=',' read -r -a group_array <<< "$groups"

        # Loop through the groups 
        for group in "${group_array[@]}"; do
            # Remove the trailing and leading whitespaces and save each group to the group variable
            group=$(echo "$group" | xargs) # Remove leading/trailing whitespace

            # Check if the group already exists
            if ! getent group "$group" &>/dev/null; then
                # If the group does not exist, create a new group
                groupadd "$group"
                log_message "Created group $group."
            fi

            # Add the user to each group
            usermod -aG "$group" "$username"
            log_message "Added user $username to group $group."
        done
    fi

```
**Explanation:**

- `[ -n "$groups" ]`: This checks if the `$groups` variable is not empty
- `IFS=',' read -r -a group_array <<< "$groups"`: This splits the `$groups` variable by commas, storing each group name as a separate element in an array named `group_array`.
- `for` loop: This iterates through each group name in the `group_array` and runs the following commands:
    - `group=$(echo "$group" | xargs)`: This removes any leading or trailing spaces from the current group name in the loop.
    - `if ! getent group "$group" &>/dev/null`: This checks if a group exists. If the group does not exist, the `groupadd` function creates the group, and a log message is recorded.
- `usermod -aG "$group" "$username"`: This command adds the user (`$username`) to the current group (`$group`)


- **Generating and Setting a User Password:**

```bash
    password=$(generate_password)
    echo "$username:$password" | chpasswd
    echo "$username,$password" >> $PASSWORD_FILE
```    

**Explanation:**

- `password=$(generate_password)`: Calls the `generate_password()` function created earlier and stores its output in a `password` variable
- `echo "$username:$password" | chpasswd`: Sets the user's password using the `password` variable
- `echo "$username,$password" >> $PASSWORD_FILE`: Saves the username and password to the password file at the path `var/secure/user_passwords.csv`


- **Feeding the Input File into the While Loop**:

```bash
    done < "$INPUT_FILE"
```
 **Explanation:**
    - `done < "$INPUT_FILE"`: This redirection operator, `<`, tells the while loop to read its input from the file specified by `$INPUT_FILE`


When you are done with this section, your `create_user.sh` file should look like this:


https://gist.github.com/Iheanacho-ai/a572c299428c68e309b549d8d4d4cb4e

With this, you have successfully created a script that effectively manages users and groups in your system.

## Testing this script

Once you've written your script, it's time to verify that it works as intended. Here's how to test it:


1. **Running the Script in a Linux Environment**

You'll need a terminal that supports Linux commands to run the script. Some options include:

- [Windows Subsystem for Linux (WSL)](https://ubuntu.com/desktop/wsl)
- [Git Bash](https://git-scm.com/downloads)
- [A Linux virtual machine](https://www.redhat.com/en/topics/virtualization/what-is-a-virtual-machine#:~:text=Linux%20containers%20and%20virtual%20machines,the%20rest%20of%20the%20system.)


2.**Making the Script Executable**

Before running the script, you need to grant it execute permission with this command:

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

3.**Running the Script**

Next, execute the script with this command:

```
    ./create_user.sh ./text_file.txt
```
If your script is running as it should, you should see this in your terminal:

![](https://paper-attachments.dropboxusercontent.com/s_DF2D4B371372CCF8FEECCA2C9FBE5B56C3A32A198F67E92F574E6C90BC984A94_1720190754904_Screenshot+2024-07-05+at+15.43.48.png)


Next, check your `/var/log/user_management.log` file to see your logs by running this command:


    cat /var/log/user_management.log
![](https://paper-attachments.dropboxusercontent.com/s_DF2D4B371372CCF8FEECCA2C9FBE5B56C3A32A198F67E92F574E6C90BC984A94_1720190805609_Screenshot+2024-07-05+at+15.44.30.png)


Finally, check your `/var/secure/user_passwords.csv` file with this command to see the users and their passwords:

    cat /var/secure/user_passwords.csv
![](https://paper-attachments.dropboxusercontent.com/s_DF2D4B371372CCF8FEECCA2C9FBE5B56C3A32A198F67E92F574E6C90BC984A94_1720190889214_Screenshot+2024-07-05+at+15.45.07.png)

## In summary

Throughout this article, you have walked through using Bash to manage users, groups, and their passwords. You have also logged all the actions into a file so that you and anybody else can look back on them and troubleshoot. But this is only the tip of the iceberg when it comes to Bash's capabilities.

With Bash, you can automate a wide variety of administrative tasks, streamline your workflows, and enhance the efficiency of your system management. Whether it's scheduling regular maintenance tasks with cron jobs, managing system updates, or monitoring system performance, Bash provides a powerful 
toolset for system administrators.

So stay curious and check out [Bash resources](https://linuxcommand.org/lc3_resources.php) for more resources on scripting with Bash for Linux systems.