<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Dayo Jude Adeyemi</title>
    <description>The latest articles on Forem by Dayo Jude Adeyemi (@jhude51).</description>
    <link>https://forem.com/jhude51</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1713756%2Ff6b959a9-34d5-453d-91a9-638d7a64a02f.jpg</url>
      <title>Forem: Dayo Jude Adeyemi</title>
      <link>https://forem.com/jhude51</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jhude51"/>
    <language>en</language>
    <item>
      <title>Automating The Creation Of Users From A Text File Using Bash Scripting</title>
      <dc:creator>Dayo Jude Adeyemi</dc:creator>
      <pubDate>Mon, 01 Jul 2024 22:33:33 +0000</pubDate>
      <link>https://forem.com/jhude51/automating-the-creation-of-users-from-a-text-file-using-bash-scripting-5b4a</link>
      <guid>https://forem.com/jhude51/automating-the-creation-of-users-from-a-text-file-using-bash-scripting-5b4a</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Introduction&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This article explains a bash script (&lt;a href="https://github.com/jhude51/hng-stage-one.git"&gt;GitHub Repo&lt;/a&gt;) designed to automate Linux user account creation from a text file containing the said users, as well as a list of supplementary group(s). The script should create users and groups as specified in the prerequisites section, set up home directories with appropriate permissions and ownership, generate random passwords for the users, and log all actions in a file. In addition, the script should store the generated passwords securely in a text file.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Prerequisites&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Before proceeding with this article, it’s pertinent that you have some basic knowledge of &lt;strong&gt;Linux OS&lt;/strong&gt; and its commands. Although I have added clear comments to the script, a basic knowledge of Bash scripting is still required to follow along.&lt;/p&gt;

