How to Create Linux Users and Groups with Bash: A Beginner's Guide
Automate User and Group Management on Linux with Bash Scripting: A Step-by-Step Guide for Beginners
As a DevOps professional, your key responsibility is to automate repetitive tasks that may slow down the software development process, such as repetitive manual configurations, complex deployment processes, and error-prone environment setups. You may also be responsible for managing and monitoring the system performance of the team you find yourself in.
One of the tools that help you with this automation is Bash Scripting. Bash scripting is a scripting language on the Bourne Again Shell that IT specialists use to create commands that they save in a text file and execute on the command line. Think of bash scripting as a way to create shortcuts to long, boring, or strenuous tasks.
In this guide, you will learn how to build a bash script that creates users, separates these users into groups, and generates usernames and passwords for these users outlined in a text file.
Prerequisites
To follow this tutorial, you will need:
Basic understanding of programming syntax and terminal commands
Bash should be running on your Linux system. To confirm that your default shell is bash, use the command:
echo $SHELL
If you have bash running, it should return:
/bin/bash
- You should have root access to your Linux system.
Logic of this Script
This script will accept and read user input in .txt format.
It will create personalized usernames, passwords, and groups for each user in the text file.
It will also set up home directories for each user.
It will ensure the users’ passwords are secure by logging each password in an already specified log file.
It will also record all actions in a log file.
It will handle errors such as existing users.
It will echo a process completion message to the terminal after execution.
Writing the Script
Creating Script File
The first step in creating this script is to make a script file and specify Bash as the correct interpreter for this script. We make this specification using a Shebang. To remove ambiguity, we will call our script file 'create_users.'
touch create_users.sh
With a single command, we have created our script file. Now, open the script in a text editor and start typing our script beginning with our Shebang like so:
vim create_users.sh
Check for Input from the User
Using an if-statement
, we will check if our user has passed any argument to our script. If they have not, it displays the correct way to run our script. We use the following command to accomplish this:
if [ "$#" -ne 1 ]; then
echo "Usage: $0 <name-of-text-file>"
exit 1
fi
In simple terms, we have declared to our script that if the number of arguments ($#) passed by the user is not exactly (-ne) one, echo to the user the accurate usage, which is the name of our script file ($0) and the name of a text file.
After that declaration, we exit the statement with a status code of 1 to stop our code from running further if the condition in our program did not execute successfully.
In Unix-like systems, we use exit followed by a non-zero value to indicate an error. In standard practice, DevOps professionals use the number 1 in bash scripts to signify an error in the system.
Define the Variables for Input, Log and Password Files
In this next step, we have to create containers that will hold the text file from the user, the log file that keeps track of all the actions our script will run, and the passwords of users specified in the text file.
# Input file
INPUT_FILE=$1
The first line of our script here takes the first argument ($1) the user passes to our script and stores it in a variable. In the next few lines, we create the log and password files in the /var
directory.
In Linux systems, we use the /var
directory to store files that are constantly changing, like management or temporary files. Additionally, organizations use the /var/secure
directory to store sensitive information and set permissions to this directory to prevent unauthorized users from accessing the files in it.
Ensure Log and Password Files Exist and Are Writable
In our previous script, we specified the file path for the log and passwords. In this step, we will confirm that our files exist in our system. If they do not exist, we create them. We also specify what administrative rights we will give the user. Lastly, we will ensure that our users can modify these files. The script goes thus:
# Ensure log and password files exist and are writable
touch $LOG_FILE
chmod 600 $LOG_FILE
mkdir -p /var/secure
touch $PASSWORD_FILE
chmod 600 $PASSWORD_FILE
The touch command creates our log file first. Then, we allow our user to read and write to this file using the chmod
command.
To handle permissions in Linux, we use chmod
; which is short for 'change mode.' In Linux, users, groups, and everybody else can be given specific access rights to write, read, or execute files and directories. To learn more about file permissions in Linux, read this article here.
Generate a Random Password
The goal of this script is to create multiple users and assign usernames and passwords to each of them. To achieve the goal of generating passwords, we used this command:
# Function to generate a random password
generate_password() {
echo $(openssl rand -base64 12)
}
In our script, we created a function that uses the OpenSSL tool to create 12 bytes of random data and encrypt this data in base 64. With this script, we are able to generate a random string of letters and numbers for our user and print those characters to the terminal using the echo command.
Create User and Groups
So far, we have created the base for our script. Now, we enter the central point of our script by creating the users and splitting them into groups. Additionally, we will create our script with the understanding that a user can belong to numerous groups. The script below executes this function:
# Function to create user and groups
create_user_and_groups() {
local username=$1
local groups=$2
local password=$(generate_password)
# Create a personal group for the user
if ! getent group $username > /dev/null 2>&1; then
groupadd $username
echo "Group $username created" >> $LOG_FILE
else
echo "Group $username already exists" >> $LOG_FILE
fi
Here, we created a mini program that stores the usernames and groups we get from the text file in a variable, assigns the position of the first and second arguments ($1 and $2) to them, and stores the passwords we generate in a variable.
The local command attached to each variable specifies to our code that the values of our variables should not be accessible outside our function.
With if ! getent group $username > /dev/null 2>&1;
then we are telling our script to check if a group with the specified username already exists. If it does not exist, with the groupadd $username
we create a new group, print this action to our terminal and log it in our log file. Else, we tell the user that the group already exists.
In this script, we also referenced the standard redirection command /dev/null 2>&1.
This script sends the result of the condition we specified in our if-statement
to a 'black hole.' That way, we avoid displaying unnecessary output and keep our code cleaner. To learn more about Linux redirections, check out this article by Digital Ocean.
# Create additional groups
# Create additional groups
IFS=',' read -ra ADDR <<< "$groups"
for group in "${ADDR[@]}"; do
group=$(echo $group | xargs) # trim whitespaces
if ! getent group $group > /dev/null 2>&1; then
groupadd $group
echo "Group $group created" >> $LOG_FILE
else
echo "Group $group already exists" >> $LOG_FILE
fi
# Add user to the group
sudo usermod -aG "$group" "$username"
echo "Added $username to $group" >> $LOG_FILE
done
# Create the user
if ! id -u $username > /dev/null 2>&1; then
useradd -m -g $username -G $groups -s /bin/bash $username
echo "$username:$password" | chpasswd
echo "User $username created with home directory and added to groups $groups" >> $LOG_FILE
# Store the password securely
echo "$username:$password" >> $PASSWORD_FILE
else
echo "User $username already exists" >> $LOG_FILE
usermod -a -G $groups $username
echo "$username:$password" | chpasswd
echo "User $username updated with groups $groups" >> $LOG_FILE
# Update the password securely
sed -i "/^$username:/d" $PASSWORD_FILE
echo "$username:$password" >> $PASSWORD_FILE
fi
}
The user writes the text file in this format: light; sudo,dev,www-data.
Therefore, we use IFS=',' read -ra ADDR <<< "$groups"
we tell our script to read the input and split the string using the comma as a separator. After separating the string, it saves it in the ‘groups’ variable and sends it to the array 'ADDR.'
Now, we create a for loop that goes through each item in our array and removes any leading or trailing or leading spaces with group=$(echo $group | xargs)
, checks if the group already exists and if not, creates it as we did above.
Next, with the useradd -m -g $username -G $groups -s /bin/bash $username
command, our script checks if the user exists and if not, creates the user, creates a home directory for the user, -g $username
sets the initial login group to the same as the username. Then, -G $groups
adds the user to additional groups listed in the variable $groups
, -s /bin/bash
sets the user's default shell to Bash, and secures their password duly.
For cases where the user may already exist, our else statement adds this user to other groups as one user can belong to multiple groups. Using the usermod -a -G $groups $username command
, we modify the user’s account by appending the additional groups our script specifies.
In the next few lines of code, we update the user’s password and log all the changes we made in the log file. Lastly, our function updates the username and new password in the password file for added security.
Read the Input File and Process Each Line
Finally, we have to loop through the input file to execute the scripts that we have written so far. To do this, we use the command below:
#Read the input file and process each line
while IFS=';' read -r username groups; do
username=$(echo $username | xargs) # trim whitespaces
groups=$(echo $groups | xargs) # trim whitespaces
create_user_and_groups $username $groups
done < $INPUT_FILE
echo "User creation process completed. Check $LOG_FILE for details and $PASSWORD_FILE for passwords."
Using a while loop, we examine each item in the input file and assign the first part of the string to a username and the second part to the group variable. We trim the whitespaces and our user creation process is complete. We record this detail in the log file and print a completion command to the terminal.
Make Sure the Script is Executable
At this point, we have finished writing our script and we need to make our script executable. Otherwise, our user cannot get the file to act as we have programmed it. To do this we use:
chmod +x create_users.sh
Significance of Automating Users and Groups in DevOps
Sometimes, DevOps engineers working in corporate environments have to manage users and groups on multiple servers. By writing automation scripts like what we have written in this article, these engineers can significantly reduce the risk of human error that occurs while working with large data manually.
A simple but practical example is a case where a company hires new sets of developers. The developers will need personalized environments in the company’s system. Instead of adding each developer as well as the groups they belong to one at a time, we can run a similar script–saving time and ensuring quality work.
By following this guide, you've learned how to create a Bash script that automates the creation of users and groups on a Linux system. This script can save you significant time and effort by automating repetitive tasks, ensuring secure password generation, and maintaining proper logging of actions.
You can find the complete script and contribute to its development on GitHub. Happy scripting!