How To Automate User Creation With Bash Script

This article explains how to create users and assign them groups and passwords from a given text file.

Contents

Objectives

By the end of this tutorial, you will be able to:

  1. Create users with a bash script
  2. Create groups for these users
  3. Assign users to groups
  4. Generate and set passwords for users
  5. Log actions and manage permissions for security

Project Setup

  1. Create a .sh file named create_users.sh.
touch create_users.sh
Enter fullscreen mode
Exit fullscreen mode
  1. Create a text file named sample.txt with user and group data.
touch sample.txt
Enter fullscreen mode
Exit fullscreen mode

Bash Script

To get started, we first need to declare that this is a bash script using:

#!/bin/bash
Enter fullscreen mode
Exit fullscreen mode

This line tells the system to use the bash shell to interpret the script.

Next, we define our log file path and the password file path. This ensures we have paths to store logs and passwords securely:

# Define the log file and password file paths
LOGFILE="/var/log/user_management.log"
PASSWORD_FILE="/var/secure/user_passwords.csv"
Enter fullscreen mode
Exit fullscreen mode

The next step ensures the script is run with root permissions. If you don’t have the right permissions, the script will exit and inform you:

# Ensure the script is run with root permissions
if [[ $EUID -ne 0 ]]; then echo "This script must be run as root or with sudo" exit 1
fi
Enter fullscreen mode
Exit fullscreen mode

Here, [[ $EUID -ne 0 ]] checks if the effective user ID is not zero (i.e., not root). The effective user ID (EUID) represents the user identity the operating system uses to decide if you have permission to perform certain actions. If the EUID is not zero, it means you’re not running the script as the root user, and the script will print a message and exit with status 1.

We then define a function to log actions:

# Function to log actions
log_action() { local message="$1" echo "$message" | tee -a "$LOGFILE"
}
Enter fullscreen mode
Exit fullscreen mode

This function takes a message as an argument and appends it to the log file, also displaying it on the terminal.

Next, we check if the log file exists, create it if it doesn’t, and set the correct permissions:

# Check if the log file exists, create it if it doesn't, and set the correct permissions
if [[ ! -f "$LOGFILE" ]]; then touch "$LOGFILE" log_action "Created log file: $LOGFILE"
else log_action "Log file already exists, skipping creation of logfile '$LOGFILE'"
fi
log_action "Setting permissions for log file: $LOGFILE"
chmod 600 "$LOGFILE"
log_action "Successfully set"
Enter fullscreen mode
Exit fullscreen mode

Here, [[ ! -f "$LOGFILE" ]] checks if the log file does not exist. If true, it creates the file and logs the action. Then, it sets the permissions to read/write for the owner only (chmod 600).

We repeat a similar process for the password file:

# Check if the password file exists, create it if it doesn't, and set the correct permissions
if [[ ! -f "$PASSWORD_FILE" ]]; then touch "$PASSWORD_FILE" log_action "Created password file: $PASSWORD_FILE"
else log_action "Password file already exists, skipping creation of password file '$PASSWORD_FILE'"
fi
log_action "Setting permissions for password file: $PASSWORD_FILE"
chmod 600 "$PASSWORD_FILE"
log_action "Successfully set"
Enter fullscreen mode
Exit fullscreen mode

This ensures the password file is created if it doesn’t exist and sets the correct permissions for security.

Next, we define a function to generate random passwords:

# Function to generate random passwords
generate_password() { local password_length=12 tr -dc A-Za-z0-9 </dev/urandom | head -c $password_length
}
Enter fullscreen mode
Exit fullscreen mode

The local keyword is used to define a variable that is local to the function. This means that the variable password_length is only accessible with the generate_passsword function, preventing it from interfering with other parts of the script.

Let’s break down what tr -dc A-Za-z0-9 </dev/urandom | head -c $password_length does, shall we?

  • /dev/urandom: This is a special file in Unix-like systems that provides random data. It’s often used for generating cryptographic keys, passwords, or any other data requiring randomness.

  • tr -dc A-Za-z0-9: The tr command is used to translate or delete characters. Here:

    • -d tells tr to delete characters.
    • -c complements the set of characters, meaning it includes everything except the specified set.
    • A-Za-z0-9 specifies the set of allowed characters: uppercase letters (A-Z), lowercase letters (a-z), and digits (0-9).
    • </dev/urandom: Redirects the random data from /dev/urandom into the tr command.
  • | head -c $password_length: The head command is used to output the first part of files.

    • -c $password_length specifies the number of characters to output, defined by the variable password_length (which is 12). This ensures the generated password is exactly 12 characters long.
      This function uses tr to generate a random string of 12 characters from /dev/urandom.

