Simplify User and Group Management with Bash Automation

Simplify User and Group Management with Bash Automation

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:

    1. Read users and groups from an input text file.

    2. Create users and assign them to a group or groups specified in the text file.

    3. Set up home directories with appropriate permissions and ownership.

    4. Generate random passwords for the users.

    5. Log all actions to /var/log/user_management.log.

    6. 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 and groups: 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 with tr.

  • Adds extracted user and group to their respective arrays (users and groups).

      # 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 the users and groups 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).
  • 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 a for loop (i as the index).

  • Extracts the current user name (user) and group names (user_groups) from the corresponding elements in the users and groups 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) using useradd -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 and IFS (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: