<?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: Raji Risikat Yewande</title>
    <description>The latest articles on Forem by Raji Risikat Yewande (@wandexdev).</description>
    <link>https://forem.com/wandexdev</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%2F830843%2Fb3911f56-af63-4fcb-bb55-f9bd7ddcaa4e.jpg</url>
      <title>Forem: Raji Risikat Yewande</title>
      <link>https://forem.com/wandexdev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/wandexdev"/>
    <language>en</language>
    <item>
      <title>Automation: Onboard New Engineers on Linux with Best Practice Bash/Shell Scripting.</title>
      <dc:creator>Raji Risikat Yewande</dc:creator>
      <pubDate>Tue, 02 Jul 2024 19:28:27 +0000</pubDate>
      <link>https://forem.com/wandexdev/automation-onboard-new-engineers-on-linux-with-best-practice-bashshell-scripting-121o</link>
      <guid>https://forem.com/wandexdev/automation-onboard-new-engineers-on-linux-with-best-practice-bashshell-scripting-121o</guid>
      <description>&lt;h2&gt;
  
  
  Synopsis
&lt;/h2&gt;

&lt;p&gt;Your role is &lt;strong&gt;SysOps or SysAdmin Engineer&lt;/strong&gt;, you are tasked with onboarding new engineers on most of the company's Linux servers. Users, groups, and home directories would be created. Access permissions for each user following the rule of less privilege should be observed. It would be inefficient to do so manually, looking at the number of servers and new engineers to be onboarded.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;I have created a script that meets the basic requirements and some more.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;&lt;em&gt;It puts measures in place for errors while running the script, creates secure files to store user lists and passwords, creates files to debug and log processes, and finally sends notifications on both the terminal and Slack, all while following best practices.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Essentials:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An Ubuntu server&lt;/li&gt;
&lt;li&gt;Basic Git Knowledge&lt;/li&gt;
&lt;li&gt;Basic Linux Knowledge&lt;/li&gt;
&lt;li&gt;A terminal user with sudo privileges.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Procedures:
&lt;/h2&gt;

&lt;p&gt;I would walk you through the logic flow of the script, lets take them in sections. &lt;/p&gt;

&lt;h3&gt;
  
  
  1. Initiation Setup Section
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;span class="c"&gt;# Define log and secure password files&lt;/span&gt;
&lt;span class="nv"&gt;LOG_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/log/user_management.log"&lt;/span&gt;
&lt;span class="nv"&gt;SECURE_PASSWORD_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/secure/user_passwords.txt"&lt;/span&gt;
&lt;span class="nv"&gt;SECURE_PASSWORD_CSV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/secure/user_passwords.csv"&lt;/span&gt;
&lt;span class="nv"&gt;SLACK_WEBHOOK_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://hooks.slack.com/services/WandesDummySlack/webhook/url"&lt;/span&gt;

&lt;span class="c"&gt;# Define custom exit codes&lt;/span&gt;
&lt;span class="nv"&gt;E_INVALID_INPUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10
&lt;span class="nv"&gt;E_USER_CREATION_FAILED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;20
&lt;span class="nv"&gt;E_GROUP_CREATION_FAILED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;30
&lt;span class="nv"&gt;E_ADD_USER_TO_GROUP_FAILED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;40

&lt;span class="c"&gt;# Define resource limits&lt;/span&gt;
&lt;span class="nb"&gt;ulimit&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; 60  &lt;span class="c"&gt;# CPU time limit in seconds&lt;/span&gt;
&lt;span class="nb"&gt;ulimit&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; 1000000  &lt;span class="c"&gt;# Virtual memory limit in kilobytes&lt;/span&gt;

&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /var/log
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /var/secure
&lt;span class="nb"&gt;sudo touch&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;sudo touch&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SECURE_PASSWORD_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;sudo touch&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SECURE_PASSWORD_CSV&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SECURE_PASSWORD_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SECURE_PASSWORD_CSV&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Valid bash scripts begin with &lt;code&gt;#!/bin/bash&lt;/code&gt;, its called a &lt;strong&gt;shebang&lt;/strong&gt; or &lt;strong&gt;hashbang&lt;/strong&gt; and it tells the OS what interpreter to use. &lt;code&gt;set -e&lt;/code&gt; is a personal default line of mine for exiting a script the moment an error occurs. The next lines are &lt;strong&gt;variables&lt;/strong&gt; and they store the value of the intended files and urls. I have 3 files that log every action, store user passwords, and my &lt;strong&gt;slack webhook url for notifications&lt;/strong&gt; once a user has been onboarded successfully. Next lines are &lt;strong&gt;exit codes&lt;/strong&gt;, &lt;strong&gt;timeouts&lt;/strong&gt; and &lt;strong&gt;resource limits&lt;/strong&gt; I defined in the script for easy debugging, hanging runs and resource optimization.&lt;br&gt;
The last set of lines in this stage show all absolute file paths and their corresponding files, which would be created and properly secured with permissions. The 600 in &lt;code&gt;chmod 600&lt;/code&gt; indicates that only the owner has read and write rights on the file; others have zero access.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Functions Section
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Function to log messages to the log file&lt;/span&gt;
log&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="s1"&gt;'+%Y-%m-%d %H:%M:%S'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; - &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Function to send notifications to Slack&lt;/span&gt;
send_slack_notification&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
    curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-type: application/json'&lt;/span&gt; &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;message&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SLACK_WEBHOOK_URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Function to generate a random password of length 12&lt;/span&gt;
generate_random_password&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &amp;lt; /dev/urandom &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-dc&lt;/span&gt; A-Za-z0-9 | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-c10&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Function to validate input format for usernames and groups&lt;/span&gt;
validate_input&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
    &lt;span class="nb"&gt;local groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$groups&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;log &lt;span class="s2"&gt;"Error: Invalid input. Usernames and groups are required."&lt;/span&gt;
        send_slack_notification &lt;span class="s2"&gt;"Invalid input provided. Usernames and groups are required."&lt;/span&gt;
        &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="nv"&gt;$E_INVALID_INPUT&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Function definitions are placed at the beginning to ensure they are available when called later in the script. As the comments on each function describes, these functions do different things. The first uses the stored message and webhook variables to make up the body of the &lt;code&gt;curl&lt;/code&gt; it would make to &lt;strong&gt;Slack Channel&lt;/strong&gt; in json format, the second generates a 10-character password piped from random 1–10 and A–Z alphanumeric characters; and the third uses the &lt;code&gt;if fi&lt;/code&gt; statement to validate the username and group name from the text file input. It also logs an error for this step if it occurs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Function to create a user and set up their home directory&lt;/span&gt;
