Automating Creation of Users and groups using Bash Script (Step-by-Step Guide)
Tags: devops, linux, learning, bash
Published: 2024-07-03 00:44:10

As a DevOps engineer, it is quite a common task to create, update, and delete users and groups. Automating this process can save a lot of time and help reduce human errors, especially when onboarding numerous users. In this article, I'll walk you through using bash to automate the creation of users and their respective groups, setting up home directories, generating random passwords, and logging all actions performed.

**Objectives**
- Create users and their personal groups. 
- Add users to specified groups.
- Set up home directories with appropriate permissions.
- Generate and securely store passwords.
- Log all actions for auditing purposes.
- Ensure error handling.

## Let's Begin
First and foremost, at the beginning of any shell script we're writing, we start with **"shebang"** in the first line.

```
#!/bin/bash
```
Shebang tells us that the file is executable.

## Setting up the directory for user logs and file
It is necessary to create a directory and files for logging and password storage. If it already exists, DO NOT CREATE IT AGAIN!!!

```
mkdir -p /var/log /var/secure
touch /var/log/user_management.log
touch /var/secure/user_passwords.txt
```
- **/var/log:** Directory for log files.
- **/var/secure:** Directory for secure files.
- **user_management.log:** Log file to record actions.

## Secure the generated passwords 

```
chmod 600 /var/secure/user_passwords.txt
```
- **user_passwords.txt:** File to store passwords securely with read and write permissions for the file owner only.

Earlier, we created the directory "`/var/secure`" to store `user_password.txt`. To give the appropriate permissions, we use `chmod 600` to give only the current user access to view our generated passwords.

## Create a function to log actions
The `log_action()` function record actions with timestamps in the log file.

```
log_action() {
    echo "$(date) - $1" >> "/var/log/user_management.log"
}
```

## User Creation Function
The `create_user()` function handles the creation of user accounts and their associated groups. It takes two arguments: username and groups.

Steps taken in the `create_user()` function:

- Check for Existing User: Logs a message if the user already exists.

```
if id "$user" &>/dev/null; then
    log_action "User $user already exists."
    return
fi
```
`id "$user" &>/dev/null` checks if the user exists by attempting to retrieve the user's information. If the user does not exist, `id` will return a non-zero exit status. The `&>/dev/null` part redirects any output (both stdout and stderr) to `/dev/null`, effectively silencing the command's output.
If the user exists `log_action "User $user already exists."` logs a message indicating that the user already exists. Using `return`, it exits the `create_user()` function early, preventing any further actions from being taken for the existing user.
- Create User Group: Creates a personal group for the user.

```
groupadd "$user"
```
`groupadd "$user"` creates a new group with the same name as the user. This group will be the primary group for the user being created.


- Handle Additional Groups: Checks and creates additional groups if they do not exist.

```
IFS=' ' read -ra group_array <<< "$groups"
log_action "User $user will be added to groups: ${group_array[*]}"
for group in "${group_array[@]}"; do
    group=$(echo "$group" | xargs)  # Trim whitespace
    if ! getent group "$group" &>/dev/null; then
        groupadd "$group"
        log_action "Group $group created."
    fi
done
```
Here, we split the `groups` string into an array using spaces as delimiters and log the groups that the user will be added to. Using `for group in "${group_array[@]}"; do`, we are able to iterates over each group in the `group_array` and trim any leading or trailing whitespace from the group name. Using `if ! getent group "$group" &>/dev/null; then` to check if the group already exists else we create the group `groupadd "$group"` and log that the group has been created `log_action "Group $group created."`.

- Create User: Creates the user with a home directory and bash shell.

```
useradd -m -s /bin/bash -g "$user" "$user"
if [ $? -eq 0 ]; then
    log_action "User $user created with primary group: $user"
else
    log_action "Failed to create user $user."
    return
fi
```

- Assign Groups: Adds the user to additional groups.

```
for group in "${group_array[@]}"; do
    usermod -aG "$group" "$user"
done
log_action "User $user added to groups: ${group_array[*]}"
```
`for group in "${group_array[@]}"; do` to iterate over each group in the `group_array` then adds the user to the specified group (`-aG` means append the user to the group). Next, log that the user has been added to the specified groups.

- Generate and Store Password: Generates a random password and stores it securely.
```
password=$(</dev/urandom tr -dc A-Za-z0-9 | head -c 12)
echo "$user:$password" | chpasswd

echo "$user,$password" >> "/var/secure/user_passwords.txt"
```
We use `/dev/urandom` to generate a random 12-character password and `tr` to filter alphanumeric characters. Next, we set the password for the user using the `chpasswd` command and append the username and password to the secure file `/var/secure/user_passwords.txt`.

- Set Permissions: This part of the code is responsible for setting the appropriate permissions and ownership for the user's home directory.
```
chmod 700 "/home/$user"
chown "$user:$user" "/home/$user"
```
Using `700`, only the user has read, write, and execute permissions. `chown "$user:$user" "/home/$user"` to set the owner and group of the home directory to the user.

