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.
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:
- Read User and Group Information: The script will rely on a text file containing user and group information
- Create User and Group: For each user specified in the file, the script will create a user account and a personal group
-
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 - Create Home Directories: Each user will have a dedicated home directory created.
-
Generate Random Passwords: Secure random passwords will be generated for each user and stored in
/var/secure/user_passwords.csv
-
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:
#!/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:
# 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:
# 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:
# 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:
# 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
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:
# 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:
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 thedo
block for each of the line-
IFS=';'
: Sets the Internal Field Separator (IFS) 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
andgroups
. 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:
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:
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:
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 theuseradd
command.Checking and Assigning Groups:
# 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 namedgroup_array
. -
for
loop: This iterates through each group name in thegroup_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, thegroupadd
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:
password=$(generate_password)
echo "$username:$password" | chpasswd
echo "$username,$password" >> $PASSWORD_FILE
Explanation:
-
password=$(generate_password)
: Calls thegenerate_password()
function created earlier and stores its output in apassword
variable -
echo "$username:$password" | chpasswd
: Sets the user's password using thepassword
variable echo "$username,$password" >> $PASSWORD_FILE
: Saves the username and password to the password file at the pathvar/secure/user_passwords.csv
Feeding the Input File into the While Loop:
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:
- Running the Script in a Linux Environment
You'll need a terminal that supports Linux commands to run the script. Some options include:
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:
Next, check your /var/log/user_management.log
file to see your logs by running this command:
cat /var/log/user_management.log
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
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.