create_user&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;

    &lt;span class="c"&gt;# Check if the user already exists&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &amp;amp;&amp;gt;/dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;log &lt;span class="s2"&gt;"User &lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="s2"&gt; already exists. Skipping user creation."&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;log &lt;span class="s2"&gt;"Creating user &lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="s2"&gt;."&lt;/span&gt;
        &lt;span class="c"&gt;# Attempt to create the user with a timeout&lt;/span&gt;
        &lt;span class="nb"&gt;timeout &lt;/span&gt;10 &lt;span class="nb"&gt;sudo &lt;/span&gt;useradd &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /bin/bash &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            log &lt;span class="s2"&gt;"Failed to create user &lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="s2"&gt;."&lt;/span&gt;
            send_slack_notification &lt;span class="s2"&gt;"Failed to create user &lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="s2"&gt;."&lt;/span&gt;
            &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="nv"&gt;$E_USER_CREATION_FAILED&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="c"&gt;# Set the user's password and home directory permissions&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sudo &lt;/span&gt;chpasswd
        &lt;span class="nb"&gt;sudo chmod &lt;/span&gt;700 &lt;span class="s2"&gt;"/home/&lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"/home/&lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        log &lt;span class="s2"&gt;"User &lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="s2"&gt; created successfully with password &lt;/span&gt;&lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="s2"&gt;."&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SECURE_PASSWORD_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SECURE_PASSWORD_CSV&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
    &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Function to create a group&lt;/span&gt;
create_group&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;groupname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;

    &lt;span class="c"&gt;# Check if the group already exists&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;getent group &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$groupname&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &amp;amp;&amp;gt;/dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;log &lt;span class="s2"&gt;"Group &lt;/span&gt;&lt;span class="nv"&gt;$groupname&lt;/span&gt;&lt;span class="s2"&gt; already exists."&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;log &lt;span class="s2"&gt;"Creating group &lt;/span&gt;&lt;span class="nv"&gt;$groupname&lt;/span&gt;&lt;span class="s2"&gt;."&lt;/span&gt;
        &lt;span class="c"&gt;# Attempt to create the group with a timeout&lt;/span&gt;
        &lt;span class="nb"&gt;timeout &lt;/span&gt;10 &lt;span class="nb"&gt;sudo &lt;/span&gt;groupadd &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$groupname&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            log &lt;span class="s2"&gt;"Failed to create group &lt;/span&gt;&lt;span class="nv"&gt;$groupname&lt;/span&gt;&lt;span class="s2"&gt;."&lt;/span&gt;
            send_slack_notification &lt;span class="s2"&gt;"Failed to create group &lt;/span&gt;&lt;span class="nv"&gt;$groupname&lt;/span&gt;&lt;span class="s2"&gt;."&lt;/span&gt;
            &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="nv"&gt;$E_GROUP_CREATION_FAILED&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        log &lt;span class="s2"&gt;"Group &lt;/span&gt;&lt;span class="nv"&gt;$groupname&lt;/span&gt;&lt;span class="s2"&gt; created successfully."&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two functions above for creating users and groups use an &lt;code&gt;if, else, fi&lt;/code&gt; statement to process their logic. The first has the username validated variable from prevoius function being used to  &lt;strong&gt;verify existing user&lt;/strong&gt; before executing the add user command with a &lt;strong&gt;timeout&lt;/strong&gt; to prevent lag, then it sets the user's password by piping to &lt;code&gt;chpasswd&lt;/code&gt;. It then goes ahead to secure a &lt;code&gt;chmod 700&lt;/code&gt; permission for restrictive access for the home directory. The second for the group is quite similar, just that it is a group being created this time, not a user. Both functions have a log line to log errors as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Onboarding Section
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;onboard_user&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
    &lt;span class="nb"&gt;local groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;

    &lt;span class="c"&gt;# Validate the input format&lt;/span&gt;
    validate_input &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$groups&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="c"&gt;# Generate a random password for the user&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;generate_random_password&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# Create the user with the generated password&lt;/span&gt;
    create_user &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="c"&gt;# Create a personal group for the user&lt;/span&gt;
    create_group &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="c"&gt;# Add the user to their personal group&lt;/span&gt;
    add_user_to_group &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="c"&gt;# Process and add the user to the specified groups&lt;/span&gt;
    &lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-ra&lt;/span&gt; group_array &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$groups&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;group &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;group_array&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
        &lt;/span&gt;&lt;span class="nv"&gt;group&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$group&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | xargs&lt;span class="si"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;# Trim whitespace from group name&lt;/span&gt;
        create_group &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$group&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        add_user_to_group &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$group&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;done&lt;/span&gt;
    &lt;span class="c"&gt;# Notify terminal that user has been successfully onboarded&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"User &lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="s2"&gt; has been successfully onboarded with groups: &lt;/span&gt;&lt;span class="nv"&gt;$groups&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As clearly explained in the comments, this function effectively combines the functionalities of the previous functions, more like the definer. It loops through the Input validation function, password generation function, user creation function, personal group creation function, and adding user to group function. It particularly splits the groups into an array using a &lt;code&gt;comma delimeter (IFS=',')&lt;/code&gt;, loops through again and &lt;strong&gt;trims any trailing white space&lt;/strong&gt; before calling the functions to do their duty.&lt;br&gt;
The last line of this section would send an &lt;strong&gt;output message to the terminal&lt;/strong&gt; each time a user has been successfully onboarded. A slack message also goes to the defined &lt;strong&gt;Slack Channel&lt;/strong&gt; for the same purpose.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Script Execution Section
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check if the script argument is provided&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$# &lt;/span&gt;&lt;span class="nt"&gt;-ne&lt;/span&gt; 1 &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Usage: &lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt; &amp;lt;users_file&amp;gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;10  &lt;span class="c"&gt;# Invalid input exit code&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Read from the provided text file&lt;/span&gt;
&lt;span class="nv"&gt;users_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Read from the input file and process each line&lt;/span&gt;
&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;';'&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; username &lt;span class="nb"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c"&gt;# Remove leading and trailing whitespaces&lt;/span&gt;
    &lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | xargs&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$groups&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | xargs&lt;span class="si"&gt;)&lt;/span&gt;
    onboard_user &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$groups&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt; &amp;lt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$users_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have ensured all functions and necessary configurations are defined and ready to be used when processing the input file, this last part checks if the script argument (input file) is provided. If not, it exits with an error message. It also validates the absence of white space and then processes the user data from a file. In summary, this section executes all previously prepared functions with the input data.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Usage:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Save the entire script as a whole with the name &lt;code&gt;create_users.sh&lt;/code&gt; or whatever name you like. The whole script can be found at my &lt;a href="https://github.com/wandexdev/Linux-User-Management-Automation-with-Bash-Scripting"&gt;github repository&lt;/a&gt;. &lt;/li&gt;
