A Practical Script for SysOps Engineers
In the SysOps engineer world, automation is key, especially for repetitive tasks that save valuable time and ensure consistency. As a SysOps engineer, knowing how to write robust and efficient scripts along with strong Linux skills, boosts productivity and gives you a better workflow. This article will walk you through a bash script designed to automate the creation of Linux users.
The script creates users and groups as specified, sets up home directories with appropriate permissions and ownership, it generates random passwords for the users, and logs all actions to /var/log/user_management.log
. Additionally, it stores the generated passwords securely in /var/secure/user_passwords.txt
. and covers error handling for scenarios like existing users.
Prerequisite:
- Bash shell environment.
- Text editor for editing the script and preparing the input file.
Let's have a look at our script which we will later use to accomplish this task, and break it down by a detailed explanation of each section:
#!/bin/bash
# Check if the script is run as root
if [[ "$(id -u)" -ne 0 ]]; then
echo "You must run the script as root" >&2
exit 1
fi
# Check if a filename is provided
if [[ -z "$1" ]]; then
echo "Usage: $0 <name-of-text-file>" >&2
exit 1
fi
# File paths
LOG_FILE="/var/log/user_management.log"
PASSWORD_FILE="/var/secure/user_passwords.csv"
# Ensure /var/secure storage for passwords
mkdir -p /var/secure
touch $PASSWORD_FILE
chown root:root /var/secure
chmod 700 /var/secure
chmod 600 $PASSWORD_FILE
# Function to log actions
log_action() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a $LOG_FILE
}
# Function to generate a random password
generate_password() {
tr -dc 'A-Za-z0-9!@#$%^&*()_+=-[]{}|;:<>,.?/~' </dev/urandom | head -c 16
}
# Function to hash passwords
hash_password() {
echo "$1" | openssl passwd -6 -stdin
}
# Read the input file line by line
while IFS=";" read -r username groups; do
# Trim whitespace
username=$(echo "$username" | xargs)
groups=$(echo "$groups" | xargs)
# Skip empty lines
if [[ -z "$username" ]]; then
continue
fi
# Check if user already exists
if id "$username" &>/dev/null; then
log_action "User $username already exists!"
continue
fi
# Create personal group
if groupadd "$username"; then
log_action "Group $username created successfully."
else
log_action "Failed to create group $username."
continue
fi
# Create user with a home directory and personal group
if useradd -m -s /bin/bash -g "$username" "$username"; then
log_action "User $username created successfully."
else
log_action "Failed to create user $username."
continue
fi
# Create groups and add user to them
IFS=',' read -ra group_array <<< "$groups"
for group in "${group_array[@]}"; do
group=$(echo "$group" | xargs)
if ! getent group "$group" >/dev/null 2>&1; then
if groupadd "$group"; then
log_action "Group $group created."
else
log_action "Failed to create group $group."
continue
fi
fi
if usermod -aG "$group" "$username"; then
log_action "User $username added to group $group."
else
log_action "Failed to add user $username to group $group."
fi
done
# Generate random password
password=$(generate_password)
hashed_password=$(hash_password "$password")
if usermod --password "$hashed_password" "$username"; then
echo "$username,$password" >> $PASSWORD_FILE
log_action "Password set for user $username."
else
log_action "Failed to set password for user $username."
fi
# Set home directory permissions
if mkdir -p "/home/$username" && chown -R "$username:$username" "/home/$username" && chmod 755 "/home/$username"; then
log_action "Home directory permissions set for user $username."
else
log_action "Failed to set home directory permissions for user $username."
fi
done < "$1"
log_action "User creation process completed. Check $LOG_FILE for details."
Breaking Down the Script
1. Check for Root Privileges
if [[ "$(id -u)" -ne 0 ]]; then
echo "You must run the script as root" >&2
exit 1
fi
This part checks if the script is run by the root user, this is because only the root user has permission to create and manage other users. If the script is not run as root, it will print an error message and exit.
2. Check for Filename Argument
if [[ -z "$1" ]]; then
echo "Usage: $0 <name-of-text-file>" >&2
exit 1
fi
This section will check if the user provided a filename as an argument when running the script. If no filename is found, it will print a usage message and exit.
3. Define File Paths
LOG_FILE="/var/log/user_management.log"
PASSWORD_FILE="/var/secure/user_passwords.csv"
These lines define the file paths for logging actions and storing passwords.
4. Ensure Secure Storage for Passwords
mkdir -p /var/secure
touch $PASSWORD_FILE
chown root:root /var/secure
chmod 700 /var/secure
chmod 600 $PASSWORD_FILE
This part of the script ensures that the /var/secure
directory and the password file exist with appropriate permissions for secure storage. mkdir -p
creates the directory if it doesn't exist, touch
creates the password file, and chmod
and chown
set the permissions so only the root user can read and write to it.
5. Logging Function
log_action() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a $LOG_FILE
}
The log_action
function's job is to log messages with timestamps to the log file. It uses the date
command to add a timestamp to each log entry and tee
to append the message to the log file.
6. Password Generation Function
generate_password() {
tr -dc 'A-Za-z0-9!@#$%^&*()_+=-[]{}|;:<>,.?/~' </dev/urandom | head -c 16
}
The generate_password
function generates a random password. tr
is used to filter characters from /dev/urandom
(a random number generator), and head -c 16
ensures the password is 16 characters long.
7. Password Hashing Function
hash_password() {
echo "$1" | openssl passwd -6 -stdin
}
The hash_password
function hashes the generated password using the openssl
command with SHA-512 encryption. This makes the password secure before storing it.
8. Read and Process Input File
while IFS=";" read -r username groups; do
# Trim whitespace
username=$(echo "$username" | xargs)
groups=$(echo "$groups" | xargs)
# Skip empty lines
if [[ -z "$username" ]]; then
continue
fi
This part reads the input file line by line, separating the username and groups by the semicolon (;
). It also trims any whitespace around the username and groups and skips any empty lines.
9. Check for Existing User
if id "$username" &>/dev/null; then
log_action "User $username already exists!"
continue
fi
This checks if the user already exists. If so, it logs the action and skips to the next user.
10. Create Personal Group
if groupadd "$username"; then
log_action "Group $username created successfully."
else
log_action "Failed to create group $username."
continue
fi
This creates a personal group with the same name as the username.
11. Create User
if useradd -m -s /bin/bash -g "$username" "$username"; then
log_action "User $username created successfully."
else
log_action "Failed to create user $username."
continue
fi
This creates the user with a home directory and assigns the personal group as the primary group.
12. Create and Assign Groups
Copy code
IFS=',' read -ra group_array <<< "$groups"
for group in "${group_array[@]}"; do
group=$(echo
How to use the script?
Save the script as create_users.sh in your preferred directory.
Prepare a text file (users_and_groups.txt) with one username per line, followed by groups separated by commas. For example: light;sudo,dev,www-data idimma;sudo mayowa;dev,www-data
Make the script executable with chmod +x create_users.sh.
Run the script with the path to your text file as an argument: ./create_users.sh users_and_groups.txt.
Conclusion
By following the structured steps outlined above, SysOps engineers can efficiently manage large numbers of user accounts and their associated groups, providing a robust solution for onboarding new developers.
Acknowledgment
This script was developed as part of the HNG Internship DevOps track. For more details on the program, visit the HNG Internship Website.
You can learn more about the HNG Internship program here and here.
Thank you for reading.
Top comments (1)
cool, thanks