&lt;p&gt;To run or use the script, take note of the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensure you have sudo privileges as user and group management typically requires root access i.e. run the script with sudo or as root. &lt;/li&gt;
&lt;li&gt;The script is written for an Ubuntu distro but would still work for other Linux flavors.&lt;/li&gt;
&lt;li&gt;Each line in the text file to be passed to the script as an argument must be formatted as &lt;code&gt;user; list of groups separated by commas&lt;/code&gt;.
&lt;strong&gt;Sample file structure:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;light; sudo,dev,www-data
idimma; sudo
mayowa; dev,www-data

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Script Explanation&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;With the housekeeping out of the way, below is the breakdown of the script.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Validation of Input File&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The script starts by checking that an input file is passed as an argument to the script and the path of the said file is valid (the file exists). &lt;br&gt;
For both checks, I used the if conditional in combination with the logical AND operator ‘&lt;strong&gt;&amp;amp;&amp;amp;&lt;/strong&gt;’, which only evaluates the second statement &lt;strong&gt;if and only if the first statement is true&lt;/strong&gt;. If the first statement evaluates to true, the script exits immediately i.e. &lt;strong&gt;&lt;em&gt;exit 1&lt;/em&gt;&lt;/strong&gt;.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbd9jh4zdaz9rsjn7r8ei.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbd9jh4zdaz9rsjn7r8ei.png" alt="Validation of Input File" width="800" height="97"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Helper Functions&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Two helper functions are defined for random password generation and logging. The random password generator function – &lt;em&gt;&lt;strong&gt;password_gen()&lt;/strong&gt;&lt;/em&gt; uses the bash built-in &lt;strong&gt;$RANDOM&lt;/strong&gt; variable which by default, generates random integer. The &lt;strong&gt;$RANDOM&lt;/strong&gt; variable is piped to the &lt;strong&gt;base64&lt;/strong&gt; module to generate an alphanumeric password.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe9zczdq6dm2j1fkas7ne.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe9zczdq6dm2j1fkas7ne.png" alt="Helper Functions - Random Password Generator" width="800" height="95"&gt;&lt;/a&gt;&lt;br&gt;
The logging function – &lt;em&gt;&lt;strong&gt;logger()&lt;/strong&gt;&lt;/em&gt; when called with an argument, would echo the current date and time together with the action to a file declared as &lt;strong&gt;$LOG_FILE&lt;/strong&gt;.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftbwomr96hpojmkna4t68.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftbwomr96hpojmkna4t68.png" alt="Helper Functions - Logging Function" width="800" height="126"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Secure the Password File&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The directory &lt;em&gt;&lt;strong&gt;/var/secure&lt;/strong&gt;&lt;/em&gt; is created if it does not exist and only the user has permissions on the directory.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp52jtfnssv6edanavocu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp52jtfnssv6edanavocu.png" alt="Secure the Password File" width="622" height="102"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Working with the Input File&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The input file is first read line by line (&lt;strong&gt;$lines&lt;/strong&gt;) and each line (&lt;strong&gt;$line&lt;/strong&gt; – delimited by a newline character) is iterated over in a for loop. &lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyhgpmr8vlu3rity34nkv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyhgpmr8vlu3rity34nkv.png" alt="Working with the Input File - read line" width="800" height="161"&gt;&lt;/a&gt;&lt;br&gt;
The &lt;strong&gt;$line&lt;/strong&gt; is then split to an array at the delimiter &lt;strong&gt;‘;’&lt;/strong&gt; with a trailing whitespace &lt;strong&gt;(‘; ’)&lt;/strong&gt;. Remember that each line of the input file is formatted as so - &lt;code&gt;user; list of groups&lt;/code&gt;. The first slice (string before the field separator/delimiter &lt;strong&gt;‘; ’&lt;/strong&gt;) is assigned to the &lt;strong&gt;$username&lt;/strong&gt; variable and the other slice to the &lt;strong&gt;$groups&lt;/strong&gt; variable.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fatw97pxe8omyojj3qqko.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fatw97pxe8omyojj3qqko.png" alt="Working with the Input File - split username and group" width="800" height="140"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;User Creation&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The script then checks if a user with the &lt;strong&gt;$username&lt;/strong&gt; already exists and if true, skips to the next iteration. Otherwise, the helper &lt;em&gt;&lt;strong&gt;password_gen()&lt;/strong&gt;&lt;/em&gt; function is called and the value assigned to a &lt;strong&gt;$password&lt;/strong&gt; variable. The &lt;strong&gt;useradd&lt;/strong&gt; utility is then called with the following flags &lt;em&gt;m&lt;/em&gt;, &lt;em&gt;U&lt;/em&gt; and &lt;em&gt;G&lt;/em&gt; (see the useradd man pages) to create the user.&lt;br&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwq49214ymzvjgjfyi3ah.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwq49214ymzvjgjfyi3ah.png" alt="User Creation - useradd" width="800" height="410"&gt;&lt;/a&gt;&lt;br&gt;
For the user password, we use &lt;strong&gt;chpasswd&lt;/strong&gt; utility to set the password with the &lt;strong&gt;$password&lt;/strong&gt; generated. In addition, the password is then redirected to the &lt;strong&gt;$PASSWORD_FILE&lt;/strong&gt; and appropriate permissions set on the file (only the user has permissions - &lt;em&gt;rw&lt;/em&gt;).&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgsatzdwtz6z2xr36y0mu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgsatzdwtz6z2xr36y0mu.png" alt="User Creation - chpasswd" width="800" height="168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Securing the User Home Directory&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Finally, appropriate permissions are set on the user’s home directory so that only the user has &lt;em&gt;read,write&lt;/em&gt; and &lt;em&gt;execute&lt;/em&gt; permissions on the directory. &lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Falta5pkyfzdza2rjqwmj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Falta5pkyfzdza2rjqwmj.png" alt="Securing the User Home Directory" width="800" height="92"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Running the Script&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Make the script executable: &lt;code&gt;chmod +x /path/to/script&lt;/code&gt;. You might have to run this command as &lt;strong&gt;sudo&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Run the script with the input file as an argument: &lt;code&gt;sudo ./path/to/script /path/to/inputfile.txt&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;You can verify the script ran successfully by running the following:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# View the $LOG_FILE
sudo cat /var/log/user_management.log
# View the $PASSWORD_FILE
sudo cat /var/secure/user_passwords.txt
# View the system accounts file
sudo cat /etc/passwd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;As a Sysops/DevOps engineer, automation of user account management using bash scripts can significantly enhance efficiency and accuracy. Take note that the desired result can be achieved using a different logic and structure. For a more streamlined solution, refactoring the main part of the script into smaller functions should be considered.&lt;br&gt;
This task is a part of the &lt;strong&gt;HNG Internship program&lt;/strong&gt; that offers a transformative 2-month internship, where the participants can amplify their skills, cultivate networks whilst working on real life projects like this one. You can learn more about the program by visiting the HNG Internship website at &lt;a href="https://hng.tech/internship"&gt;HNG Internship&lt;/a&gt;. You can also join the &lt;strong&gt;HNG Premium Network&lt;/strong&gt; where you can get connected with top techies, collaborate with them, and grow your career. To learn more about the HNG Premium Network, visit &lt;a href="https://hng.tech/premium"&gt;HNG Premium&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>linux</category>
      <category>hng</category>
    </item>
  </channel>
</rss>