&lt;li&gt;Assemble the input file; the argument formatted this way: usernames are differentiated by a semicolon, and groups are differentiated by a comma.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alice; admin,dev,qa
bob; prod
carol; test,dev,prod
tunde; pilot,prod,test,dev
tade; pilot,dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Save the file as &lt;code&gt;text.txt&lt;/code&gt; or your preferred name
On the ubuntu terminal, run &lt;code&gt;chmod +x create_users.sh&lt;/code&gt; to ensure the file is executable.&lt;/li&gt;
&lt;li&gt;Run script with &lt;code&gt;sudo&lt;/code&gt; via &lt;code&gt;sudo bash create_users.sh text.txt&lt;/code&gt; 
What we eventually get are success messages like those shown in Figure 1 below:

&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%2F86trbixmrn049ltgbe8z.PNG" alt="Terminal window displaying a green success message" width="788" height="133"&gt;Figure 1: Success Messages Displayed on Terminal

&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. Conclusion
&lt;/h3&gt;

&lt;p&gt;In conclusion, this blog post has covered the automation of linux user mangement for new staff. By following these steps and leveraging bash scripting, you can effectively do the automations and improve efficiency by 100%. Now it's your turn! Try out these techniques and share your experiences in the comments below. Additionally, if you have any questions, feel free to leave a comment or reach out to me directly on &lt;a href="https://twitter.com/wandexdev"&gt;Twitter&lt;/a&gt;. &lt;br&gt;
Lastly, I would love to appreciate the team at &lt;a href="https://hng.tech/internship"&gt;HNG internship&lt;/a&gt; for doing an awesome job at impacting knowledge. The &lt;a href="https://hng.tech/hire"&gt;team&lt;/a&gt; is a home of talents. Kudos&lt;/p&gt;

</description>
      <category>bash</category>
      <category>ubuntu</category>
      <category>automation</category>
      <category>linux</category>
    </item>
    <item>
      <title>Costly EKS Cluster Set Up? AWS to the rescue!</title>
      <dc:creator>Raji Risikat Yewande</dc:creator>
      <pubDate>Sun, 23 Apr 2023 18:10:03 +0000</pubDate>
      <link>https://forem.com/aws-builders/costly-eks-cluster-set-up-aws-to-the-rescue-2o4d</link>
      <guid>https://forem.com/aws-builders/costly-eks-cluster-set-up-aws-to-the-rescue-2o4d</guid>
      <description>&lt;h2&gt;
  
  
  Storyline
&lt;/h2&gt;

&lt;p&gt;The goal was to do an automated &lt;strong&gt;Terraform Kubernetes Deployment&lt;/strong&gt;. I eventually provisioned a production grade EKS Cluster using Terraform-EKS-modules and it created other AWS resources to be intergrated along with it. As the beginner that I was, I left the cluster running for days hoping to sort out deploying different types of microservice applications to the same cluster then finally clean up. The total bill accrued after a while was jaw-dropping and problematic but &lt;strong&gt;AWS&lt;/strong&gt; came to my rescue and sorted it all out.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Aim
&lt;/h2&gt;

&lt;p&gt;This walks you through the process of creating a standard EKS-Cluster using Terraform modules in AWS EKS Cluster Creation and steps to request a total cost/bills waiver from AWS in AWS Bills Waiver. The waiver process is useful for other workloads and not just this cluster set up.&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS EKS Cluster Creation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Essentials:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;An AWS Account, create a free tier &lt;a href="https://portal.aws.amazon.com/gp/aws/developer/registration/index.html?refid=99f831a2-d162-429a-9a77-a89f6b3bd6cd" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Be logged in an IAM user with Admin priviledges, secret and access keys. &lt;/li&gt;
&lt;li&gt;Ubuntu Host OS (Local PC or Virtual Machine).&lt;/li&gt;
&lt;li&gt;VS code or any IDE of your choice.&lt;/li&gt;
&lt;li&gt;An S3 bucket to store the terraform state files remotely&lt;/li&gt;
&lt;li&gt;Basic Linux Terminal use&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Installations:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Install latest &lt;strong&gt;Terraform&lt;/strong&gt; on the command line:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; gnupg software-properties-common curl
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://apt.releases.hashicorp.com/gpg | &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-key add -
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-add-repository &lt;span class="s2"&gt;"deb [arch=amd64] https://apt.releases.hashicorp.com &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;lsb_release &lt;span class="nt"&gt;-cs&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; main"&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;terraform
terraform &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frxt3fa0wsh85p94lq7j5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frxt3fa0wsh85p94lq7j5.png" alt="Terraform's version Output"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install the &lt;strong&gt;Terraform extension&lt;/strong&gt; by &lt;em&gt;Anton Kulikov&lt;/em&gt; on VS code for syntax highlights and efficiency.&lt;/li&gt;
&lt;li&gt;Install &lt;strong&gt;AWS CLI&lt;/strong&gt; :
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; less curl unzip
curl &lt;span class="s2"&gt;"https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s2"&gt;"awscliv2.zip"&lt;/span&gt;
unzip &lt;span class="nt"&gt;-q&lt;/span&gt; awscliv2.zip
&lt;span class="nb"&gt;sudo&lt;/span&gt; ./aws/install
aws &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsxmufzqsxq6rwt7imsu2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsxmufzqsxq6rwt7imsu2.png" alt="AWS CLI's version Output"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Configure AWS CLI with &lt;code&gt;aws configure&lt;/code&gt; command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fill up outputs with secret and access keys previously created from IAM user&lt;/li&gt;
&lt;li&gt;add &lt;code&gt;us-east-1&lt;/code&gt; as region &lt;/li&gt;
&lt;li&gt;Test configuration details with &lt;code&gt;aws configure list&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;No need to create environment variables as runnng &lt;code&gt;env | grep AWS&lt;/code&gt; will not recognize the enviroment variables set in another terminal tab hence &lt;code&gt;aws configure&lt;/code&gt; is appropriate for global setup.
&lt;/li&gt;
&lt;/ul&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"YOUR_AWS_ACCESS_KEY_ID"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"YOUR_AWS_SECRET_ACCESS_KEY"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_DEFAULT_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;/ul&gt;



