DEV Community

Ugochukwu Elekwachi
Ugochukwu Elekwachi

Posted on

Creating users and groups from a file with bash.

As a devops engineer creating users and managing them is one of the primary responsibilities as a devops engineer or sysadmin.

Today we will be creating a bash script that :

  1. Reads a text file containing the employee’s usernames and group names, where each line is formatted as user;groups.

  2. create users and groups as specified

  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. Additionally, store the generated passwords securely in /var/secure/user_passwords.txt

Without further ado let's get started.

Firstly lets create the file create_user.sh using the touch command

touch create_user.sh
we want to first add our shebang #!/bin/bash.

Now we an start ticking off some of the requirements.

requirement 5 specifically, let us create the user_management.log file

Creating the required log and txt files

if [[ ! -e /var/log/user_management.log ]]; then
    sudo mkdir -p /var/log/
    sudo touch /var/log/user_management.log
fi

Enter fullscreen mode Exit fullscreen mode

analyzing the above code: we check if the fire /var/log/user_management.log exist, if it does not we

create the directory /var/log/ with mkdir and the -p flag for persistence, then we create the file user_management.log using touch and then end the if statement using fi.

lets do the same for the file that all the passwords will be saved in

if [[ ! -e /var/secure/user_passwords.txt ]]; then
    sudo mkdir -p /var/secure/
    sudo touch /var/secure/user_passwords.txt
fi
Enter fullscreen mode Exit fullscreen mode

/var/secure/user_passwords.txt and /var/log/user_management.log is difficult to type and a mouthful to say so we'll assign the to variables

log_file="/var/log/user_management.log"
pass_file="/var/secure/user_passwords.txt"

Enter fullscreen mode Exit fullscreen mode

and now we can start sending outputs of all that is going on to our logfile

echo "logfile created..." >> $log_file

echo "checking if text_file exists" >> $log_file

Enter fullscreen mode Exit fullscreen mode

checking if a text file containing the users and groups have been passed

Confirming a file was passed as an argument

if [[ -z "$1" ]]; then
    echo "Usage: $0 filename"
    echo "Text file does not exist. ...exiting" >> $log_file
    exit 1
fi
Enter fullscreen mode Exit fullscreen mode

The above code snippet checks if there is user_input from the terminal
and if there is not it sends a text to the log file and the program ends.

However assuming there is a file passed/user_input made in the terminal

Reading the file

echo "Reading file" >> $log_file

file_path="$1"
if [[ -f "$file_path" ]]; then
    echo "fetching the usernames and groups" >> $log_file
    while IFS= read -r lines; do
              user_name=$(echo "$lines" | awk -F'; ' '{print $1}')
        groups=$(echo "$lines" | awk -F'; ' '{print $2}')


Enter fullscreen mode Exit fullscreen mode

The above code opens up the file and starts to read from it,

Fetching the usernames and groups

While IFS = read -r lines; do
    user_name=$(echo "$lines" | awk -F'; ' '{print $1}')
    groups=$(echo "$lines" | awk -F'; ' '{print $2}')
Enter fullscreen mode Exit fullscreen mode

The above snippet basically says while there are lines in the file i.e the code has not traveled to the end of the file, we then assign user_name to the $lines being piped through awk and the delimiter '; ' to separate the line

basically,
we have a line vale; sudo, vale, data
when we pipe it into awk -F'; ' '{print $1}' it splits the line into tow parts which are vale and sudo, vale, data and {print $1} let's us access the first part of the line before the delimiter. while the line below does the same thing but {print $2} gives us access to the second part of the line.

file_path="$1"
if [[ -f "$file_path" ]]; then
    echo "fetching the usernames and groups" >> $log_file
    while IFS= read -r lines; do
        user_name=$(echo "$lines" | awk -F'; ' '{print $1}')
        groups=$(echo "$lines" | awk -F'; ' '{print $2}')

        if id -u "$user_name">/dev/null 2>&1; then
            echo "user $user_name already exists"
            echo "user $user_name already exists" >> $log_file
        else
            IFS=',' read -ra group_array <<< "$groups" #explain in the post
Enter fullscreen mode Exit fullscreen mode

now that we have access to the users we can check if a user already exist, to do that we simply check if a user has an id, if the name already has an id it exist and we simply make it known to the user that the user_name has been created already

        if id -u "$user_name">/dev/null 2>&1; then
            echo "user $user_name already exists"
            echo "user $user_name already exists" >> $log_file
Enter fullscreen mode Exit fullscreen mode

checking if user already exists

id -u "$user_name" is used to get the id of a user and >dev/null 2>&1 is used to send the output to a null file so no output is displayed

else
IFS=',' read -ra group_array <<< "$groups"
is used to read the elements in groups and put them into an array group_array where the elements are split by the ',' delimiter.

file_path="$1"
if [[ -f "$file_path" ]]; then
    echo "fetching the usernames and groups" >> $log_file
    while IFS= read -r lines; do
        user_name=$(echo "$lines" | awk -F'; ' '{print $1}')
        groups=$(echo "$lines" | awk -F'; ' '{print $2}')

        if id -u "$user_name">/dev/null 2>&1; then
            echo "user $user_name already exists"
            echo "user $user_name already exists" >> $log_file
        else
            IFS=',' read -ra group_array <<< "$groups" #explain in the post
            for group in "${group_array[@]}"; do
                if ! getent group "$group" >/dev/null 2>&1; then
                    sudo groupadd "$group"
                    echo "Group $group created"

                    echo "Group $group created" >> $log_file
                fi
            done


Enter fullscreen mode Exit fullscreen mode

Group creation

