User(s) Creation Automation with Bash
Tags: devops, bash
Published: 2024-07-02 20:29:28

Hey there! 

So here is a synopsis of my stage 1 task at the [HNG DevOps Internship](https://hng.tech/internship)


### 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.

**Sample Input**

```
light; sudo,dev,www-data
idimma; sudo
mayowa; dev,www-data
```


### Solution

The programme I wrote solves the problem by following these procedures;

- First, we read the `input file` using a function that adds the users to a global variable called `users` and the groups to another variable called `groups`. It does this simultaneously allowing the index of each user in `users` to match their corresponding groups in `groups`.

I also ensure the user has entered a valid input file before running this.

Here's the code that does all of this:

```bash
declare -a users
declare -a groups

function read_input() {
  local file="$1"

  while IFS= read -r line; do
    user=$(echo "$line" | cut -d';' -f1)
    groups_data=$(echo "$line" | cut -d';' -f2 | tr -d '[:space:]')
    users+=("$user")
    groups+=("$groups_data")
  done < "$file"
}


if [[ $# -ne 1 ]]; then
  echo "Usage: $0 <input_file>"
  exit 1
fi

input_file="$1"
echo "Reading input file: $input_file"
read_input "$input_file"
```

- Next, I go on to create the required files and their directories if they don't already exist using this code:

```bash
log_file="/var/log/user_management.log"
password_file="/var/secure/user_passwords.txt"


if [ ! -f "$log_file" ]; then
  mkdir -p /var/log
  touch "$log_file"
fi

if [ ! -f "$password_file" ]; then
  mkdir -p /var/secure
  touch "$password_file"
fi
```


- Then the main event, at this point I have a list of the users in `users`, a list of their corresponding groups in `groups` and all the files I will need to store valuable information such as logs and the passwords of the users I created.

Now, I use a `for` loop to iterate over each user and their corresponding groups with an index.

Remember we created the `users` and `groups` array simultaneously by looping over each line in the file this means the user at `index 
0` in `users` needs to be added to the groups at `index 0` in `groups`, so our `for` loop will look like this:

```bash
for (( i = 0; i < ${#users[@]}; i++ )); do
  user="${users[$i]}"
  user_groups="${groups[$i]}"

```

So `user` is the user we are working on and `user_groups` are the groups we are adding them to.

Next, we check if the `user` exists, if they do we just continue with the next iteration; else, we create them with this code:

```bash
  if id "$user" &>/dev/null; then
    echo "User $user already exists, Skipped" | tee -a "$log_file"
  else
    # Create user
    useradd -m -s /bin/bash "$user"
    if [[ $? -ne 0 ]]; then
      echo "Failed to create user $user" | tee -a "$log_file"
      exit 1
    fi
    echo "User $user created" | tee -a "$log_file"
```

Then we set a password for them by using openssl to generate 50 random base64 characters then we filter the result to extract the alphabets, numbers and some special characters. We finally slice the first 10 characters and this will serve as the user's password.

We go ahead to store the users password in `/var/secure/user_passwords.txt`.

These are done using the code below:

```bash
   # 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 "Failed to set password for $user" | tee -a "$log_file"
      exit 1
    fi

    echo "Password for $user set" | tee -a "$log_file"
    echo "$user:$password" >> "$password_file"

```


Finally, we add the user to their groups. 


We do this by first of all checking if a personal group was created for the user--most linux distros do this by default on user creation due to security reasons. But if it didn't we create a personal group for the user and add the user to the group.

See code below:

```bash
    # Create personal group if linux distro didn't create it by default
    if grep -q "^$user:" /etc/group; then
      echo "Personal group $user already exists" | tee -a "$log_file"
    else
      echo "Personal group $user does not exist, creating $user" | tee -a "$log_file"
      groupadd "$user"
      if [[ $? -ne 0 ]]; then
        echo "Failed to create personal group $user" | tee -a "$log_file"
        exit 1
      fi
    fi

    # Add user to personal group
    usermod -aG "$user" "$user"
    if [[ $? -ne 0 ]]; then
      echo "Failed to add $user to $user group" | tee -a "$log_file"
      exit 1
    fi
    echo "Added $user to $user group" | tee -a "$log_file"

```

After which we split the previously created variable `user_groups` by `,` and iterate through each element creating the group if it doesn't already exist and adding the user to the group. Then we finally close our initial loop


We do this using this code:

```bash
    # Add user to other groups
    for group in $(echo "$user_groups" | tr ',' '\n'); do
      if grep -q "^$group:" /etc/group; then
        echo "Group $group already exists" | tee -a "$log_file"
      else
        echo "Group $group does not exist, creating $group" | tee -a "$log_file"
        groupadd "$group"
      
        if [[ $? -ne 0 ]]; then
          echo "Failed to create group $group" | tee -a "$log_file"
          exit 1
        fi
      fi

    usermod -aG "$group" "$user"
    if [[ $? -ne 0 ]]; then
      echo "Failed to add $user to $group group" | tee -a "$log_file"
      exit 1
    fi
    echo "Added $user to $group group" | tee -a "$log_file"
    done
  fi
done

```


And just like that, all the new employees now have user profiles!

You can also reuse this script for new employees. Exciting right?

You will also notice how I appropriately log each event in the log file and gracefully handle failures in each command so even if we run into unexpected problems in our execution we not only end the programme gracefully we also have a log for further investigation. 

That's it for now, but be sure to tune in for the Stage 2 task 😉

I am also open to constructive criticism so be sure to leave a comment.




Also, you can learn more about HNG [here](https://hng.tech/)