Here is the full combined code within the `create_user()` function.
```
create_user() {
    local user="$1"
    local groups="$2"
    local password

    if id "$user" &>/dev/null; then
        log_action "User $user already exists."
        return
    fi

    groupadd "$user"

    IFS=' ' read -ra group_array <<< "$groups"
    log_action "User $user will be added to groups: ${group_array[*]}"
    for group in "${group_array[@]}"; do
        group=$(echo "$group" | xargs)
        if ! getent group "$group" &>/dev/null; then
            groupadd "$group"
            log_action "Group $group created."
        fi
    done

    useradd -m -s /bin/bash -g "$user" "$user"
    if [ $? -eq 0 ]; then
        log_action "User $user created with primary group: $user"
    else
        log_action "Failed to create user $user."
        return
    fi

    for group in "${group_array[@]}"; do
        usermod -aG "$group" "$user"
    done
    log_action "User $user added to groups: ${group_array[*]}"

    password=$(</dev/urandom tr -dc A-Za-z0-9 | head -c 12)
    echo "$user:$password" | chpasswd
    echo "$user,$password" >> "/var/secure/user_passwords.txt"

    chmod 700 "/home/$user"
    chown "$user:$user" "/home/$user"

    log_action "Password for user $user set and stored securely."
}

```

## Main Execution Flow
```
if [ $# -ne 1 ]; then
    echo "Usage: $0 <user_list_file>"
    exit 1
fi

filename="$1"

if [ ! -f "$filename" ]; then
    echo "Users list file $filename not found."
    exit 1
fi

while IFS=';' read -r user groups; do
    user=$(echo "$user" | xargs)
    groups=$(echo "$groups" | xargs | tr -d ' ')
    groups=$(echo "$groups" | tr ',' ' ')
    create_user "$user" "$groups"
done < "$filename"

echo "Creation of user is complete. Go ahead and check /var/log/user_management.log for detailed information."
```
Here, we check if the user file is provided, then read the user list file and process each user entry, calling the `create_user()` function for each line and passing the `$user` and `$groups` as arguments.

## Let's put everything together

```
#!/bin/bash

# Create directory for user logs and passwords
mkdir -p /var/log /var/secure

# Create logs file and passwords file
touch /var/log/user_management.log
touch /var/secure/user_passwords.txt

# Make the password file secure for the file owner (read and write permissions for file owner only)
chmod 600 /var/secure/user_passwords.txt

# Create a function to record actions to log file
log_action() {
    echo "$(date) - $1" >> "/var/log/user_management.log"
}

create_user() {
    local user="$1"
    local groups="$2"
    local password

    # Check if user already exists by logging user information with id
    if id "$user" &>/dev/null; then
        log_action "User $user already exists."
        return
    fi

    # Create personal group for the user
    groupadd "$user"

    # Create additional groups if they do not exist
    IFS=' ' read -ra group_array <<< "$groups"
    log_action "User $user will be added to groups: ${group_array[*]}"
    for group in "${group_array[@]}"; do
        group=$(echo "$group" | xargs)  # Trim whitespace
        if ! getent group "$group" &>/dev/null; then
            groupadd "$group"
            log_action "Group $group created."
        fi
    done

    # Create user with home directory and shell, primary group set to the personal group
    useradd -m -s /bin/bash -g "$user" "$user"
    if [ $? -eq 0 ]; then
        log_action "User $user created with primary group: $user"
    else
        log_action "Failed to create user $user."
        return
    fi

    # Add the user to additional groups
    for group in "${group_array[@]}"; do
        usermod -aG "$group" "$user"
    done
    log_action "User $user added to groups: ${group_array[*]}"

    # Generate password and store it securely in a file
    password=$(</dev/urandom tr -dc A-Za-z0-9 | head -c 12)
    echo "$user:$password" | chpasswd

    # Store user and password securely in a file
    echo "$user,$password" >> "/var/secure/user_passwords.txt"

    # Set permissions and ownership for user home directory
    chmod 700 "/home/$user"
    chown "$user:$user" "/home/$user"

    log_action "Password for user $user set and stored securely."
}

# Check if user list file is provided
if [ $# -ne 1 ]; then
    echo "Usage: $0 <user_list_file>"
    exit 1
fi

filename="$1"

if [ ! -f "$filename" ]; then
    echo "Users list file $filename not found."
    exit 1
fi

# Read user list file and create users
while IFS=';' read -r user groups; do
    user=$(echo "$user" | xargs)
    groups=$(echo "$groups" | xargs | tr -d ' ')

    # Replace commas with spaces for usermod group format
    groups=$(echo "$groups" | tr ',' ' ')
    create_user "$user" "$groups"
done < "$filename"

echo "Creation of users is complete. Go ahead and check /var/log/user_management.log for detailed information."
```

## Usage
To use this script, provide a user list file as an argument. Each line in the file should contain a username and a comma-separated list of groups (optional), separated by a semicolon. For example:

```
light; sudo,dev,www-data
idimma; sudo
mayowa; dev,www-data
```
To run the script, use this command.
```
./script_name.sh user_list_file.txt
```
If you are not a root user, use `sudo ./create_users.sh user_list_file.txt`

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9jhs11zw8wihpi5dc9ak.png)


## Conclusion
This script provides a robust solution for automating user creation and management, ensuring secure handling of passwords and detailed logging of actions. It simplifies the process of setting up new user accounts while maintaining security and auditability.

To learn more and kickstart your programming journey you can visit:
https://hng.tech/internship or https://hng.tech/premium

Feel free to reach out with questions or suggestions for improvements. Happy automating!😁👌