for group in "${group_array[@]}"; do
                if ! getent group "$group" >/dev/null 2>&1; then
                    sudo groupadd "$group"
                    echo "Group $group created"

                    echo "Group $group created" >> $log_file
                fi
            done
Enter fullscreen mode Exit fullscreen mode

lets break down the above block of code :
for group in "{$group_array[@]}; this creates a variable group that is equal to the current item in the group array that was created before and it keeps on changing according to the number of elements in that group
do
if ! getent group "$group" >/dev/null 2>&1;
remember when we wanted to find out if a user exists or not? This is basically the same thing but instead of a user we use the getent function to get entities that belong to a particular group and if no entity belongs to a particular group the group most likely does not exists so we pipe the output to the null folder and create the group.

password creation

we can generate a random password using openssl

password=$(openssl rand -base64 12)

This creates a variable password which would have a 12 digit password in base 64

creating users with their assigned group and passwords

luckily someone asked this question on stackoverflow and got a response

Image description

Thank you very much Netzego and Damien
sudo useradd -m -G "$groups" -p "$(openssl passwd -1 "$password")" "$user_name"

echo "adding groups :$groups for user: $user_name and password in $pass_file " >> $log_file
group_fold="/home/$user_name"

so far we have:

file_path="$1"
if [[ -f "$file_path" ]]; then
    echo "fetching the usernames and groups" >> $log_file
    while IFS= read -r lines; do
        user_name=$(echo "$lines" | awk -F'; ' '{print $1}') #explain in the post
        groups=$(echo "$lines" | awk -F'; ' '{print $2}')

        if id -u "$user_name">/dev/null 2>&1; then #explain in the post
            echo "user $user_name already exists"
            echo "user $user_name already exists" >> $log_file
        else
            IFS=',' read -ra group_array <<< "$groups" #explain in the post
            for group in "${group_array[@]}"; do
                if ! getent group "$group" >/dev/null 2>&1; then #explain in the post
                    sudo groupadd "$group"
                    echo "Group $group created"

                    echo "Group $group created" >> $log_file
                fi
            done

            password=$(openssl rand -base64 12)


            sudo useradd -m -G "$groups" -p "$(openssl passwd -1 "$password")" "$user_name" #explain in post
            echo "adding groups :$groups for user: $user_name and password in $pass_file " >> $log_file
            group_fold="/home/$user_name"
Enter fullscreen mode Exit fullscreen mode

now we just have to create the users home folders with the appropriate permissions and groups

sudo chmod 700 $group_fold > $log_file 2>&1
            sudo chown $user_name:$user_name $group_fold > $log_file 2>&1

            echo "Home directory for $user set up with appropriate permissions and ownership" >> $log_file

            echo "$user_name,$password" >> "$pass_file"
        fi
    done < "$file_path"
    sudo chmod 600 "$password_file"
        sudo chown "$(id -u):$(id -g)" "$password_file"
        echo "File permissions for $password_file set to owner-only read" >> $log_file
else
    echo "File not found: $file_path" >> "$log_file"
    exit 1
Enter fullscreen mode Exit fullscreen mode

The complete code:

#!/bin/bash

if [[ ! -e /var/log/user_management.log ]]; then
    sudo mkdir -p /var/log/
    sudo touch /var/log/user_management.log
fi

if [[ ! -e /var/secure/user_passwords.txt ]]; then
    sudo mkdir -p /var/secure/
    sudo touch /var/secure/user_passwords.txt
fi

log_file="/var/log/user_management.log"
pass_file="/var/secure/user_passwords.txt"

echo "logfile created..." >> $log_file
echo "checking if text_file exists" >> $log_file

if [[ -z "$1" ]]; then
    echo "Usage: $0 filename"
    echo "Text file does not exist. ...exiting" >> $log_file
    exit 1
fi

echo "Reading file" >> $log_file

file_path="$1"
if [[ -f "$file_path" ]]; then
    echo "fetching the usernames and groups" >> $log_file
    while IFS= read -r lines; do
        user_name=$(echo "$lines" | awk -F'; ' '{print $1}') #explain in the post
        groups=$(echo "$lines" | awk -F'; ' '{print $2}')

        if id -u "$user_name">/dev/null 2>&1; then #explain in the post
            echo "user $user_name already exists"
            echo "user $user_name already exists" >> $log_file
        else
            IFS=',' read -ra group_array <<< "$groups" #explain in the post
            for group in "${group_array[@]}"; do
                if ! getent group "$group" >/dev/null 2>&1; then #explain in the post
                    sudo groupadd "$group"
                    echo "Group $group created"

                    echo "Group $group created" >> $log_file
                fi
            done

            password=$(openssl rand -base64 12)


            sudo useradd -m -G "$groups" -p "$(openssl passwd -1 "$password")" "$user_name" #explain in post
            echo "adding groups :$groups for user: $user_name and password in $pass_file " >> $log_file
            group_fold="/home/$user_name"

            sudo chmod 700 $group_fold > $log_file 2>&1
            sudo chown $user_name:$user_name $group_fold > $log_file 2>&1

            echo "Home directory for $user set up with appropriate permissions and ownership" >> $log_file

            echo "$user_name,$password" >> "$pass_file"
        fi
    done < "$file_path"
    sudo chmod 600 "$password_file"
        sudo chown "$(id -u):$(id -g)" "$password_file"
        echo "File permissions for $password_file set to owner-only read" >> $log_file
else
    echo "File not found: $file_path" >> "$log_file"
    exit 1
Enter fullscreen mode Exit fullscreen mode

This task was assigned to me during my HNG internship devops track

Top comments (0)