User Management Automation in Linux Using Bash Script
Tags: bash, automation, linux, devops
Published: 2024-07-03 02:27:10

## Scenario
As a SysOps engineer, you have been tasked to write a bash script that creates Users, Groups, and home directories with appropriate permissions for new employees in your company.

This article will walk you through writing the bash script to automate the process of creating users and groups in a Linux system.

## Solution
### Pre-requisites
1. An Ubuntu server
2. Basic Linux knowledge 
3. User with sudo privileges

### Script Overview
The script `create_users.sh` reads a text file containing usernames and group names, creates the specified users and groups, sets up home directories, generates random passwords for each user, and logs all actions. It is important to run the script with a user with sudo privileges as using your root user is not considered best practice. The script takes the input file as a command-line argument.

### Breakdown of the create_users.sh script
#### Initial Checks
The script starts with some important checks:
```bash
# Function to check if user has sudo privileges
check_sudo() {
    if sudo -n true 2>/dev/null; then
        return 0
    else
        return 1
    fi
}

# Check if user has sudo privileges
if [ ! check_sudo ]; then
    echo "This script requires sudo privileges to run"
    echo "Please run this script with sudo or as a user with sudo privileges"
    exit 1
fi

# Check if input file is provided
if [ $# -eq 0 ]; then
    echo "Please provide an input file"
    echo "Usage: $0 <input-file>"
    exit 1
fi
```
These checks ensure that the script is run with root privileges and that an input file is provided.

#### Directory and File Setup
```bash
input_file=$1
log_file="/var/log/user_management.log"
password_csv="/var/secure/user_passwords.csv"
password_file="/var/secure/user_passwords.txt"
password_dir="/var/secure"

#create the password directory if it doesn't exist
if [ ! -d $password_dir ]; then
    sudo mkdir $password_dir
fi

# Create log file, password csv, and password file and set permissions
sudo touch $log_file $password_csv $password_file
sudo chmod 600 $password_csv $password_file
```
The variables denoting the various files and directories are defined. The input_file containing the user and group names is assigned **$1** denoting that it is the first command-line argument supplied.

The if statement checks whether the directory where the user passwords will be stored exists and creates the directory if it doesn’t .

The password files are given restrictive permissions (600) to ensure only the owner can read it.

#### Helper Functions
Two helper functions are defined:
```bash
# Function to log actions
log() {
    echo "$(date): $1" | sudo tee -a "$log_file" > /dev/null
}

# Function to generate random password
generate_password() {
    openssl rand -base64 12
}
```
The `log` function appends timestamped messages to the log file, while `generate_password` creates a random 12-character password.

#### Reading the input file and Error Handling
The input file is read line-by-line using the `while` loop
```bash
#set line number to zero. to be used in error handling for invalid inputs
line_number=0

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

    #increment the line number after reading each line
    line_number=$((line_number + 1))

    # Check that the username and group is present
    if [[ -z "$username" || -z "$groups" ]]; then
        log "Error: Invalid input on line $line_number. Ensure Username and groups are provided"
        echo "Error: Invalid input on line $line_number. Ensure Username and groups are provided"
        continue
    fi

    # Check if user already exists
    if id "$username" &>/dev/null; then
        log "User $username already exists. Skipping."
        continue
    fi

    # Create user's personal group
    sudo groupadd $username
    log "Created personal group $username"

    # Create user with home directory with appropriate ownership and permissions to allow only the user read, write, and execute
    sudo useradd -m -g $username $username
    sudo chmod 700 "/home/$username"
    sudo chown "$username:$username" "/home/$username"
    log "Created user $username with home directory"

    # Set random password for user
    password=$(generate_password)
    echo "$username:$password" | sudo chpasswd
    echo "$username,$password" | sudo tee -a $password_csv > /dev/null
    echo "$username,$password" | sudo tee -a $password_file > /dev/null
    log "Set password for user $username"

    # Add user to additional groups
    IFS=',' read -ra group_array <<< "$groups"
    for group in "${group_array[@]}"; do
        group=$(echo $group | xargs)
        # Create group if it doesn't exist
        if ! getent group $group &>/dev/null; then
            sudo groupadd $group
            log "Created group $group"
        fi
        sudo usermod -a -G $group $username
        log "Added user $username to group $group"
    done

done < "$input_file"
```
Before the loop, a variable called `line_number` is defined as 0. This variable will be used as a form of error handling to note any lines in the input file that does not have a username or a group defined.

`IFS=';'` sets the field separator to semicolon because the user and group names are separated by a semicolon. `read -r username groups` reads a line, splitting it into username and groups variables. Leading and trailing whitespaces are removed from username and groups using `xargs`.

`line_number=$((line_number + 1))` increases by 1 at the start of each iteration in order to reference the current line number in the error logs and notifications.

The script then goes on to check if the username or groupname is present in the file and sends an error message to the log file and also notifies you on the command line about which line has the error. The script then goes on to the next line to run the script. It also checks whether or not a user has already been created and skips that user to the next.

After that it creates the user's personal group, creates the user with a home directory and permissions set to 700 to only allow the owner access to it, and sets a random password for the user.

The script also handles multiple group assignments in the instance where a user is to join multiple groups. This is separated in the input file by a comma delimiter. The script loops through the array of `groups` and creates the groups if they don't exist and adds the user to each specified group.

### Usage
To use the script, run:
```bash
sudo chmod +x create_users.sh
./create_users.sh input_file.txt
```
Where input_file.txt contains lines in the format 
```
username;group1,group2,group3
```

### Security Considerations
1. The user running the script must have sudo privileges
2. Passwords are stored in a file with restricted permissions.
3. Random passwords are generated using OpenSSL for better security.

## Conclusion
This bash script provides a powerful and flexible way to automate user and group creation in Linux systems. By handling various scenarios and providing detailed logging, it simplifies the process of managing multiple user accounts, making it an invaluable tool for system administrators.

This is a practical example of the skills developed at [HNG](https://hng.tech/hire), where participants learn to create efficient, scalable solutions for real-world problems.
Register for the [HNG internship](https://hng.tech/internship) to develop these skills.