We then define the create_user function, which creates a user and assigns groups:

create_user() { local username="$1" local groups="$2" log_action "Processing user: $username with groups: $groups" # Check if the user already exists if id "$username" &>/dev/null; then log_action "User $username already exists, skipping creation" return 1 fi # Create the user with a home directory useradd -m "$username" if [[ $? -ne 0 ]]; then log_action "Failed to create user: $username" return 1 fi # Create a personal group for the user groupadd "$username" if [[ $? -ne 0 ]]; then log_action "Failed to create group: $username" return 1 fi # Add the user to the personal group usermod -aG "$username" "$username" # Add the user to additional groups IFS=',' read -ra ADDR <<< "$groups" for group in "${ADDR[@]}"; do groupadd "$group" 2>/dev/null usermod -aG "$group" "$username" done # Set up home directory permissions chown -R "$username":"$username" "/home/$username" chmod 700 "/home/$username" # Generate a password and set it for the user password=$(generate_password) echo "$username:$password" | chpasswd # Log the user creation and password log_action "Created user: $username with groups: $groups" echo "$username,$password" >> "$PASSWORD_FILE"
}
Enter fullscreen mode
Exit fullscreen mode
  • if id "$username" &>/dev/null; then ...: This checks if the user exists then redirects both the standard output (stdout) and standard error (stderr) to /dev/null. /dev/null is a special device file that discards any data written to it.
  • useradd -m "$username": creates the user with a home directory. -m flag tells useradd to create a home directory for the user.
  • groupadd "$username": creates a personal group for the user.
  • usermod -aG "$username" "$username": adds the user to their personal group.-a flag stands for “append”. It tells usermod to append the user to the specified group(s) without removing them from any other groups. On the other hand, -G flag specifies that the following argument ("$username") is a list of supplementary groups which the user is also a member of.
  • IFS=',' read -ra ADDR <<< "$groups" splits additional groups by commas. Here the Internal Field Separator (IFS) means the shell will split strings into parts based on commas.
  • groupadd "$group" 2>/dev/null creates additional groups if they don’t exist.
  • chown -R "$username":"$username"“/home/$username” sets the ownership of the home directory. chown stands for change owner and the command recursively changes ownership for all files and subdirectories within /home/$username.
  • chmod 700 "/home/$username" sets permissions of the home directory. chmod stands for change mode and it changes file permission to 700. 7 grants read, write and execute permissions to the owner. 0 denies all permissions to others.
  • password=$(generate_password) generates a random password.
  • echo "$username:$password" | chpasswd sets the user’s password.
  • echo "$username,$password" >> "$PASSWORD_FILE" stores the username and password in the password file.

Finally, we define the function to read the file and create users:

# Define a function to read the file
read_file() { local filename="$1" # Check if the file exists if [[ ! -f "$filename" ]]; then log_action "File not found: $filename" return 1 fi # Read the file line by line while IFS= read -r line; do # Remove whitespace and extract user and groups local user=$(echo "$line" | cut -d ';' -f 1 | xargs) local groups=$(echo "$line" | cut -d ';' -f 2 | xargs) # Create the user and groups create_user "$user" "$groups" done < "$filename"
} # Call the function with the filename passed as a script argument
read_file "$1"
Enter fullscreen mode
Exit fullscreen mode
  • [[ ! -f "$filename" ]]: checks if the file exists. -f: This is a file test operator. It checks if the provided path (in this case, stored in the variable $filename) exists and is a regular file.
  • while IFS= read -r line; do ... done < "$filename": reads the file line by line.
  • local user=$(echo "$line" | cut -d ';' -f 1 | xargs): extracts the username by using cut to split the line into fields (-f) based on the delimiter (-d) semicolon (;). -f 1 selects the first field. xargs trims any leading or trailing whitespace and ensures that the result is treated as a single entity.
  • local groups=$(echo "$line" | cut -d ';' -f 2 | xargs): extracts the groups.
  • create_user "$user" "$groups": creates the user and assigns groups.

This section of the script reads a file passed as an argument and processes each line to create users and assign groups.

Test

To test the code simply run the following command in your terminal:

bash create_users.sh sample.txt
Enter fullscreen mode
Exit fullscreen mode

That’s it! You can find the full code in my repository.

Cheers!

[This article was written in completion of the HNG Internship stage one DevOps task. Learn more about the program here.]


Discover more from Coursity

Subscribe to get the latest posts sent to your email.

Leave a Comment

Your email address will not be published. Required fields are marked *

Discover more from Coursity

Subscribe now to keep reading and get access to the full archive.

Continue reading

Scroll to Top