This content originally appeared on DEV Community and was authored by Great-Victor Anjorin
Automating user creation and management can save time, reduce errors, and enhance security for SysOps engineers. In this article, we will go over a Bash script that automates the creation of users and groups, sets up home directories, and manages passwords securely. The script reads from a text file where each line consists of a username and their corresponding groups, logs every action in a log file, and saves the randomly generated passwords for each user in a secure .csv
file accessible only to the owner.
Let's dive into the bash script and break it down step by step.
Line-by-Line Explanation
1.) First off, we have our shebang.
#!/bin/bash
This specifies the type of interpreter script will be run with. Since it is a "bash" script, it should be run with the Bourne Again Shell (Bash) interpreter. Also, some commands in the script may not be interpreted correctly outside of Bash.
2.) The paths for the log file and the password file are set to avoid unnecessary repetition in the script.
# Define log and password storage files
LOG_FILE="/var/log/user_management.log"
PASSWORD_FILE="/var/secure/user_passwords.csv"
3.) To ensure that the bash script runs with root privileges, an if statement checks if the Effective User ID (EUID) is equal to zero. The EUID determines the permissions the script will use to run, and 0 represents the root user ID in Linux systems. Only users with administrative privileges (users who can use sudo or the root user itself) can run the script. If someone attempts to run it without such privileges, an error message and the script's run process will be terminated.
# Check if the script is run with root privileges
if [[ $EUID -ne 0 ]]; then
echo "This script must be run with root privileges." >&2
exit 1
fi
4.) To ensure that an input file is provided as an argument when running the script, this if
statement will terminate the script if no argument is provided. In this statement, $#
represents the argument provided when running the script. If it is equal to zero (no argument is provided) or if it is greater than or equal to 2, an error message will be printed and the script's execution is halted.
# Check if the input file is provided
if [[ $# -eq 0 || $# -ge 2 ]]; then
echo "Usage: $0 <user_file>" >&2
exit 1
fi
5.) Next is the log_action
function that records logs using bold lettering (formatted with ANSI escape codes: \033[1m
and \033[0m
) and a timestamp (using the date
command to get the current date and the specified date format: '%Y-%m-%d %H:%M:%S'
). This function is used to log important steps, success messages, and error messages in the script.
# Log function
log_action() {
echo "--------------------------------------------------" | tee -a "$LOG_FILE"
echo -e "$(date +'%Y-%m-%d %H:%M:%S') - \033[1m$1\033[0m" | tee -a "$LOG_FILE"
echo "--------------------------------------------------" | tee -a "$LOG_FILE"
}
6.) Next is the create_user_account
function that manages the entire process of creating a user, setting up their home directories with appropriate permissions and ownership, adding them to specified groups, and assigning randomly generated passwords. Every important step is logged.
create_user_account
function
create_user_account() {
local username="$1"
local groups="$2"
log_action "Creating user account '$username'..."
# Check if user already exists
if id "$username" &> /dev/null; then
echo "User '$username' already exists. Skipping..." | tee -a "$LOG_FILE"
return 1
fi
# Create user with home directory and set shell
if useradd -m -s /bin/bash "$username"; then
echo "User $username created successfully." | tee -a "$LOG_FILE"
else
echo "Error creating user $username." | tee -a "$LOG_FILE"
return 1
fi
# Create user group if it does not exist (in case the script is run in other linux distributions that do not create user groups by default)
if ! getent group "$username" >/dev/null; then
groupadd "$username"
usermod -g "$username" "$username"
log_action "Group $username created."
fi
# Set up home directory permissions
echo "Setting permissions for /home/$username..." | tee -a "$LOG_FILE"
chmod 700 "/home/$username" && chown "$username:$username" "/home/$username"
if [[ $? -eq 0 ]]; then
echo "Permissions set for /home/$username." | tee -a "$LOG_FILE"
else
echo "Error setting permissions for /home/$username." | tee -a "$LOG_FILE"
return 1
fi
# Add user to additional groups (comma separated)
echo "Adding user $username to specified additional groups..." | tee -a "$LOG_FILE"
IFS=',' read -ra group_array <<< "$groups"
for group in "${group_array[@]}"; do
group=$(echo "$group" | xargs)
# Check if group exists, if not create it
if ! getent group "$group" &>/dev/null; then
if groupadd "$group"; then
echo "Group $group did not exist. Now created." | tee -a "$LOG_FILE"
else
echo "Error creating group $group." | tee -a "$LOG_FILE"
continue
fi
fi
# Add user to group
if gpasswd -a "$username" "$group"; then
echo "User $username added to group $group." | tee -a "$LOG_FILE"
else
echo "Error adding user $username to group $group." | tee -a "$LOG_FILE"
fi
done
# Log if no additional groups are specified
if [[ -z "$groups" ]]; then
echo "No additional groups specified." | tee -a "$LOG_FILE"
fi
# Generate random password, set it for the user, and store it in a file
echo "Setting password for user $username..." | tee -a "$LOG_FILE"
password=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 12)
echo "$username:$password" | chpasswd
if [[ $? -eq 0 ]]; then
echo "Password set for user $username." | tee -a "$LOG_FILE"
echo "$username,$password" >> "$PASSWORD_FILE"
else
echo "Error setting password for user $username. Deleting $username user account" | tee -a "$LOG_FILE"
userdel -r "$username"
return 1
fi
}
- In the function, I initially set local variables to hold the values of the specified username and groups, to avoid repetition.
local username="$1"
local groups="$2"
- In this next section, I use the
log_action
function to record the start of each user account creation. Additionally, I verify whether the user already exists. If the user does exist, an error message is displayed and the script's execution is stopped.
log_action "Creating user account '$username'..."
# Check if user already exists
if id "$username" &> /dev/null; then
echo "User '$username' already exists. Skipping..." | tee -a "$LOG_FILE"
return 1
fi
- Next, there is an
if
statement that uses theuseradd
command with the-m
and-s
flags to create a user with a login shell (In this case, the login shell is set to be/bin/bash
. If you want, you can modify or remove the-s /bin/bash
part entirely) and assigns a home directory to the user. It stops the script's run process if an error occurs during the execution of the command.
# Create user with home directory and set shell
if useradd -m -s /bin/bash "$username"; then
echo "User $username created successfully." | tee -a "$LOG_FILE"
else
echo "Error creating user $username." | tee -a "$LOG_FILE"
return 1
fi
- Next, in the case that the script is run on other Linux distributions that do not create and assign a primary group with the same name as the newly created user, the
if
statement here will create a group with the same name as the user and add it as the user's primary group.
# Create user group if it does not exist (in case the script is run in other linux distributions that do not create user groups by default)
if ! getent group "$username" >/dev/null; then
groupadd "$username"
usermod -g "$username" "$username"
log_action "Group $username created."
fi
- In this section of the function, the newly created user is designated as the owner of the newly created home directory. The owner is also granted all possible permissions for the directory. This is all done using the
chmod
andchown
commands. If this process is unsuccessful, an error message is printed and the execution is halted.
# Set up home directory permissions
echo "Setting permissions for /home/$username..." | tee -a "$LOG_FILE"
chmod 700 "/home/$username" && chown "$username:$username" "/home/$username"
if [[ $? -eq 0 ]]; then
echo "Permissions set for /home/$username." | tee -a "$LOG_FILE"
else
echo "Error setting permissions for /home/$username." | tee -a "$LOG_FILE"
return 1
fi
- In this section, the newly created user is added to additional groups specified in the input file. By setting the Internal Field Separator (IFS) to expect comma-separated values and using the
read
command with the-ra
flags, the groups are individually placed inside an array calledgroup_array
to be used in the subsequentfor
loop. Within the loop, for every value in the group_array, thexargs
command removes any whitespace, creates the group if it does not exist, and finally adds the user to the group using thegpasswd
command with the-a
flag. In the case where no group is specified for the user in the input file, a message will be printed.
# Add user to additional groups (comma separated)
echo "Adding user $username to specified additional groups..." | tee -a "$LOG_FILE"
IFS=',' read -ra group_array <<< "$groups"
for group in "${group_array[@]}"; do
group=$(echo "$group" | xargs)
# Check if group exists, if not create it
if ! getent group "$group" &>/dev/null; then
if groupadd "$group"; then
echo "Group $group did not exist. Now created." | tee -a "$LOG_FILE"
else
echo "Error creating group $group." | tee -a "$LOG_FILE"
continue
fi
fi
# Add user to group
if gpasswd -a "$username" "$group"; then
echo "User $username added to group $group." | tee -a "$LOG_FILE"
else
echo "Error adding user $username to group $group." | tee -a "$LOG_FILE"
fi
done
# Log if no additional groups are specified
if [[ -z "$groups" ]]; then
echo "No additional groups specified." | tee -a "$LOG_FILE"
fi
- For the final section of the function, a random 12-character password is generated and set for the user. The head command collects a stream of random bytes from the
/dev/urandom
file. This stream is piped to thetr
command, which filters the bytes to include only alphanumeric characters (A-Z, a-z, 0-9) using the-dc
flag. The filtered result is then piped to another head command, which selects only the first 12 characters from the edited stream. The password is then set by piping the user's name and the randomly generated password to thechpasswd
command. The user and the generated password are saved in the designated password.csv
file. If setting the password fails, the script deletes the user account and logs the error, to avoid any possible security risk.
# Generate random password, set it for the user, and store it in a file
echo "Setting password for user $username..." | tee -a "$LOG_FILE"
password=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 12)
echo "$username:$password" | chpasswd
if [[ $? -eq 0 ]]; then
echo "Password set for user $username." | tee -a "$LOG_FILE"
echo "$username,$password" >> "$PASSWORD_FILE"
else
echo "Error setting password for user $username. Deleting $username user account" | tee -a "$LOG_FILE"
userdel -r "$username"
return 1
fi
7.) After creating the create_user_account function, the script processes a file containing user information and creates user accounts accordingly.
# Process the user file
user_file="$1"
while IFS=';' read -r username groups; do
if create_user_account "$username" "${groups%%[ ;]}"; then
log_action "User account '$username' created successfully."
else
log_action "Error creating user account '$username'."
fi
done < "$user_file"
- The script takes the user file as its argument and assigns it to the variable
user_file
. - A while loop reads each line of the user file. The
IFS=';'
part sets the Internal Field Separator to a semicolon (;), splitting each line at the semicolon. Theread -r
username groups part reads the split parts into the username and groups variables. - For each line in the file, the script calls the create_user_account function with the username and the groups (with trailing spaces removed using
${groups%%[ ;]}
). The script also logs a message if the result of thecreate_user_account
was a success or failure.
8.) After all that is done, the script gives only the owner (root) and those with root privileges access to the password file, logs the completion of the script's execution and prints the log file and password file location.
# Keep password file accessible only to those with root privileges
chmod 600 "$PASSWORD_FILE"
# Log completion
log_action "User creation script completed."
# Print log file and password file location
echo "Check $LOG_FILE for details."
echo "Check $PASSWORD_FILE for user passwords."
Prerequisites for running the script
- A Linux system
- A bash terminal (Optional. You can use any available shell terminal on the Linux system).
- Root privileges on your system.
- The text file containing the usernames and groups, must be formatted as
username;group1,group2
.
To run the script:
1.) Copy the file from or clone the repository HNG_Stage1
2.) Copy the text file containing the usernames and groups to the folder where the script is located.
3.) Then, in the directory where both the script and the text file are now located, run sudo ./create_users.sh <text file>
Conclusion
This script simplifies some administrative tasks. With such a script, SysOps engineers can automate user and group creation and management, allowing them to focus on more critical work while ensuring efficient and secure user management.
This is one of the project assignments in the HNG Internship program designed to enhance your resume and deepen your knowledge of bash scripting. For the best experience, visit HNG Premium.
This content originally appeared on DEV Community and was authored by Great-Victor Anjorin
Great-Victor Anjorin | Sciencx (2024-07-02T21:45:58+00:00) Automating User Creation and Management with Bash: A Step-by-Step Guide. Retrieved from https://www.scien.cx/2024/07/02/automating-user-creation-and-management-with-bash-a-step-by-step-guide/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.