&lt;ul&gt;
&lt;li&gt;Install &lt;strong&gt;Kubectl&lt;/strong&gt; as the command line tool that interacts with the Kubernetes Cluster:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; apt-transport-https ca-certificates curl
curl &lt;span class="nt"&gt;-fsSLo&lt;/span&gt; /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/apt/sources.list.d/kubernetes.list
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; kubectl
kubectl version &lt;span class="nt"&gt;--client&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;ul&gt;
&lt;li&gt;(Optiona) Install &lt;strong&gt;Graphviz&lt;/strong&gt; to visualize the terraform plan process:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;graphviz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0xkthnfad2vkudpmrtyo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0xkthnfad2vkudpmrtyo.png" alt="Set Up"&gt;&lt;/a&gt;&lt;br&gt;
This shows the summarized version of how the cluster would be formed via the terraform module(more like a group of interwoven resources used together). Other resources such as VPC, Security groups, NAT and Internet gateways for routing traffic from inside the private subnets and the internet respectively..etc. would be created alongside it. &lt;br&gt;
This set up has the VPC created with three Public and three Private Subnets which tallies with the three availabilty zones present in &lt;code&gt;us-east-1&lt;/code&gt; region.&lt;br&gt;
&lt;br&gt;&lt;br&gt;
The values in the Terraform Configuration aren't fixed and can be changed according to individual's preference but I'll suggest that beginners follow through these exact values in order to eradicate bugs and avoidable errors.&lt;/p&gt;
&lt;h3&gt;
  
  
  Procedures:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Navigate to preferred working directory by git cloning my repository 
&lt;code&gt;git clone https://github.com/wandexdev/k8s-microservices-deployment-via-CICD-terraform.git&lt;/code&gt;, then &lt;code&gt;cd terraform/eks_creation-terraform/&lt;/code&gt; to switch directory.

&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ensure your present working directory is &lt;code&gt;~/k8s-microservices-deployment-via-CICD-terraform/terraform/eks_creation-terraform&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Confirm with the command &lt;code&gt;pwd&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;All required Terraform files(.tf) are present in the folder on my repo, you can create a folder for yours to manually write the &lt;code&gt;.tf&lt;/code&gt; files if you &lt;strong&gt;did not&lt;/strong&gt; clone the repository.&lt;/p&gt;


