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 at your disposal for automating this task is Bash scripting.
This article will guide you through the process of creating a Bash script to manage user accounts on a Linux system.
This project was created during my time as an HNG intern; if you would like to be a part of this internship in the future or hire some amazing talents from its finalists, check out the official HNG website.
The problem statement
You are presented with a slight but famous problem.
Your company has employed many new developers. As a SysOps engineer, write a bash script that reads a text file containing the employee’s usernames and group names, where each line is formatted as username;groups.
The user can have multiple groups, which are the personal group named after the username and the groups specified after the semi-colon in each line of the text file (username; groups). You can also specify multiple groups in the text file, but they have to be separated by a comma, for example:
fleabag; dev, www-dark
The script should create users and groups as specified, set up home directories with appropriate permissions and ownership, and generate random passwords for the users.
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 solve this problem?
Creating the Project
For you to solve this problem, the Bash script must:
- Create a user and use the username to create a personal group for the user
- Create an additional group for the user if specified in the text file as such (user; group)
- Creates home directories for each user
- Generate random passwords for each user
- Log all actions taken by the script to
var/log/user_management.log
- Store all the users and their generated passwords in
/var/secure/user_passwords.csv
file
An example text file for the bash script
A text file will be fed into the bash script, and it contains the user and groups the script is looking to create.
To create this text file, navigate to your project’s root directory, and create a text file with the name text_file.txt
, and this file will contain:
villanelle; sudo, dev
eve; dev, www-data
You can replace the usernames and groups with whatever names you’d like.
Creating the Bash script
Next, an important piece is creating the Bash script that will create the users, assign them groups, log all the activity, etc.
Create a create_user.sh
file in your project’s root directory. In this file, write these commands to check if the user running the script is currently signed in as the root user. This is because the script will perform actions that require elevated privileges, such as creating users and groups, setting passwords, creating files, etc.
#!/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
Next, you need to verify that text_file.txt
was passed as an argument into the bash script. To perform this check, add these lines of code to your create_user.sh
file:
# Check if the file was passed into the script
if [ -z "$1" ]; then echo "Please pass the file parameter" exit 1
fi
The code above checks whether the first positional parameter is empty. If it is empty, it sends a message to the user to pass in a file parameter and then exits the script.
Next, you will create some environment variables that will hold the text_file.txt
, the logs file path (var/log/user_management.log
), and password file path (/var/secure/user_passwords.csv
).
To create these variables, add these lines of code to your create_user.sh
file.
# 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"
Next, create the log and password files and give them the necessary edit permissions with this command:
# 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
: Defines the permissions that state that only the user has read, write, and execute permissions for this/var/secure
directory -
touch $PASSWORD_FILE
: Creates a/var/secure/user_passwords.csv
file -
chmod 600 $PASSWORD_FILE
: Defines the permissions that state that only the user has read and write permissions for this/var/secure/user_passwords.csv
file
Next, create the log_message()
and the generate_password()
functions. These functions will create the log message for each action and the user passwords:
# Generate logs and passwords
log_message() { echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> $LOG_FILE
} generate_password() { openssl rand -base64 12
}
The functions above are:
-
log_message
: Thelog_message
function appends the current date and time to the log message it receives as a positional parameter. The function then writes this log out to the$LOG_FILE
-
generate_password
: This function generates a random password every time its called. To learn more about the OpenSSL library, check out the official OpenSSL documentation
Creating the users and groups
You have created variables holding the different file paths pointing to the log file, password file, and input file. You have also verified that the user is running this script as a superuser.
The next step is to loop through every entry in the input textfile, split these entries by usernames and group, then create the users, and their multiple groups in the bash script.
To create these users and groups, add these commands to your create_user.sh
file:
# 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"
The command above does the following:
- Read the Input File Line by Line:
while IFS=';' read -r username groups || [ -n "$username" ]; do
while …; do … done
:
This construct sets up a loop that will execute the commands within the
do
block for each line read from the input file-
IFS=';'
: Sets the Internal Field Separator (IFS) to ‘;’, meaning it will use the semicolon to split each line in the input file. -
read -r username groups
: Reads each line of the input file and splits it into two variables: username and groups. -
|| [ -n "$username" ]
: Ensures that the loop continues processing the last line even if it doesn’t end with a newline character.
-
Trimming Whitespace:
username=$(echo "$username" | xargs) groups=$(echo "$groups" | xargs)
-
xargs
: Removes leading and trailing whitespace from the username and groups variables. - Stores the new username, and groups variables without the leading and trailing spaces into a username and groups variable
-
Checking and Creating the Personal Group:
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
-
getent group "$username" &>/dev/null
: This checks if a group with the username exists. If it doesn’t, thegroupadd
command creates the group, and a log message is recorded.
It is important to reiterate that each user will have at least one group, with the first being a personal group named after the username. Any additional groups for the user are specified after the semicolon in the input text file.
-
The code block above creates the personal group.
Checking and Creating the User:
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
id -u "$username" &>/dev/null
: Checks if a user with the username exists. If the user does not exist,useradd
is used to create the user, and a log message is recorded. To learn more about the useradd command and all its available options, check out how to create users in Linux (useradd command).Checking and Assigning Groups:
if [ -n "$groups" ]; then IFS=',' read -r -a group_array <<< "$groups" for group in "${group_array[@]}"; do group=$(echo "$group" | xargs) if ! getent group "$group" &>/dev/null; then groupadd "$group" log_message "Created group $group." fi usermod -aG "$group" "$username" log_message "Added user $username to group $group." done fi
[ -n "$groups" ]
: Checks if the groups variable is not empty.IFS=',' read -r -a group_array <<< "$groups"
: Splits the groups variable by commas into an array group_array.Loop through
group_array
: For each group in the array:- Remove whitespace:
group=$(echo "$group" | xargs)
- Check if the group exists:
if ! getent group "$group" &>/dev/null
- If the group doesn’t exist, create it with
groupadd
.
- Remove whitespace:
Add the user to the group:
usermod -aG "$group" "$username"
Generating and Setting a User Password:
password=$(generate_password) echo "$username:$password" | chpasswd echo "$username,$password" >> $PASSWORD_FILE
generate_password
: Calls a function that generates a random password.echo "$username:$password" | chpasswd
: Sets the user’s password.echo "$username,$password" >> $PASSWORD_FILE
: Saves the username and password to a password file.Feeding the Input File into the While Loop:
done < "$INPUT_FILE"
-
done < "$INPUT_FILE"
: Feeds the content of$INPUT_FILE
into the while loop, line by line.
-
When you are done with this section, your create_user.sh
file should look like this:
#!/bin/bash
# Check if the current user is sudo, exit if it's not the superuser
if [ "$EUID" -ne 0 ]; then echo "Please run as root" exit 1
fi # Check if the file was passed into the script
if [ -z "$1" ]; then echo "Please pass the file parameter" exit 1
fi # Define the file paths for the logfile, and the password file
INPUT_FILE="$1"
LOG_FILE="/var/log/user_management.log"
PASSWORD_FILE="/var/secure/user_passwords.csv" # 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 # Generate logs and passwords
log_message() { echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> $LOG_FILE
} generate_password() { openssl rand -base64 12
} # 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"
With this, you have successfully created a script that effectively manages users and groups in your system.
Testing this script
To figure out if your script is working as expected, you need to run it on your terminal that supports Linux commands. Examples of these terminals are:
- Windows Subsystem for Linux (WSL)
- Git Bash
- A Linux virtual machine, etc.
In your terminal, run the script with this command:
./create_user.sh ./text_file.txt
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 for more resources on scripting with Bash for Linux systems.
Discover more from Coursity
Subscribe to get the latest posts sent to your email.