DEV Community

Don-Fortune Tangban
Don-Fortune Tangban

Posted on

Linux User Creation Bash Script

Problem Statement

Your company hng 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.

Case Study

Let's start with a text file, developers.txt for hng hire:
light;sudo,dev,www-data
idimma;sudo
mayowa;dev,www-data

The above file lists the developers: light, idimma, and mayowa, with a list of groups separated by commas sudo, www-data, dev.

Our job now is to create a bash script that will take this file (developers.txt) and process it.

Solution

  1. #!/usr/bin/bash

    • The shebang line tells the system that this script should run with the bash shell.

  2. log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
    }

    • Defines a function log to log messages with timestamps to the log file specified by LOG_FILE.

  3. generate_password() {
    local password_length=8
    tr -dc A-Za-z0-9 </dev/urandom | head -c $password_length
    }

    • Defines a function to generate a random password for developers.

  4. if [ "$(id -u)" -ne 0 ]; then
    echo "Run script as root user or try using sudo " >&2
    log "Script not run as root or with sudo privileges"
    exit 1
    fi

    • Verifies that the script is run as root user.

  5. USER_FILE="$1"
    LOG_FILE="/var/log/user_management.log"
    PASSWORD_FILE="/var/secure/user_passwords.txt"

    • Define Paths:
      • USER_FILE = first argument
      • LOG_FILE = keeps operational logs
      • PASSWORD_FILE = stores the randomly generated passwords

  6. if [ -z "$USER_FILE" ]; then
    echo "Usage: $0 <name-of-text-file>"
    log "Error: No file provided. Usage: $0 <name-of-text-file>"
    exit 1
    fi

    • Checks if a filename is provided as an argument. If not, logs an error and exits.

  7. if [ ! -f "$USER_FILE" ]; then
    echo "File does not exist"
    log "File '$USER_FILE' not found"
    exit 1
    fi

    • Checks if the provided user file exists. If not, logs an error and exits.

  8. if [ ! -f "$LOG_FILE" ]; then
    touch "$LOG_FILE"
    chmod 0600 "$LOG_FILE"
    log "Log file created: $LOG_FILE"
    fi

    • Creates the log file if it doesn't exist and sets its permissions to be readable and writable only by the owner.

  9. if [ ! -f "$PASSWORD_FILE" ]; then
    mkdir -p /var/secure
    touch "$PASSWORD_FILE"
    chmod 0600 "$PASSWORD_FILE"
    log "Password file created: $PASSWORD_FILE"
    fi

    • Creates the password file if it doesn't exist, ensuring it is stored securely by setting appropriate permissions.

  10. users_created=false
    user_creation_failed=false
    password_setting_failed=false
    group_creation_failed=false
    home_directory_setup_failed=false
    all_users_exist=true
    any_users_created=false

    • Initializes various flags to track the success or failure of different operations.
  11. `
    validate_username() {
    if [[ ! "$1" =~ ^[a-zA-Z0-9_-]+$ ]]; then
    return 1
    fi
    return 0
    }

    validate_groups() {
    IFS=',' read -ra group_list <<< "$1"
    for group in "${group_list[@]}"; do
    if [[ ! "$group" =~ ^[a-zA-Z0-9_-]+$ ]]; then
    return 1
    fi
    done
    return 0
    }
    `

    • Defines functions validate_username and validate_groups to ensure usernames and groups are valid, using regex checks.

  12. while IFS=';' read -r username groups; do
    # Trim whitespace from username and groups
    username=$(echo "$username" | xargs)
    groups=$(echo "$groups" | xargs)

    • Reads the user file line by line, splitting each line into username and groups variables, and trims any whitespace.
  13. `
    if [ -z "$username" ] || [ -z "$groups" ]; then
    log "Invalid line format in user file: '$username;$groups'"
    user_creation_failed=true
    continue
    fi

    if ! validate_username "$username"; then
        log "Invalid username format: '$username'"
        user_creation_failed=true
        continue
    fi
    
    if ! validate_groups "$groups"; then
        log "Invalid group format: '$groups'"
        group_creation_failed=true
        continue
    fi
    

    `

    • Validates the format of each line in the user file, logging errors and setting flags as necessary.
  14. `
    if id "$username" &>/dev/null; then
    log "User $username already exists."
    continue
    fi

    # User does not exist, so there's work to be done
    all_users_exist=false
    

    `

    • Checks if the user already exists. If so, logs a message and skips to the next user. Otherwise, sets a flag indicating new users need to be created.

  15. if ! getent group "$username" > /dev/null; then
    if groupadd "$username"; then
    log "Group $username created."
    else
    log "Failed to create group $username."
    group_creation_failed=true
    continue
    fi
    fi

    • Creates a personal group for the user if it doesn't already exist, logging the outcome.

  16. IFS=',' read -ra group_list <<< "$groups"
    for group in "${group_list[@]}"; do
    if ! getent group "$group" > /dev/null; then
    if groupadd "$group"; then
    log "Group $group created."
    else
    log "Failed to create group $group."
    group_creation_failed=true
    fi
    fi
    done
    unset IFS

    • Creates any additional groups if they don't already exist, logging the outcome.

  17. if useradd -m -g "$username" -G "$groups" "$username"; then
    log "User $username created and added to groups $groups"
    users_created=true
    any_users_created=true
    else
    log "Failed to create user $username"
    user_creation_failed=true
    continue
    fi

    • Creates the user, assigns them to the primary group, and adds them to additional groups, logging the outcome.

  18. password=$(generate_password)
    log "Generated password for $username"

    • Generates a random password and sets the user's password, storing it securely.


    # Set the user's password
    if echo "$username:$password" | chpasswd; then
    # Store the password securely in TXT format
    echo "$username,$password" >> "$PASSWORD_FILE"
    log "Password set for $username and stored securely"
    else
    log "Failed to set password for $username"
    password_setting_failed=true
    continue
    fi


  19. if chown "$username:$username" "/home/$username" && chmod 700 "/home/$username"; then
    log "Home directory for $username set up with appropriate permissions."
    else
    log "Failed to set up home directory for $username"
    home_directory_setup_failed=true
    fi

    • Sets the correct permissions and ownership for the user's home directory, logging the outcome.

  20. done < "$USER_FILE"

    • Ends the while loop that processes each line of the user file.
  21. `
    log "User creation script run completed."

    if [ "$any_users_created" = true ]; then
    echo "$(date '+%Y-%m-%d %H:%M:%S') - User creation script completed successfully."
    elif [ "$all_users_exist" = true ]; then
    echo "Users already exist. Nothing left to do"
    else
    echo "$(date '+%Y-%m-%d %H:%M:%S') - No users were created successfully. Check log file."
    log "No users were created successfully. Please check the input file format: username;group1,group2,group3."
    fi
    `

    • Logs a summary of the script's execution and prints appropriate messages to the console based on the success or failure of user creation.

  22. [ "$user_creation_failed" = true ] && echo "Users creation incomplete." && log "Some users were not created due to errors. Check file format"
    [ "$password_setting_failed" = true ] && echo "Users' passwords creation incomplete." && log "Some users' passwords were not set due to errors. Check file format"
    [ "$group_creation_failed" = true ] && echo "Groups creation incomplete." && log "Some groups were not created due to errors. Check file format"
    [ "$home_directory_setup_failed" = true ] && echo "Home directories creation incomplete." && log "Some home directories were not set up due to errors."

    • Checks various flags and logs additional error messages if any failures occurred during the script's execution.

  23. exit 0

    • Exits the script with a success status code.

To call the script create_users.sh, you need to provide the path to the user file as an argument. The user file should contain lines formatted as username;group1,group2,group3.

Here's the general format for calling the script:

sudo ./create_users.sh <name-of-text-file>

Top comments (0)