&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzfktp324wjvi214lackd.png" alt="Terraform"&gt;All required .tf files

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The majority configuration of the module used was gotten from &lt;a href="https://registry.terraform.io/modules/terraform-aws-modules/eks/aws/latest?tab=inputs" rel="noopener noreferrer"&gt;terraform-aws-modules/eks in Terraform's Registry&lt;/a&gt;. You can add more/switch the &lt;strong&gt;non required&lt;/strong&gt; inputs or decide which preferred resources or versions you want outside the ones I already chose in my repository.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a &lt;strong&gt;providers.tf&lt;/strong&gt; file to recognize the specific terraform-provider plugins that directly interact with AWS service.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
        provider "aws" {
            region  = var.region
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;ul&gt;
&lt;li&gt;Create a &lt;strong&gt;s3_backend.tf&lt;/strong&gt; file to store remote state files in an external S3 bucket as this ensures multiple users or CI server gets the latest state data before running terraform. Its also more secure.
&amp;gt; &lt;strong&gt;PS&lt;/strong&gt;
&amp;gt;
&amp;gt; Please ensure the S3 bucket is created prior to terraform being initialized

&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
        terraform {
        required_version = "&amp;gt;= 0.12"
        backend "s3" {
            bucket         = var.S3_bucket_name
            key            = var.bucket_key
            region         = var.region
        }
     }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;ul&gt;
&lt;li&gt;Create a &lt;strong&gt;variables.tf&lt;/strong&gt; file to declare all variables to be used in this project.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
        #----------------------------BACKEND-------------------------
        variable "S3_bucket_name" {
            description = "name of S3 bucket for remote backend storage"
        }

        variable "bucket_key" {
            description = "It's the file path inside the bucket"
        }

        variable "region" {
            description = "The region of cluster deployment"
        }

        #--------------------------------VPC-------------------------------#

        variable "vpc_cidr_block" {
            description = "ip range for vpc"
        }

        variable "private_subnet_cidr_blocks" {
            description = "ip range for private subnet"
        }

        variable "public_subnet_cidr_blocks" {
            description = "ip range for public subnets"
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;ul&gt;
&lt;li&gt;Create a &lt;strong&gt;vpc.tf&lt;/strong&gt; file for the configurations of the Virtual Private Environment the EKS-cluster would be created in.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
        #-----------DATA SOURCE-------------------#
        # By state
        data "aws_availability_zones" "available" {
        }

        module "wandek8s-vpc" {
        source                                    = "terraform-aws-modules/vpc/aws"
        version                                   = "2.64.0"

        name                                      = "wandek8s-vpc"
        cidr                                      = var.vpc_cidr_block
        private_subnets                           = var.private_subnet_cidr_blocks
        public_subnets                            = var.public_subnet_cidr_blocks
        azs                                       = data.aws_availability_zones.available.names 

        enable_nat_gateway                        = true
        single_nat_gateway                        = true
        enable_dns_hostnames                      = true

        tags                                      = {
                "kubernetes.io/cluster/wandek8s-eks-cluster" = "shared"
        }

        public_subnet_tags                        = {
                "kubernetes.io/cluster/wandek8s-eks-cluster" = "shared"
                "kubernetes.io/role/elb"            = 1
        }

        private_subnet_tags                       = {
                "kubernetes.io/cluster/wandek8s-eks-cluster" = "shared"
                "kubernetes.io/role/internal-elb"   = 1
        }
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;ul&gt;
&lt;li&gt;Create an &lt;strong&gt;outputs.tf&lt;/strong&gt; file to share data between Terraform configurations and external tools. Outputs are also the only way to share data between child module and root module of the configuration.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    output "cluster_id" {
    description       = "EKS cluster ID."
    value             = module.eks.cluster_id
    }

    output "cluster_name" {
    description       = "Amazon Web Service EKS Cluster Name"
    value             = module.eks.cluster_name
    }

    output "cluster_endpoint" {
    description       = "Endpoint for Amazon Web Service EKS "
    value             = module.eks.cluster_endpoint
    }

    output "region" {
    description       = "Amazon Web Service EKS Cluster region"
    value             = var.region
    }

    output "cluster_security_group_id" {
    description       = "Security group ID for the Amazon Web Service EKS Cluster "
    value             = module.eks.cluster_security_group_id
    }

    output "cluster_ca_certificate" {
    value             = module.eks.cluster_certificate_authority_data
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;ul&gt;
&lt;li&gt;Create an &lt;strong&gt;eks-cluster.tf&lt;/strong&gt; file which specifies the Terraform Kubernetes Provider in order to create Kubernetes objects, the module configurations, the partly managed nodes groups + node instances to be created in the cluster etc.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
        #-----------provider to communicate with Kubernetes’ resources----------#

        provider "kubernetes" {
            host                        = module.eks.cluster_endpoint # end point of k8s (API server)
            token                       = data.aws_eks_cluster_auth.wandek8s-eks-cluster.token
            cluster_ca_certificate      = base64decode(module.eks.cluster_certificate_authority_data)
        }

        #--------Query data ------------#
        #data "aws_eks_cluster" "wandek8s-eks-cluster" {
        #    name                        = module.eks.cluster_id
        #}

        data "aws_eks_cluster_auth" "wandek8s-eks-cluster" {
            name                        = module.eks.cluster_name
        }

        module "eks" {
        source                        = "terraform-aws-modules/eks/aws"
        version                       = "19.10.0"

        cluster_name                  = "wandek8s-eks-cluster"
        cluster_version               = "1.24"

        subnet_ids                    = module.wandek8s-vpc.private_subnets
        vpc_id                        = module.wandek8s-vpc.vpc_id
        cluster_endpoint_public_access = true

        tags = {
            environment = "development"
            application = "wandek8s"
        }

        eks_managed_node_groups = {
            one = {
            min_size     = 1
            max_size     = 4
            desired_size = 3

            instance_types = var.instance_type
            }
        }
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;ul&gt;
&lt;li&gt;Finally Create a &lt;strong&gt;terraform.tfvars&lt;/strong&gt; file to input the variable values defined in &lt;strong&gt;variables.tf&lt;/strong&gt; file. Herein lies the ranges for VPC CIDR block, specific resources names etc. Please ensure to input the absolute path for private and public key located in the HOST OS as mine is removed for security reasons. &lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;This is a very sensitive file that &lt;strong&gt;shouldn't&lt;/strong&gt; be pushed to version control.&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
        S3_bucket_name               = "wandek8s-bucket"

        bucket_key                   = "cicd-server/state.tfstate"

        region                       = "us-east-1"

        key_pair                     = "cicd-server"

        local_public_key_location    = ""

        local_private_key_location   = ""

        instance_type                = ["t3.medium"]

        vpc_cidr_block               = "10.0.0.0/16"

        public_subnet_cidr_blocks    = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]

        private_subnet_cidr_blocks   = ["10.0.4.0/24", "10.0.5.0/24", "10.0.6.0/24"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;ul&gt;
&lt;li&gt;Initialize the current working directory with &lt;code&gt;terraform init&lt;/code&gt;

&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2beeu6q3s7tmad3fad07.png" alt="Terraform"&gt;Terraform init Output

&lt;/li&gt;
&lt;li&gt;Next &lt;code&gt;terraform plan&lt;/code&gt; to preveiw the list of the desired state to be executed.

&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdurxl6ax9z55fr2nq59d.png" alt="Terraform"&gt;Terraform plan part Output

&lt;/li&gt;
&lt;li&gt;Optionally, we can visualize the terraform plan process before finally creating the resources in a graph via &lt;code&gt;terraform graph -type plan  | dot -Tsvg &amp;gt; graph.svg&lt;/code&gt;. A graph.svg file would be created that can abe viewed on your PC or via any svg viewing tool.&lt;/li&gt;
&lt;li&gt;Next is &lt;code&gt;terraform apply&lt;/code&gt; and patience because this process would take a little while.

&lt;img alt="Terraform"&gt;Terraform apply Output

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Console Verification: After a successful apply, we can confirm the resources created on the AWS console&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0tsfw8rgmve6rf3o5wt0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0tsfw8rgmve6rf3o5wt0.png" alt="console"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg5ftu557wxkr24uimbh2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg5ftu557wxkr24uimbh2.png" alt="console"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwmf6haeo71nkw49e5wm9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwmf6haeo71nkw49e5wm9.png" alt="console"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Validate the cluster with kubectl via &lt;code&gt;aws eks update-kubeconfig --name wandek8s-eks-cluster&lt;/code&gt; and now the cluster is fully ready for interaction and deployments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Please do remember to &lt;strong&gt;Clean up&lt;/strong&gt; the infrastructure as the spend for this set up is costly. Terraform makes this super easy by typing &lt;code&gt;terraform delete&lt;/code&gt;. If not proceed to the next section where I explain how I got my bills waived as a 1 time occurence. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  AWS Bills Waiver.
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Steps:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Navigate to the AWS console and select support centre from the drop down list&lt;/p&gt;


&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhobimutbr09smx8wory7.png" alt="Waiver"&gt;Figure 1: Support Centre on web console

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Ensure &lt;strong&gt;Account &amp;amp; Billing&lt;/strong&gt; is ticked then proceed to &lt;strong&gt;Create Case&lt;/strong&gt;&lt;/p&gt;


&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftujlqrje4bf2o3wtuuce.png" alt="Waiver"&gt;Figure 2: Create Case in Account &amp;amp; Billing

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Follow the option specifications in Figure 3 below.&lt;/p&gt;


&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnjzwqpwu0bk0r3c56kfm.png" alt="Waiver"&gt;Figure 3: Selecting Service and Category options

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fill the &lt;strong&gt;Subject&lt;/strong&gt; &lt;strong&gt;Summary&lt;/strong&gt;. I recommend an honest breakdown of the events that lead to the cost build, documents can be attached also. Here is my description version here, you can use it as a template.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz9mrjxvmafk8eruot62s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz9mrjxvmafk8eruot62s.png" alt="Case summary"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello AWS Support team,

I am requesting your assistance with my recent AWS charges. As a beginner to AWS processes, I have incurred very high unexpected charges that I am not able to afford at this moment while setting up an EKS Cluster with modules and I am hoping to find a solution that will allow me to continue learning and using AWS services without experiencing any financial burden.


I understand that some of the charges may have been due to my lack of knowledge about AWS pricing and usage. I have since taken steps to educate myself and ensure that I am aware of the free tier limits and best practices for minimizing costs and curbing hidden costs.


I would be very grateful if my account be waived off all the pending AWS charges on my account as a one-time courtesy due to my status as a new user. This would allow me to continue my learning journey with AWS without any worry about unexpected charges.


Thank you for your time and attention to this matter. I appreciate your assistance in resolving this issue.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Select &lt;strong&gt;Web&lt;/strong&gt; and you'll be contacted via chat with a wave off alongside few aws credits to clear future debts. Depending on your location, you can also select &lt;strong&gt;Phone&lt;/strong&gt;, provide your phone number and you'll be contacted by the team.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxcu4pw2hng3upn4qlywm.png" alt="Contact"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
Thank you for coming on this journey with me and I hope I have been able to guide you in 2 different ways with my experiences with AWS awesomeness. &lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
Feel free to reach out in the comment section with doubts or questions as well as directly on &lt;a href="https://twitter.com/Wandexdev" rel="noopener noreferrer"&gt;twitter&lt;/a&gt;. Cheers to a fruitful learning journey with AWS. &lt;/p&gt;

</description>
      <category>beginners</category>
      <category>aws</category>
      <category>kubernetes</category>
      <category>eks</category>
    </item>
    <item>
      <title>How to deploy a static website on a PRIVATE S3 bucket served by CloudFront on a custom domain name.</title>
      <dc:creator>Raji Risikat Yewande</dc:creator>
      <pubDate>Sat, 21 Jan 2023 10:34:39 +0000</pubDate>
      <link>https://forem.com/wandexdev/how-to-deploy-a-static-website-on-a-private-s3-bucket-served-by-cloudfront-on-a-custom-domain-name-24f5</link>
      <guid>https://forem.com/wandexdev/how-to-deploy-a-static-website-on-a-private-s3-bucket-served-by-cloudfront-on-a-custom-domain-name-24f5</guid>
      <description>&lt;h2&gt;
  
  
  Scenario:
&lt;/h2&gt;

&lt;p&gt;You're assigned a task to create an S3 bucket, with a time stamp to ensure it's unique, enable static website hosting, whilst the bucket must be private. Explore Cloudfront to privately expose your index.html file in your S3 bucket, and ensure it's accessible via a secured custom domain name.&lt;/p&gt;

&lt;h2&gt;
  
  
  Essentials:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An AWS Account.&lt;/li&gt;
&lt;li&gt;Be logged in an IAM user with Admin priviledges.&lt;/li&gt;
&lt;li&gt;Files for the static website &lt;/li&gt;
&lt;li&gt;A custom domain name. 

&lt;ul&gt;
&lt;li&gt;If you dont have, get a free domain name from &lt;a href="https://www.freenom.com/en/index.html?lang=en" rel="noopener noreferrer"&gt;freenom&lt;/a&gt;

&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh9dsjpocr094cdftfj9d.png" alt="Deployment architecture." width="757" height="637"&gt;Figure 1: Cloudfront and S3 Deployment Architecture

&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Figure 1 explains the deployment architecture.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Private Amazon S3 bucket containing files for the static website to be hosted &lt;/li&gt;
&lt;li&gt;CloudFront caches the  S3 private pages and distributes the content to its default URL&lt;/li&gt;
&lt;li&gt;CloufFront is linked to the custom domain name(wandexdev.me) via Hosted zone in Route 53.&lt;/li&gt;
&lt;li&gt;The connection is secured via SSL and Certificate Mangaer &lt;/li&gt;
&lt;li&gt;Users can access the static website by simply navigating the custom domain name(wandexdev.me)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Steps to achieve the setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Create an S3 bucket with private Access
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Navigate to your home console on your aws account, select Northern Virginia (us-east-1) as your region. This is optional and done to stay within the free tier limits of the aws account&lt;/li&gt;
&lt;li&gt;Move to the S3 console either by searching for S3 in the search bar or by clicking S3 in 'recently visited services'&lt;/li&gt;
&lt;li&gt;Select create Bucket in Figure 2 below.

&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fryhu14z0i7pi7ruyov3y.png" alt="Create Bucket." width="800" height="165"&gt;Figure 2: Create a bucket in S3 console

&lt;/li&gt;
&lt;li&gt;Set up Bucket Configuration. Refer to figures 3, 4, 5

&lt;ul&gt;
&lt;li&gt;Give a bucket name. The name has to be unique, no spaces or uppercase letters present, avoid using dots(.), hyphens(-) are more welcome. Using Timestamps helps relieve the stress.&lt;/li&gt;
&lt;li&gt;Region is US East (N. Virginia) as previously mentioned&lt;/li&gt;
&lt;li&gt;Leave Object Ownership as default

&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmbkxkm4n7d0dle9nimog.png" alt="Bucket Details" width="800" height="690"&gt;Figure 3: Bucket Name, Region and Object Ownership

&lt;/li&gt;
&lt;li&gt;Leave Public Access blocked as we want our bucket private&lt;/li&gt;
&lt;li&gt;Enable Versioning if you would like to still make changes to the static files you're about to upload but still have older versions uploaded, keep it Disabled if not. &lt;/li&gt;
&lt;li&gt;Tags are not neccessary for this simple static website demo&lt;/li&gt;
&lt;li&gt;Server-side Encryption is automatically applied so we leave encryption to default 'Amazon S3-managed keys'. The Encryption enables S3 encrypt objects before saving it and decrypts it when downloaded. Encryption doesn't change the way that you access data as an authorized user.&lt;/li&gt;
&lt;li&gt;Disable Bucket key as we are not using KMS encryption
click on Create bucket

&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvgdw6i6pnbaa0p3cpv5m.png" alt="Bucket Details" width="800" height="744"&gt;Figure 4: Public Access, Bucket Versioning and Encryption



&lt;ul&gt;
&lt;li&gt;Observe and verify new bucket details (name, region, bucket access etc) and ensure no error was made

&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F376l3805k5nmllxnq9a8.png" alt="Bucket Details" width="800" height="720"&gt;Figure 5: New Bucket details

&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 2: Upload static website files into S3 bucket created.
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Select the bucket name just created&lt;/li&gt;
&lt;li&gt;Select Upload, then select add files&lt;/li&gt;
&lt;li&gt;Add all files needed to run the static website from your local device

&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feg13x9baslnwgpvw67si.png" alt="Upload Details" width="800" height="721"&gt;Figure 6: Pre Upload Details

&lt;/li&gt;
&lt;li&gt;Leave other options at default&lt;/li&gt;
&lt;li&gt;Select Upload

&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhfzqpaa4g4vqr68fw6tb.png" alt="Upload Details" width="800" height="717"&gt;Figure 7: Uploaded Bucket Details

&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 3: Enable Static Website Hosting
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Select Bucket name, then Properties&lt;/li&gt;
&lt;li&gt;Move down to the very last option 'static website hosting' and select Edit

&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft1pd9l01yaszcm6nyc5o.png" alt="Web Static Hosting" width="800" height="213"&gt;Figure 8: Static Website Hosting Section

&lt;/li&gt;
&lt;li&gt;Select Enable and a new interface shows.&lt;/li&gt;
&lt;li&gt;Select host static website. A Static website is made up of individual webpages that contain static / unchaging content. Enabling your S3 bucket as a static website mean the website is available at the specific AWS Region website endpoint of the S3 bucket.&lt;/li&gt;
&lt;li&gt;Specify the home page of the website files i.e index.html&lt;/li&gt;
&lt;li&gt;Save changes&lt;/li&gt;
&lt;li&gt;Success message

&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3xk9p1ili7ejw8ztgck6.png" alt="Web Static Hosting" width="800" height="682"&gt;Figure 9: Successfully Enabled

&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 4: Set up Hosted Zone and name servers in Route 53
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Move to the route 53 console either by searching for Route 53 in the search bar or by clicking Route 53 in 'recently visited services'&lt;/li&gt;
&lt;li&gt;Click create hosted zone&lt;/li&gt;
&lt;li&gt;Input your custom Domain name in the Domain name field&lt;/li&gt;
&lt;li&gt;Select Public hosted zone as Type

&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu7umb0ovwsxk546zj8to.png" alt="Hosted zone" width="800" height="268"&gt;Figure 10: Hosted Zone Created in Route 53

&lt;/li&gt;
&lt;li&gt;Click on the hosted zone. The ns- values under the Value/Route traffic to are called name servers

&lt;ul&gt;
&lt;li&gt;To link custom domain name with route 53, the name servers from route 53 have to be updated on custom domain name provider's management.&lt;/li&gt;
&lt;li&gt;The name servers generally tell the domain name to redirect requests to it.

&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl406a8cxluyw65qhlvxo.png" alt="Name Servers" width="800" height="387"&gt;Figure 11: List of Name servers generated

&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Switch to a new tab in your browser (do not close the Route 53 page with the nameservers), log in your custom domain provider's site and add name servers

&lt;ul&gt;
&lt;li&gt;Mine is namecheap.com, log in, select domain list, then manage&lt;/li&gt;
&lt;li&gt;Navigate to custom DNS, select add name server and add all four name servers from Route 53 without the last dots!(.)&lt;/li&gt;
&lt;li&gt;Save changes

&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqwbkgcg7ycg661bq9l2o.png" alt="Name Servers" width="800" height="463"&gt;Figure 12: Name servers in Domain Providers site

&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 5: Set up an SSL certificate via the AWS Certificate Manager
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Setting up an SSL certificate ensures CloudFront custom domain with SSL is secured.&lt;/li&gt;
&lt;li&gt;Move to the Certificate Manager console either by searching for Certificate Manager in the search bar or by clicking Certificate Manager in 'recently visited services'&lt;/li&gt;
&lt;li&gt;Select Request a public certificate and then Request a certificate.&lt;/li&gt;
&lt;li&gt;Input your custom Domain name i.e wandexdev.me&lt;/li&gt;
&lt;li&gt;Choose DNS validation as method of validation.&lt;/li&gt;
&lt;li&gt;Select next till you can confirm and request certificate&lt;/li&gt;
&lt;li&gt;It then displays 'pending validation' on next page. This would be fixed by linking the certificate with Route 53&lt;/li&gt;
&lt;li&gt;Click Certificate ID&lt;/li&gt;
&lt;li&gt;Next click on Create record in Route 53 button under Domains.

&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frzctg2c5kqi671relcqz.png" alt="SSL Cetificate" width="690" height="602"&gt;Figure 13: Link Pending SSL Certificate with Route 53

&lt;/li&gt;
&lt;li&gt;click create records&lt;/li&gt;
&lt;li&gt;Certificate Manager takes a while before certificate is issued so please be patient.

&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh95eb6523m24ptpy2rbl.png" alt="SSL Cetificate" width="800" height="133"&gt;Figure 14: Issued SSL Certificate

&lt;/li&gt;
&lt;li&gt;Once certificate is issued, CloudFront Distribution can be set up.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 6: Set up CloudFront Distribution
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Move to the CloudFront console either by searching for CloudFront in the search bar or by clicking CloudFront in 'recently visited services'&lt;/li&gt;
&lt;li&gt;Select create Cloudfront distribution in Figure 15 below.

&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgky8235u6hqffldo696w.png" alt="CloudFront" width="800" height="388"&gt;Figure 15: Create CloudFront Distribution

&lt;/li&gt;
&lt;li&gt;Click on Origin domain and select the name of S3 bucket created from drop down list i.e wandebucket-0432pm.s3.amazonaws.com. Its simply the source from which CloudFront caches from.&lt;/li&gt;
&lt;li&gt;Origin Name automatically fills up&lt;/li&gt;
&lt;li&gt;Select Legacy access identities for Origin access. Origin Access is mainly for restricting access of a S3 bucket to only authenticated requests from CloudFront. An OAI(Origin Access Identity) would be used to do this&lt;/li&gt;
&lt;li&gt;Select Create new OAI

&lt;ul&gt;
&lt;li&gt;An autogenerated name shows in the prompt, click create

&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe97njclcnul7rhqs1iog.png" alt="Cloudfront" width="800" height="388"&gt;Figure 16: Origin Access Identity (OAI)

&lt;/li&gt;
&lt;li&gt;Select yes, update bucket policy as in Figure 17 below

&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmxmh0mhjeos5246soo76.png" alt="CloudFront" width="800" height="693"&gt;Figure 17: CloudFront Configuration



&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6v8l3nwxa5jvxe6urit1.png" alt="CloudFront" width="800" height="702"&gt;Figure 18: CloudFront Configuration

&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Select to Redirect HTTP to HTTPs&lt;/li&gt;

&lt;li&gt;Scroll down to Alternate Domain Name (CNAMEs) under Settings section then type in your custom domain name i.e wandexdev.me&lt;/li&gt;

&lt;li&gt;Still under settings, select the drop down list for Custom SSl certificate and select your issued certificate for your domain name.

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F72zbp6s94iqro4k336qp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F72zbp6s94iqro4k336qp.png" alt="CloudFront" width="800" height="865"&gt;&lt;/a&gt;&lt;/p&gt;
Figure 19: CloudFront Configuration




&lt;/li&gt;

&lt;li&gt;Scroll to Default root object and write index.html in the box&lt;/li&gt;

&lt;li&gt;Create distribuition&lt;/li&gt;

&lt;li&gt;The Distribution takes a while to deploy so be patient

&lt;ul&gt;
&lt;li&gt;Once provisioned, status changes to deployed and state turns Enabled.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 7: Link Custom domain with CloudFront via Alias in Route 53
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt; Move to the route 53 console either by searching for Route 53 in the search bar or by clicking Route 53 in 'recently visited services'&lt;/li&gt;
&lt;li&gt;Select your domain name in Hosted zone&lt;/li&gt;
&lt;li&gt;Click Create record, choose Simple routing, turn Alias on.&lt;/li&gt;
&lt;li&gt;Select the drop down menu at Route Traffic to and choose Alias to CloudFront distribution&lt;/li&gt;
&lt;li&gt;Click drop down list and select cloudfront distridution

&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqe9ljz1zyvjyyjpesm37.png" alt="Alias Record" width="800" height="768"&gt;Figure 20: Create an Alias Record for CloudFront

&lt;/li&gt;
&lt;li&gt;Wait a few minutes for the DNS records to be updated&lt;/li&gt;
&lt;li&gt;Type in your custom domain name in a browser and you would see your static website ONLY distributed and accesible via by cloudfront.
Here's mine!!

&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpp4uqkvd47qzncd92sja.png" alt="My Chess Static website" width="800" height="476"&gt;Figure 21: Final Output showing my static website

&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Remarks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;I hope you found this guide helpful and as enjoyable as I enjoyed making it. Ask me questions in the comment section and I'll surely respond.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;I used &lt;a href="https://app.diagrams.net" rel="noopener noreferrer"&gt;app.diagram&lt;/a&gt; to draw my architechture diagram and its free!&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Please dont forget to delete all AWS resources provided after youre done. Staying within the free tier limits is low cost friendly&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>watercooler</category>
    </item>
    <item>
      <title>How to create a single-tier highly available architechture on AWS</title>
      <dc:creator>Raji Risikat Yewande</dc:creator>
      <pubDate>Tue, 10 Jan 2023 22:07:03 +0000</pubDate>
      <link>https://forem.com/wandexdev/how-to-create-a-single-tier-highly-available-architechture-on-aws-49ph</link>
      <guid>https://forem.com/wandexdev/how-to-create-a-single-tier-highly-available-architechture-on-aws-49ph</guid>
      <description>&lt;h3&gt;
  
  
  Scenario
&lt;/h3&gt;

&lt;p&gt;This tutorial would take you on the process of easily creating a highly available single tier architecture on AWS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsl3c8v95co2epk9aens8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsl3c8v95co2epk9aens8.png" alt="Deployment architecture." width="723" height="675"&gt;&lt;/a&gt;&lt;br&gt;Figure 1: Single tier, fault resistant, highly available AWS Architecture
  &lt;/p&gt;

&lt;p&gt;Figure 1 explains the entire architecture setup which includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Custom VPC with two Availability Zones for High Availability&lt;/li&gt;
&lt;li&gt;Each Availability Zone has a public subnet and a private subnet.&lt;/li&gt;
&lt;li&gt;There are two EC2 instances, which are in private subnets and the Application Load Balancer(ALB) is attached to two public subnets as expected.&lt;/li&gt;
&lt;li&gt;Two Network Address Translation(NAT) Gateways are attached to the the two public subnets and they allow Internet traffic to the instances in the private subnets via an associated Elastic IP
&lt;/li&gt;
&lt;li&gt;ALB is coupled with Auto Scaling&lt;/li&gt;
&lt;li&gt;There is one Target Group connecting both the EC2 instances&lt;/li&gt;
&lt;li&gt;EC2 Apache installation happens with the user data fromm the Auto Scaling Launch template&lt;/li&gt;
&lt;li&gt;CloudFormation, an Infrasture as code tool would be used to automatically Provision the servers and Network Infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Essentials:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;An AWS Account with IAM admin Priviledges&lt;/li&gt;
&lt;li&gt;Git installed for cloning repository&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Procedures
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Navigate to the AWS Console or aws CLI &lt;/li&gt;
&lt;li&gt;If youre using the console, search for cloudformation, Click on Create a new stack.

&lt;ul&gt;
&lt;li&gt;Two Stacks would be needed for this set up&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Click this &lt;a href="https://github.com/wandexdev/singletier-aws-architecture/tree/main/cloudformation" rel="noopener noreferrer"&gt;cloudformation&lt;/a&gt; and follow instructions on the readme file.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Let me know in the comment section if you have any questions, I'll be happy to oblige. &lt;/p&gt;

</description>
      <category>welcome</category>
      <category>community</category>
    </item>
    <item>
      <title>The Oven...</title>
      <dc:creator>Raji Risikat Yewande</dc:creator>
      <pubDate>Tue, 15 Mar 2022 06:40:44 +0000</pubDate>
      <link>https://forem.com/wandexdev/the-oven-3kej</link>
      <guid>https://forem.com/wandexdev/the-oven-3kej</guid>
      <description>&lt;p&gt;"Hello World"&lt;/p&gt;

&lt;p&gt;Haha, I tend to see that phrase a lot these days so I can as well us it. So, I'm WANDE and I unofficially started my journey on this path (a career transition)in October last year.&lt;/p&gt;

&lt;p&gt;I'm trusting myself to document my journey and do some #technicalwriting to help people overcome problems I may have faced along the way. &lt;/p&gt;

&lt;p&gt;The Oven a.k.a the journey is mine because I'll be 80% baking myself (the awesome boot camps I'm in deserve credit haha) and I'm looking forward to how far i would have developed myself.&lt;/p&gt;

&lt;p&gt;Cheers to doing hard things...&lt;br&gt;
Bismillah&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>design</category>
      <category>cloud</category>
    </item>
  </channel>
</rss>
