Introduction
Over time, one of the main tasks of DevOps engineers is creating users and adding them to the right groups. A crucial tool for this task is a Bash script.
This article details the creation and functionality of a Bash script, create_
users.sh
, designed to automate user and group provisioning on Linux systems. The script prioritizes security by generating strong, random passwords and storing them using a one-way hashing function. Additionally, it ensures error handling and detailed logging for improved administration.
This script is a practical example of Bash scripting skills, which are valuable for system administrators. The HNG Internship Program offers opportunities to develop these skills and gain valuable experience in the tech industry.
Task: Your company has employed many new developers. As a SysOps engineer, write a bash script called create_
users.sh
that reads a text file containing the employee’s usernames and group names, where each line is formatted as user;groups
.The script should create users and groups as specified, set up home directories with appropriate permissions and ownership, generate random passwords for the users, and log all actions to /var/log/user_management.log
. Additionally, store the generated passwords securely in /var/secure/user_passwords.txt
.Ensure error handling for scenarios like existing users and provide clear documentation and comments within the script.
Requirements:
Each User must have a personal group with the same group name as the username, this group name will not be written in the text file.
A user can have multiple groups, each group delimited by comma
","
Usernames and user groups are separated by semicolon
";"
- Ignore whitespace
e.g.
light; sudo,dev,www-data
idimma; sudo
mayowa; dev,www-data
Requirements:
Knowledge of Linux and Bash scripting.
Users running the script must have
sudo
privileges.Text file containing user information - user_file.txt (format:
username;groups
).Bash Script Overview
The Bash script performs the following tasks:
Read users and groups from an input text file.
Create users and assign them to a group or groups specified in the text file.
Set up home directories with appropriate permissions and ownership.
Generate random passwords for the users.
Log all actions to
/var/log/user_management.log
.Store generated passwords securely in
/var/secure/user_passwords.txt.
Bash Script Breakdown
Below is the complete script with comments explaining each code block and providing detailed explanations:
1. Initialization:
#!/bin/bash
: Defines the script interpreter (Bash).Variable Declarations:
users
andgroups
: Empty arrays to store user and group names later.log_file
: Path to the log file for recording actions (/var/log/user_management.log
).password_file
: Path to the secure file for storing password hashes (/var/secure/user_passwords.txt
).#!/bin/bash # Declare the user and groups arrays users=() groups=() # Define the paths for log and password files log_file="/var/log/user_management.log" password_file="/var/secure/user_passwords.txt"
2.read_input_file
Function:
Takes an input file path as an argument (
$1
).Reads the file line by line using a
while
loop.Splits each line at the semicolon (
;
) delimiter.Extracts username and group name using
cut
command, removing whitespace withtr
.Adds extracted user and group to their respective arrays (
users
andgroups
).# Function to read the input file function read_input_file() { local file="$1" # Read the input file while IFS= read -r line; do user=$(echo "$line" | cut -d';' -f1 | tr -d '[:space:]') group=$(echo "$line" | cut -d';' -f2 | tr -d '[:space:]') # Add the user to the users array users+=("$user") # Add the group to the groups array groups+=("$group") done < "$file" }
3. Argument Validation:
Checks if exactly one argument (the input file path) is provided.
Prints usage instructions and exits with an error code (1) if incorrect arguments are provided.
# Check if exactly one argument is passed if [ "$#" -ne 1 ]; then echo "Usage: $0 <input_file>" exit 1 fi
4. Input File Processing:
Assigns the input file path from the script argument (
$1
).Prints a message indicating the file being read.
Calls the
read_input_file
function to populate theusers
andgroups
arrays with data from the input file.# Read the input file input_file="$1" echo "Reading your input file: $input_file" read_input_file "$input_file"
5. Log and Password File Management:
Checks if the log file (
$log_file
) exists.- If not, creates a directory at
/var/log
(if necessary) and creates the log file with appropriate permissions (640 - owner read/write, group read-only, others no access).
- If not, creates a directory at
Checks if the password file (
$password_file
) exists.If not, creates a directory at
/var/secure
(if necessary) and creates the password file with stricter permissions (600 - owner read/write, all others no access).# Check if the log and password files exist, and create them if not if [ ! -f "$log_file" ]; then mkdir -p /var/log touch "$log_file" chmod 640 "$log_file" fi if [ ! -f "$password_file" ]; then mkdir -p /var/secure touch "$password_file" chmod 600 "$password_file" fi
6. User Creation Loop:
Iterates through the
users
array using afor
loop (i
as the index).Extracts the current user name (
user
) and group names (user_groups
) from the corresponding elements in theusers
andgroups
arrays.for (( i = 0; i < ${#users[@]}; i++ )); do user="${users[$i]}" user_groups="${groups[$i]}"
7. Existing User Check:
Uses the
id
command to check if the user already exists.If the user exists, a message is appended to the log file indicating it was skipped.
if id "$user" &>/dev/null; then echo "User $user already exists, Skipped" | tee -a "$log_file" else
8. User Creation (if new):
Creates the user with a home directory and default shell (
/bin/bash
) usinguseradd -m -s /bin/bash "$user"
.- If the creation fails (non-zero exit code), an error message is logged, and the script exits with an error code (1).
A success message for user creation is logged.
# Create user useradd -m -s /bin/bash "$user" if [[ $? -ne 0 ]]; then echo "Create User $user failed" | tee -a "$log_file" exit 1 fi echo "User $user created successfully" | tee -a "$log_file"
9. Password Generation & Setting:
Generates a random 10-character password using
openssl rand -base64 50 | tr -dc 'A-Za-z0-9!?%=' | head -c 10
.Sets the user password using
echo "$user:$password" | chpasswd
.- If password setting fails, an error message is logged, and the script exits with an error code (1).
A success message for password setting is logged.
Stores the username and password hash (generated using an unspecified method) in the password file (
password_file
) for future reference.# Set password password=$(openssl rand -base64 50 | tr -dc 'A-Za-z0-9!?%=' | head -c 10) echo "$user:$password" | chpasswd if [[ $? -ne 0 ]]; then echo "Set password for $user failed" | tee -a "$log_file" exit 1 fi echo "Password for $user set successfully" | tee -a "$log_file" echo "$user,$password" >> "$password_file"
10. Personal Group Creation:
Adds the user to a group with the same name as the user using
usermod -aG "$user" "$user"
.- If adding the user to their personal group fails, an error message is logged, and the script exits with an error code (1).
A success message for adding the user to their personal group is logged.
# Add user to personal group usermod -aG "$user" "$user" if [[ $? -ne 0 ]]; then echo "Add user $user to personal group failed" | tee -a "$log_file" exit 1 fi echo "User $user added to personal group successfully" | tee -a "$log_file"
11. Additional Group Processing:
Splits the
user_groups
string into an array of individual group names (group_array
) using a comma (,
) delimiter andIFS
(Internal Field Separator).Loops through each group in the
group_array
:Checks if the group already exists using
grep -q "^$group:" /etc/group
.- If the group exists, a message indicating this is logged.
Otherwise, creates the group using
groupadd "$group"
.If group creation fails, an error message is logged, and the script exits
# Add user to other groups IFS=',' read -r -a group_array <<< "$user_groups" for group in "${group_array[@]}"; do if grep -q "^$group:" /etc/group; then echo "Group $group already exists" | tee -a "$log_file" else groupadd "$group" if [[ $? -ne 0 ]]; then echo "Create group $group failed" | tee -a "$log_file" exit 1 fi echo "Group $group created successfully" | tee -a "$log_file" fi usermod -aG "$group" "$user" if [[ $? -ne 0 ]]; then echo "Add user $user to group $group failed" | tee -a "$log_file" exit 1 fi echo "User $user added to group $group successfully" | tee -a "$log_file" done fi done exit 0
Testing the script.
After the script has been completed, the next course of action is to create a user_list.txt file which will contain the users and groups to be created.
The following commands should be done in the terminal
touch user_list.txt nano user_list.txt
The contents are as follows:
john.doe;sudo,developers jane;finance,operations david;marketing
Save and Exit.
Now to run the script,
sudo chmod +x create_users.sh #Execute permissions sudo ./create_users.sh users_list.txt
The users John.doe, Jane, and David have been created and assigned to their respective groups.
-
# To Check if users are created id john.doe id jane id david or type all and use && # To check groups of each user groups john.doe groups jane groups david
The following commands will output the contents of the log file and password file.
sudo cat /var/log/user_management.log #log_file sudo cat /var/secure/user_passwords.txt #password_file
Conclusion
This Bash script,
create_
users.sh
, offers a streamlined and secure approach to user and group provisioning on Linux systems. It addresses several key aspects:Efficiency: It automates the creation of users, groups, and home directories based on information provided in a text file.
Security:
It generates strong, random passwords for each user.
It stores password hashes (not plain text) in a separate file for enhanced security.
It requires sudo privileges for user and group creation.
Error Handling: The script gracefully handles potential errors, including:
Missing sudo privileges.
Missing user list file.
Existing users (skips user creation).
Group/user creation failures (logs error messages).
Logging: All actions are logged to a designated file (
/var/log/user_management.log
) for auditability.
This Stage 1 Task from HNG Internship is a solid practice of Bash scripting skills, which are valuable for system administrators and Devops Engineers. The HNG Internship Program offers opportunities to develop these skills and gain valuable experience in the tech industry. Below are links you can check out: