<?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>DEV Community: Vivek Raj</title>
    <description>The latest articles on DEV Community by Vivek Raj (@vraj10).</description>
    <link>https://dev.to/vraj10</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%2F888227%2Ffae84151-2411-441e-95cd-1d69a1be11b1.jpg</url>
      <title>DEV Community: Vivek Raj</title>
      <link>https://dev.to/vraj10</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vraj10"/>
    <language>en</language>
    <item>
      <title>Evolving an Application from a Single Server to a Modern Elastic Architecture P1: The Manual Process</title>
      <dc:creator>Vivek Raj</dc:creator>
      <pubDate>Mon, 18 Nov 2024 14:28:26 +0000</pubDate>
      <link>https://dev.to/vraj10/evolving-an-application-from-a-single-server-to-a-modern-elastic-architecture-p1-the-manual-process-25me</link>
      <guid>https://dev.to/vraj10/evolving-an-application-from-a-single-server-to-a-modern-elastic-architecture-p1-the-manual-process-25me</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Cloud computing has significantly transformed how applications are developed and architected. Two of the most important changes that we've seen are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Splitting applications into individual services known as microservices rather than a single large application&lt;/li&gt;
&lt;li&gt;The ability to scale parts of an application horizontally automatically instead of scaling vertically&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Microservices allow individual services to be updated independently, enabling dedicated development teams to focus on specific functionalities. This approach allows us to scale and update only the necessary services, rather than the entire application.&lt;/p&gt;

&lt;p&gt;One key benefit of horizontal scaling is the ability to spin up new instances in minutes instead of hours, days, or weeks.. This allows us to keep up with the demand for our application and prevents outages increasing our availability and overall customer experience. Another benefit is that we can add these instances and remove them when we don't need them any more, this saves us money compared to scaling the instance vertically in which we increase the size of the instance instead of adding additional instances.&lt;/p&gt;

&lt;p&gt;Along with the two above, another benefit of cloud computing is infrastructure as code (IaC). With IaC, we can define and deploy architecture programmatically, avoiding the need for manual configuration through the console. While the console might seem simpler for beginners, this project will demonstrate why IaC is a favorite among Cloud Engineers and Architects. In short, you can have your architecture in code, share that architecture with others, and spin up the exact same architecture in different regions or accounts in a matter of minutes.&lt;/p&gt;

&lt;p&gt;In this project, I will be using Adrian Cantrill's lab on architecture evolution found &lt;a href="https://github.com/acantril/learn-cantrill-io-labs/tree/master/aws-elastic-wordpress-evolution" rel="noopener noreferrer"&gt;here&lt;/a&gt;. If you are looking to learn more about AWS or are working on certifications I highly recommend checking out his courses. Building on his project, instead of clicking through the console, we'll be using Terraform to create our architecture.&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%2Fjg2acg6bcnxnlcun1oad.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%2Fjg2acg6bcnxnlcun1oad.png" alt="Image description" width="800" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial Terraform Code and Setup
&lt;/h2&gt;

&lt;p&gt;For Part One of this project, we'll be using a Terraform template I created for the base architecture of our environment. Included in this will be the VPC, subnets, route tables, IGW, as well as security groups and rules.  I've provided the code below, to use this, you'll first need to download the Terraform CLI for which the instructions can be found  &lt;a href="https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Once you download this, create a directory for your Terraform code and create a  main.tf file using the code below. If you're using a Mac like I am, open terminal and go to the directory, you can then use "&lt;strong&gt;terraform init"&lt;/strong&gt; to start up Terraform. Some things you will need as well is to export your AWS Access Key and Secret Access Key as environmental variables. This will allow for the services to be spun up in your account. As best practice you don't want to add these keys to your code, especially if you're going to be sharing them with others or uploading to a code repo. To create the env variables, you'll run&lt;/p&gt;

&lt;p&gt;export AWS_ACCESS_KEY_ID="anaccesskey"&lt;br&gt;
export AWS_SECRET_ACCESS_KEY="asecretkey"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Configure AWS Provider
provider "aws" {
    region = "us-east-1"
}

# Retrieve the list of AZs in current AWS region
data "aws_availability_zones" "available" {}
data "aws_region" "current" {} 

# Define VPC
resource "aws_vpc" "vpc" {
    cidr_block = "10.16.0.0/16"
    enable_dns_support = true
    enable_dns_hostnames = true
}


# Create an Internet Gateway
resource "aws_internet_gateway" "igw" {

    vpc_id = aws_vpc.vpc.id
}

# Create the Public Route Table
resource "aws_route_table" "rtpub" {
    vpc_id = aws_vpc.vpc.id

    route {
        cidr_block = "0.0.0.0/0"
        gateway_id = aws_internet_gateway.igw.id
    }

    route {
        ipv6_cidr_block = "::/0"
        gateway_id = aws_internet_gateway.igw.id
    }
}

# Subnet Route Table Associations
resource "aws_route_table_association" "rt_assoc_pub_a" {
  subnet_id      = aws_subnet.pub-a.id
  route_table_id = aws_route_table.rtpub.id
}

resource "aws_route_table_association" "rt_assoc_pub_b" {
  subnet_id      = aws_subnet.pub-b.id
  route_table_id = aws_route_table.rtpub.id
}

resource "aws_route_table_association" "rt_assoc_pub_c" {
  subnet_id      = aws_subnet.pub-c.id
  route_table_id = aws_route_table.rtpub.id
}

resource "aws_subnet" "db-a" {
    vpc_id = aws_vpc.vpc.id
    availability_zone = data.aws_availability_zones.available.names[0]
    cidr_block = "10.16.16.0/20"
    map_public_ip_on_launch = true


    tags = {
        Name = "sn-db-A"
    }


}

resource "aws_subnet" "db-b" {
    vpc_id = aws_vpc.vpc.id
    availability_zone = data.aws_availability_zones.available.names[1]
    cidr_block = "10.16.80.0/20"
    map_public_ip_on_launch = true

    tags = {
        Name = "sn-db-B"
    }



}

resource "aws_subnet" "db-c" {
    vpc_id = aws_vpc.vpc.id
    availability_zone = data.aws_availability_zones.available.names[2]
    cidr_block = "10.16.144.0/20"
    map_public_ip_on_launch = true

    tags = {
        Name = "sn-db-C"
    }


}

resource "aws_subnet" "app-a" {
    vpc_id = aws_vpc.vpc.id
    availability_zone = data.aws_availability_zones.available.names[0]
    cidr_block = "10.16.32.0/20"
    map_public_ip_on_launch = true

    tags = {
        Name = "sn-app-A"
    }


}

resource "aws_subnet" "app-b" {
    vpc_id = aws_vpc.vpc.id
    availability_zone = data.aws_availability_zones.available.names[1]
    cidr_block = "10.16.96.0/20"
    map_public_ip_on_launch = true

    tags = {
        Name = "sn-app-B"
    }


}

resource "aws_subnet" "app-c" {
    vpc_id = aws_vpc.vpc.id
    availability_zone = data.aws_availability_zones.available.names[2]
    cidr_block = "10.16.160.0/20"
    map_public_ip_on_launch = true

    tags = {
        Name = "sn-app-C"
    }


}

resource "aws_subnet" "pub-a" {
    vpc_id = aws_vpc.vpc.id
    availability_zone = data.aws_availability_zones.available.names[0]
    cidr_block = "10.16.48.0/20"
    map_public_ip_on_launch = true

    tags = {
        Name = "sn-pub-A"
    }


}

resource "aws_subnet" "pub-b" {
    vpc_id = aws_vpc.vpc.id
    availability_zone = data.aws_availability_zones.available.names[1]
    cidr_block = "10.16.112.0/20"
    map_public_ip_on_launch = true

    tags = {
        Name = "sn-pub-B"
    }

}

resource "aws_subnet" "pub-c" {
    vpc_id = aws_vpc.vpc.id
    availability_zone = data.aws_availability_zones.available.names[2]
    cidr_block = "10.16.176.0/20"
    map_public_ip_on_launch = true

    tags = {
        Name = "sn-pub-C"
    }


}

# Creating Security Groups

resource "aws_security_group" "sg-wordpress" {
    name = "SGWordpress"
    description = "Controls access to Wordpress Instances"
    vpc_id = aws_vpc.vpc.id
}

resource "aws_vpc_security_group_ingress_rule" "allow_http" {
    security_group_id = aws_security_group.sg-wordpress.id
    cidr_ipv4 = "0.0.0.0/0"
    from_port = 80
    to_port = 80
    ip_protocol = "tcp"

}

resource "aws_vpc_security_group_ingress_rule" "allow_nfs_inbound" {
    security_group_id = aws_security_group.sg-wordpress.id
    referenced_security_group_id = aws_security_group.sg-efs.id
    from_port = 2049
    to_port = 2049
    ip_protocol = "tcp"

}

resource "aws_vpc_security_group_egress_rule" "allow_https_outbound" {
    security_group_id = aws_security_group.sg-wordpress.id
    cidr_ipv4 = "0.0.0.0/0"
    ip_protocol = "tcp"
    from_port = 443
    to_port = 443
}

resource "aws_vpc_security_group_egress_rule" "allow_http_outbound" {
    security_group_id = aws_security_group.sg-wordpress.id
    cidr_ipv4 = "0.0.0.0/0"
    ip_protocol = "tcp"
    from_port = 80
    to_port = 80
}

resource "aws_vpc_security_group_egress_rule" "allow_mysql_outbound" {
  security_group_id = aws_security_group.sg-wordpress.id
  cidr_ipv4 = "0.0.0.0/0"
  ip_protocol = "tcp"
  from_port = 3306
  to_port = 3306
}

resource "aws_vpc_security_group_egress_rule" "allow_nfs_outbound" {
  security_group_id = aws_security_group.sg-wordpress.id
  cidr_ipv4 = "0.0.0.0/0"
  ip_protocol = "tcp"
  from_port = 2049
  to_port = 2049
}

resource "aws_security_group" "sg-database" {
    name = "SGDatabase"
    description = "Controll access to Database"
    vpc_id = aws_vpc.vpc.id
}

resource "aws_vpc_security_group_ingress_rule" "allow_mysql" {
    security_group_id = aws_security_group.sg-database.id
    from_port = 3306
    to_port = 3306
    ip_protocol = "tcp"
    referenced_security_group_id = aws_security_group.sg-wordpress.id
}

resource "aws_security_group" "sg-loadbalancer" {
    name = "SG-LoadBalancer"
    description = "Control Access to Load Balancer"
    vpc_id = aws_vpc.vpc.id
}

resource "aws_vpc_security_group_ingress_rule" "allow_http_lb" {
    security_group_id = aws_security_group.sg-loadbalancer.id
    from_port = 80
    to_port = 80
    ip_protocol = "tcp"
    cidr_ipv4 = "0.0.0.0/0"
}

resource "aws_security_group" "sg-efs" {
    name = "SG-EFS"
    description = "Control Access to EFS"
    vpc_id = aws_vpc.vpc.id
}

resource "aws_vpc_security_group_ingress_rule" "allow_efs_in" {
    security_group_id = aws_security_group.sg-efs.id
    from_port = 2049
    to_port = 2049
    ip_protocol = "tcp"
    referenced_security_group_id = aws_security_group.sg-wordpress.id

}

resource "aws_vpc_security_group_egress_rule" "allow_efs_out" {
    security_group_id = aws_security_group.sg-efs.id
    ip_protocol = "-1"
    cidr_ipv4 = "0.0.0.0/0"

}

# iam role for wordpress
resource "aws_iam_role" "wordpress_role" {
  name = "WordpressRole"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect = "Allow"
      Principal = {
        Service = "ec2.amazonaws.com"
      }
      Action = "sts:AssumeRole"
    }]
  })

  managed_policy_arns = [
    "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy",
    "arn:aws:iam::aws:policy/AmazonSSMFullAccess",
    "arn:aws:iam::aws:policy/AmazonElasticFileSystemClientFullAccess",
    "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
  ]
}

# iam instance profile
resource "aws_iam_instance_profile" "wordpress_instance_profile" {
  name = "WordpressInstanceProfile"
  path = "/"
  role = aws_iam_role.wordpress_role.name
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you've created the environment variables as well as run "&lt;strong&gt;terraform init&lt;/strong&gt;", you can next run "&lt;strong&gt;terraform plan&lt;/strong&gt;" which will give you a plan of all the things that will be created in your account, and if this all looks good, we will finally run "&lt;strong&gt;terraform apply&lt;/strong&gt;" which will actually create the resources.&lt;/p&gt;

&lt;p&gt;For this we will be creating&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A VPC with the CIDR range 10.16.0.0/16&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An Internet Gateway which will route traffic to the outside internet&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Three public, application, and database subnets&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A route table to route the traffic from the public subnets to the public internet&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Security groups for each subnet which will determine what kind of traffic is allowed in and out and from where&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An IAM role for our Wordpress instance&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This will likely take up to 10 minutes to create so nows a good time to take a little break. Once that's done we will start getting into creating our instance manually.&lt;/p&gt;




&lt;h2&gt;
  
  
  Manually Creating the Instance
&lt;/h2&gt;

&lt;p&gt;To illustrate the advantages of IaC, we'll first create an instance manually, highlighting the challenges of this approach. For brevity, I’ve skipped creating the architecture manually, as it’s a lengthy process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating Parameters
&lt;/h3&gt;

&lt;p&gt;To begin, the first thing we will do is add some parameters to SSM store, this will store our parameters, so we can call them instead of hard coding them into our Terraform file. We will append this code to the existing Terraform file, save it, and then run "&lt;strong&gt;terraform apply&lt;/strong&gt;" again to create the parameters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Create SSM Parameter Secrets
resource "aws_ssm_parameter" "DBuser" {
    name = "DBuser"
    type = "String"
    data_type = "text"
    value = "a4lwordpressuser"
}

resource "aws_ssm_parameter" "DBname" {
    name = "DBname"
    type = "String"
    data_type = "text"
    value = "a4lwordpressdb"
}


resource "aws_ssm_parameter" "DBpassword" {
    name = "DBpassword"
    type = "SecureString"
    data_type = "text"
    value = "4n1m4l54L1f3"
}

resource "aws_ssm_parameter" "DBrootpassword" {
    name = "DBrootpassword"
    type = "SecureString"
    data_type = "text"
    value = "4n1m4l54L1f3"
}

resource "aws_ssm_parameter" "DBendpoint" {
    name = "DBendpoint"
    type = "String"
    data_type = "text"
    value = "localhost"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create parameters that we will use when creating our Wordpress instance. &lt;/p&gt;

&lt;h3&gt;
  
  
  Launching the EC2 Instance
&lt;/h3&gt;

&lt;p&gt;Now lets jump over to the EC2 console and click on "launch new instance". We're gonna name this instance "Wordpress-Full" and choose Amazon Linux as our image, and choose an instance size that's eligible for the free tier.&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%2Ftas3nxkk92f1bv5gib41.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%2Ftas3nxkk92f1bv5gib41.png" alt="Image description" width="778" height="816"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We won't choose a key pair for this project, since we'll be using Session Manager to connect to our instances. We will also be using the VPC we created and the subnet pub-A, auto-assign IP also needs to be enabled, and we'll use the Wordpress security group that we created in our Terraform file&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%2Fyhr1e47furs3wpap0pol.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%2Fyhr1e47furs3wpap0pol.png" alt="Image description" width="782" height="695"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the instance IAM profile, we'll use the one create in our template as well. This will allow us to connect to our instance using Session Manager. &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%2Fbhd009g00bfuascq0wnw.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%2Fbhd009g00bfuascq0wnw.png" alt="Image description" width="777" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click 'Create' to launch the instance. Once it's running and health checks are complete, select the instance and click 'Connect' at the top right.&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%2Fvghqcakar92jqtuz2evi.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%2Fvghqcakar92jqtuz2evi.png" alt="Image description" width="800" height="217"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Connecting To and Setting Up Our Instance
&lt;/h3&gt;

&lt;p&gt;This will take us to the connection page, and in this we will use Session Manager. When we create the IAM role, we added the ability to connect using SSM, which allows for us to connect without a key pair.&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%2Fn8d9dj37kaliw8dd0fil.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%2Fn8d9dj37kaliw8dd0fil.png" alt="Image description" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Session Manager should be opened in a new tab, now we can use the "sudo bash" command, the "cd", and "clear". The next step will be to pull in the parameters we created earlier as store them as ENV variables. We will start with the DBPassword&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- DBPassword=$(aws ssm get-parameters --region us-east-1 --names DBpassword --with-decryption --query Parameters[0].Value)

- DBPassword=`echo $DBPassword | sed -e 's/^"//' -e 's/"$//'`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first command above pulls in the parameter value, and the second command removes the double quotes at the beginning and the end leaving us with only the value we stored as the parameter. Now lets do the same with the rest of our parameters&lt;br&gt;
&lt;/p&gt;

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

- DBRootPassword=$(aws ssm get-parameters --region us-east-1 --names DBrootpassword --with-decryption --query Parameters[0].Value)

- DBRootPassword=`echo $DBRootPassword | sed -e 's/^"//' -e 's/"$//'`



- DBUser=$(aws ssm get-parameters --region us-east-1 --names DBuser  --query Parameters[0].Values)

- DBUser=`echo $DBUser | sed -e 's/^"//' -e 's/"$//'`



- DBName=$(aws ssm get-parameters --region us-east-1 --names DBname --query Parameters[0].Values)

- DBName=`echo $DBName | sed -e 's/^"//' -e 's/"$//'`



- DBEndpoint=$(aws ssm get-parameters --region us-east-1 --names DBendpoint --query Parameters[0].Values)

- DBEndpoint=`echo $DBEndpoint | sed -e 's/^"//' -e 's/"$//'`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we've pulled all the parameters as env variables, we're going to update the server as well as pull the requisites we'll need including the webserver and database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo dnf -y update

sudo dnf install wget php-mysqlnd httpd php-fpm php-mysqli mariadb105-server php-json php php-devel stress -y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, set the Webserver and DB to run and start by default so if the instance were to reset, the services don't need to manually restarted&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl enable httpd
sudo systemctl enable mariadb
sudo systemctl start httpd
sudo systemctl start mariadb

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

&lt;/div&gt;



&lt;p&gt;We also need to set the root password for the DB using the ENV variable we set earlier&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo mysqladmin -u root password $DBRootPassword
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now install and extract Wordpress. If for whatever reason the download times out, make sure that you chose the pub-A subnet. If you chose a subnet that's not a public subnet, you won't be able to access the public internet and download the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo wget http://wordpress.org/latest.tar.gz -P /var/www/html
cd /var/www/html
sudo tar -zxvf latest.tar.gz
sudo cp -rvf wordpress/* .
sudo rm -R wordpress
sudo rm latest.tar.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then configure the Wordpress PHP config file with DBName, DBUser, and DBPassword&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo cp ./wp-config-sample.php ./wp-config.php
sudo sed -i "s/'database_name_here'/'$DBName'/g" wp-config.php
sudo sed -i "s/'username_here'/'$DBUser'/g" wp-config.php
sudo sed -i "s/'password_here'/'$DBPassword'/g" wp-config.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To be able to access the file we'll have to change permissions for the file as well, which we will do now&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo usermod -a -G apache ec2-user   
sudo chown -R ec2-user:apache /var/www
sudo chmod 2775 /var/www
sudo find /var/www -type d -exec chmod 2775 {} \;
sudo find /var/www -type f -exec chmod 0664 {} \;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, create the Wordpress user, set its password, and create the DB and configure permissions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo echo "CREATE DATABASE $DBName;" &amp;gt;&amp;gt; /tmp/db.setup
sudo echo "CREATE USER '$DBUser'@'localhost' IDENTIFIED BY '$DBPassword';" &amp;gt;&amp;gt; /tmp/db.setup
sudo echo "GRANT ALL ON $DBName.* TO '$DBUser'@'localhost';" &amp;gt;&amp;gt; /tmp/db.setup
sudo echo "FLUSH PRIVILEGES;" &amp;gt;&amp;gt; /tmp/db.setup
sudo mysql -u root --password=$DBRootPassword &amp;lt; /tmp/db.setup
sudo rm /tmp/db.setup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets check to see that everything was installed and configured correctly. Copy the public address of the EC2 instance, its important to open as HTTP not HTTPS or it will not open correctly. If this works, we will proceed with the initial config &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;in Site Title enter Catagram&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;in Username enter admin in Password enter 4n1m4l54L1f3&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;in Your Email enter your email address&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click Install WordPress Click Log In&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In Username or Email Address enter admin&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;in Password enter the previously noted down strong password&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click Log In&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you should be at the Wordpress home page&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%2Fo1du3xwy7y2tv4fho1ts.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%2Fo1du3xwy7y2tv4fho1ts.png" alt="Image description" width="800" height="271"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click "posts" in the menu of the left, you should see a "Hello World"  post, move that to the trash and apply. Now let's add a new post! Click "add new" and name the site "Best Animals", click "+" under the title, select "gallery" and upload a few animal pictures. Now publish the post.&lt;/p&gt;

&lt;p&gt;This is the end of Part 1! This manual process demonstrates the challenges of scaling and maintaining consistency in deployments. In the next part, we’ll leverage launch templates in our Terraform code to streamline deployment, improve scalability, and eliminate manual errors. Stay tuned!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>terraform</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Deploying a Three-Tier Architecture in AWS Using Terraform</title>
      <dc:creator>Vivek Raj</dc:creator>
      <pubDate>Tue, 12 Jul 2022 02:35:33 +0000</pubDate>
      <link>https://dev.to/vraj10/deploying-a-three-tier-architect-in-aws-with-terraform-5288</link>
      <guid>https://dev.to/vraj10/deploying-a-three-tier-architect-in-aws-with-terraform-5288</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Infrastructure as code or IaC allows an architect to deploy services using code rather than logging into the console and deploying using the GUI. The benefits of this over deploying through the console are that it allows for a more consistent environment and also the code can be reused and edited as needed which saves time. The IaC tool that I will be using in this project is Terraform which is compatible with most major cloud providers.&lt;/p&gt;

&lt;p&gt;This project will involve building out a three-tier architecture on AWS using Terraform. In a three-tier architecture an application is broken down into a web facing presentation layer, and internal application and database layers. This is a change from the previous ways of building an application where the frontend, backend, and database are all sitting in the same place. The services that we will be using to build out this architecture will be Virtual Private Cloud (VPC), Elastic Compute Cloud (EC2), Elastic Load Balancer (ELB), Security Groups, Route Tables, and Internet Gateway (IGW)&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%2Fmsnj3ghnuidf1bgvt1eb.jpg" 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%2Fmsnj3ghnuidf1bgvt1eb.jpg" alt="Image description" width="800" height="838"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Why Should We Architect This Way?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Modularity:&lt;/strong&gt;  One benefit of building our architecture out this way is that each layer can be managed separately. This can be beneficial to keeping a secure environment where users can only log in and manage the specific servers and services that they need to be working on ie. the DB developer works on the DB, front-end developer works on the front-end. Another benefit of this is more efficient troubleshooting and if one layer were to go down we can focus on that specifically without needing to take down the entire application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. High Availability:&lt;/strong&gt; Another thing we will be focusing on is high availability which we will address by building in two separate AWS availability zones. In case one availability zone were to go down for whatever reason our application would still be running in the other. This is a huge benefit of cloud compared to the traditional on-site server where if power were to go out or internet connection was lost the application would be completely down.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Security:&lt;/strong&gt; We will be building the front-end on a public subnet which will be accessed through a load balancer. Our application and database layers will be on private subnets which can only be accessed using SSH from specified security groups, or IP addresses. The security groups will allow us to set the criteria for ingress and egress traffic and specify which protocols can be used from what CIDR blocks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Consistency:&lt;/strong&gt; An important part of rolling out these projects is maintaining consistency. When we make any changes in this project, we will be doing so using Terraform and not the AWS console. The reason for this is that if we need to find out any kind of information on the configuration, we can look through our Terraform files which will let us know exactly how things are set up. Another benefit of this is we can reuse the code again, for example if we need to add additional server or if we are working on another similar project, we can use the code for this project to deploy the architecture which saves us time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Let's Get To It!
&lt;/h2&gt;

&lt;p&gt;One of the beautiful things about Terraform is that when we write the code, as long as the services are in the document it doesn't matter which order they are in. For example, if we put the VPC information after the subnet, Terraform would still build out the VPC first and then the subnets. Best practice is still to build out things in order so that's what we will be following here.&lt;/p&gt;

&lt;p&gt;Before we get started with everything else, the first piece of code we will write will be our provider configuration. This will specify which cloud provider we are using as well as the secret and access keys for your AWS account. For the secret and access keys the best practice is to use environmental variables instead of typing the values in the code. The commands for this are &lt;strong&gt;export AWS_ACCESS_KEY_ID="anaccesskey"&lt;/strong&gt; and &lt;strong&gt;export AWS_SECRET_ACCESS_KEY="asecretkey"&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~&amp;gt; 3.0"
    }
  }
}

# Configure the AWS Provider
provider "aws" {
  region     = "us-east-1"
  access_key = AWS_ACCESS_KEY_ID
  secret_key = AWS_SECRET_ACCESS_KEY
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Variables
&lt;/h2&gt;

&lt;p&gt;Another best practice when using Terraform is to use variables, the reason being is that when updating something, it provides much more consistency to update one variable rather than going in and updating every value. For example lets say we have a variable for our instance type, if we want to change the type we would just update one variable and all the code using that variable would be updated. If we didn't use a variable we would need to go in and update each item and we may forget to update an item which would lead to inconsistency in our architecture. Let's go ahead and create the variables now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vpc_cidr
&lt;/h2&gt;

&lt;p&gt;This is the default value for the CIDR block of our VPC which will be 10.0.0.0/16&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;### VPC variable

variable "vpc_cidr" {
  description = "default VPC CIDR block"
  type        = string
  default     = "10.0.0.0/16"
}

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Availability_zone_names
&lt;/h2&gt;

&lt;p&gt;This variable will create a list with the availability zones we'll be using&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;### Availability Zone variable

variable "availability_zone_names" {
  type    = list(string)
  default = ["us-east-1a", "us-east-1b"]
}

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Subnet_cidr
&lt;/h2&gt;

&lt;p&gt;These variables will be lists that hold the CIDR values for our subnets&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;### Web Subnet CIDR

variable "web_subnet_cidr" {
  type    = list(string)
  default = ["10.0.1.0/24", "10.0.2.0/24"]

}

### Application Subnet CIDR

variable "application_subnet_cidr" {
  type    = list(string)
  default = ["10.0.11.0/24", "10.0.12.0/24"]
}

### Database Subnet CIDR

variable "database_subnet_cidr" {
  type    = list(string)
  default = ["10.0.21.0/24", "10.0.22.0/24"]

}

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Rds_instance
&lt;/h2&gt;

&lt;p&gt;We will use this variable later on when we are building our database instance. It holds the values for storage, DB engine, version, instance type, and whether or not to deploy in multiple availability zones&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;### Database variables

variable "rds_instance" {
  type = map(any)
  default = {
    allocated_storage   = 10
    engine              = "mysql"
    engine_version      = "8.0.20"
    instance_class      = "db.t2.micro"
    multi_az            = true
    name                = "my_db"
    skip_final_snapshot = true
  }
}

### Create DB Variables
variable "user_information" {
  type = map(any)
  default = {
    username = "username"
    password = "password"
  }
  sensitive = true
}

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Instance Variables
&lt;/h2&gt;

&lt;p&gt;The following variables will hold the information for our EC2 instances such as the AMI ID, and the instance type&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;### Instance variable 

variable "ami_id" {
  description = "default ami"
  type        = string
  default     = "ami-0cff7528ff583bf9a"
}

variable "instance_type" {
  description = "default instance type"
  type        = string
  default     = "t2.micro"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Count Variable
&lt;/h2&gt;

&lt;p&gt;This will be used as the count for our Availability Zones and Instances, we will keep this at 2 for this project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;### Count variable

variable "item_count" {
  description = "default count used to set AZs and instances"
  type        = number
  default     = 2
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Creating our services
&lt;/h2&gt;

&lt;h2&gt;
  
  
  1. VPC
&lt;/h2&gt;

&lt;p&gt;Our first step will be to create a Virtual Private Cloud (VPC). This is a virtual network where we will be deploying our services. When creating a VPC we will give it a name and CIDR range, one thing to keep in mind here is that the CIDR range cannot be changed later.&lt;/p&gt;

&lt;p&gt;For this project we will use the very original name &lt;strong&gt;vpc-1&lt;/strong&gt; and our CIDR range will be &lt;strong&gt;10.0.0.0/16&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;### Create a VPC
resource "aws_vpc" "vpc-1" {
  cidr_block = var.vpc_cidr
  tags = {
    Name = "Demo VPC"
  }

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. IGW
&lt;/h2&gt;

&lt;p&gt;Next will be the Internet Gateway or IGW, this will allow the EC2 instances in our public subnet to access the public internet. Also in this step I've create a route table which will add the route to the public internet through our IGW, as well as a route table association which will attach the route table to the desired subnets.&lt;/p&gt;

&lt;p&gt;One thing you'll see below and in the further sections is &lt;strong&gt;count&lt;/strong&gt; which refers to variable we created earlier and that will be the number of &lt;strong&gt;route_table_associations&lt;/strong&gt; we are creating. Another thing is &lt;strong&gt;[count.index]&lt;/strong&gt; in the &lt;strong&gt;subnet_id&lt;/strong&gt; field. What this means is it will run through the two subnets we will be creating which are web-facing[0], and web-facing[1]. I will cover this a bit more in the next section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;### Create an Internet Gateway
resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.vpc-1.id

  tags = {
    Name = "IGW"
  }

}

### Create a Web Facing Routing Table
resource "aws_route_table" "public-rt" {
  vpc_id = aws_vpc.vpc-1.id


  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.igw.id

  }

  tags = {
    Name = "Public-Rt"
  }

}

### Create Subnet Association with Route Table
resource "aws_route_table_association" "a" {
  count          = var.item_count
  subnet_id      = aws_subnet.web-facing[count.index].id
  route_table_id = aws_route_table.public-rt.id
}

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Subnets
&lt;/h2&gt;

&lt;p&gt;Now that our VPC has been created we can move on to creating the subnets. For this project we will have six subnets, 2 public subnets for the presentation layer, 2 private subnets for the application layer, and 2 more private subnets for the database layer. The instances in the public subnet will have public IPs and will be able to access the internet, while the private subnets will have private IPs and only send traffic internally.&lt;/p&gt;

&lt;p&gt;As I mentioned in the previous section, we are not typing up two different subnets like &lt;strong&gt;web-facing1&lt;/strong&gt;, and &lt;strong&gt;web-facing2&lt;/strong&gt;. Instead, we are using the &lt;strong&gt;count&lt;/strong&gt; variable to reference the number 2, so it will loop twice and create two subnets which saves us from having to type out extra code giving us a cleaner look as well. We will do the same for &lt;strong&gt;cidr_block&lt;/strong&gt; which references the cidr_block variable and index we created earlier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;### Create Web Public Subnet
resource "aws_subnet" "web-facing" {
  count                   = var.item_count
  vpc_id                  = aws_vpc.vpc-1.id
  cidr_block              = var.web_subnet_cidr[count.index]
  availability_zone       = var.availability_zone_names[count.index]
  map_public_ip_on_launch = true

  tags = {
    Name = "web-${count.index + 1}"
  }

}

### Create Application Private Subnet
resource "aws_subnet" "application" {
  count                   = var.item_count
  vpc_id                  = aws_vpc.vpc-1.id
  cidr_block              = var.application_subnet_cidr[count.index]
  availability_zone       = var.availability_zone_names[count.index]
  map_public_ip_on_launch = false

  tags = {
    Name = "application-${count.index + 1}"
  }

}


### Create Database Private Subnet
resource "aws_subnet" "db" {
  count             = var.item_count
  vpc_id            = aws_vpc.vpc-1.id
  cidr_block        = var.database_subnet_cidr[count.index]
  availability_zone = var.availability_zone_names[count.index]

  tags = {
    Name = "db-${count.index + 1}"
  }

}

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Application Load Balancer
&lt;/h2&gt;

&lt;p&gt;We will be provisioning a load balancer to distribute traffic between our instances in the different availability zones. When accessing our application, the users will hit IP address of the load balancer which will then direct the traffic to our EC2 instances.&lt;/p&gt;

&lt;p&gt;Along with the load balancer itself, we also have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A target group, which we use to route requests to a registered target&lt;/li&gt;
&lt;li&gt;A target group attachment which lets us attach our instances to the load balancer&lt;/li&gt;
&lt;li&gt;A listener which checks for connection requests from the port and protocol that we specify
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;### Create External Load Balancer

resource "aws_lb" "external-lb" {
  name               = "External-LB"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.web-sg.id]
  subnets            = aws_subnet.web-facing[count.index]

  enable_deletion_protection = true
}

### Create an External Target Group

resource "aws_lb_target_group" "external-elb" {
  name     = "ALB-TG"
  port     = 80
  protocol = "HTTP"
  vpc_id   = aws_vpc.vpc-1.id
}

### Create Target Group Attachment

resource "aws_lb_target_group_attachment" "external-elb1" {
  count            = var.item_count
  target_group_arn = aws_lb_target_group.external-elb.arn
  target_id        = aws_instance.webserver[count.index].id
  port             = 80

  depends_on = [
    aws_instance.webserver,
  ]
}

### Create LB Listener

resource "aws_lb_listener" "external-elb" {
  load_balancer_arn = aws_lb.external-lb.arn
  port              = "80"
  protocol          = "HTTP"


  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.external-elb.arn
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Security Groups
&lt;/h2&gt;

&lt;p&gt;The next step is to create security groups. Security groups allow traffic from specified ports and it also lets you set where you want to allow that traffic from, whether it be a CIDR range or another AWS security group. We will create 4 security groups, one for the load balancer, one for the web server, one for the application layer, and one for the database layer.&lt;/p&gt;

&lt;p&gt;The load balancer will allow HTTP and HTTPS traffic from anywhere, the web server will allow HTTP traffic from the load balancer, the application layer will allow SSH from the public layer where we can set a bastion host, and the database layer will allow traffic on port 3306 from the application layer which would be for MySQL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;### Create Security Groups

resource "aws_security_group" "web-sg" {
  name        = "Web-SG"
  description = "Allow HTTP Inbound Traffic"
  vpc_id      = aws_vpc.vpc-1.id

  ingress {
    description = "HTTP from VPC"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    description = "HTTPS from VPC"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "Web-SG"
  }
}

### Create Web Server Security Group

resource "aws_security_group" "webserver-sg" {
  name        = "Webserver-SG"
  description = "Allow Inbound Traffic from ALB"
  vpc_id      = aws_vpc.vpc-1.id

  ingress {
    description     = "Allow traffic from web layer"
    from_port       = 80
    to_port         = 80
    protocol        = "tcp"
    security_groups = [aws_security_group.web-sg.id]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "Webserver-SG"
  }
}

### Create Application Security Group

resource "aws_security_group" "app-sg" {
  name        = "App-SG"
  description = "Allow SSH Inbound Traffic"
  vpc_id      = aws_vpc.vpc-1.id

  ingress {
    description     = "SSH from VPC"
    from_port       = 22
    to_port         = 22
    protocol        = "tcp"
    security_groups = [aws_security_group.web-sg.id]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "App-SG"
  }
}
### Created Database Security Group

resource "aws_security_group" "database-sg" {
  name        = "Database-SG"
  description = "Allow Inbound Traffic from application layer"
  vpc_id      = aws_vpc.vpc-1.id

  ingress {
    description     = "Allow traffic from application layer"
    from_port       = 3306
    to_port         = 3306
    protocol        = "tcp"
    security_groups = [aws_security_group.webserver-sg.id]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "Database-SG"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. EC2 Instances
&lt;/h2&gt;

&lt;p&gt;Now that the networking side of things has been completed, we can start working on EC2 instances. In this section we will be creating the instances for the web-facing and application layers. We will be spinning up 2 instances of both types in different availability zones. &lt;/p&gt;

&lt;p&gt;We once again use the &lt;strong&gt;count&lt;/strong&gt; variable to specify the number of instances we want to create. Also in the web-facing instances, I'm using a shell command &lt;strong&gt;file("install_apache.sh")&lt;/strong&gt; that the instances will execute on creation which will install Apache to make it a web server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;### Create EC2 Instance
resource "aws_instance" "webserver" {
  count                  = var.item_count
  ami                    = var.ami_id
  instance_type          = var.instance_type
  availability_zone      = var.availability_zone_names[count.index]
  vpc_security_group_ids = [aws_security_group.webserver-sg.id]
  subnet_id              = aws_subnet.web-facing[count.index].id
  user_data              = file("install_apache.sh")

  tags = {
    Name = "Web Server-${count.index}"
  }
}

### Create App Instance
resource "aws_instance" "appserver" {
  count                  = var.item_count
  ami                    = var.ami_id
  instance_type          = var.instance_type
  availability_zone      = var.availability_zone_names[count.index]
  vpc_security_group_ids = [aws_security_group.app-sg.id]
  subnet_id              = aws_subnet.application[count.index].id

  tags = {
    Name = "App Server-${count.index}"
  }

}

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. RDS Instance
&lt;/h2&gt;

&lt;p&gt;Next we'll move to creating the RDS instance which will be our backend database. Along with the instance itself, we will a subnet_group for our db instances. This is a subnet we specify for all of our database instances.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;### Create RDS Instance

resource "aws_db_instance" "default" {
  allocated_storage      = var.rds_instance.allocated_storage
  db_subnet_group_name   = aws_db_subnet_group.default.id
  engine                 = var.rds_instance.engine
  engine_version         = var.rds_instance.engine_version
  instance_class         = var.rds_instance.instance_class
  multi_az               = var.rds_instance.multi_az
  name                   = var.rds_instance.name
  username               = var.user_information.username
  password               = var.user_information.password
  skip_final_snapshot    = var.rds_instance.skip_final_snapshot
  vpc_security_group_ids = [aws_security_group.database-sg.id]

}

### Create RDS Subnet Group

resource "aws_db_subnet_group" "default" {
  name       = "main"
  subnet_ids = aws_subnet.db[count.index]

  tags = {
    name = "My DB subnet group"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  8.Output
&lt;/h2&gt;

&lt;p&gt;This next step will print out the DNS name of the load balancer into our terminal so that once our instances are up and running, we can copy and paste this into our browser to make sure that we are able to access the site.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;### Create ouput to print

output "lb_dns_name" {
  description = "The DNS name of the load balancer"
  value       = aws_lb.external-lb.dns_name

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  9. Terraform Commands
&lt;/h2&gt;

&lt;p&gt;Now that the template for our services has been created, the next step is to initialize Terraform and get our infrastructure built out. These are the Terraform commands that we will use to get things going. We will first need to open terminal and move to the directory in which we have our Terraform files.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Terraform init&lt;/strong&gt; will initialize Terraform&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terraform fmt&lt;/strong&gt; will make sure that our code is in the correct format and will modify our code to match the format&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terraform validate&lt;/strong&gt; will make sure that we have no errors in our syntax and if we do it will return the file and line where the error occurs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terraform plan&lt;/strong&gt; is what we will do to plan out and see what resources will be created&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terraform apply&lt;/strong&gt; after we run the plan command and ensure everything looks correct, we will run the apply command which is what actually builds out our infrastructure.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  10. Testing
&lt;/h2&gt;

&lt;p&gt;Once the services have been created, the terminal should also list the DNS name of our load balancer. We will copy and paste this into our browser to make sure that we are able to access the site.&lt;/p&gt;

&lt;h2&gt;
  
  
  11. Clean Up
&lt;/h2&gt;

&lt;p&gt;Whenever we are finished with this project it is important to delete our infrastructure so that we don't continue to get charged. To do so we issue the &lt;strong&gt;Terraform destroy&lt;/strong&gt; command which will delete everything we have created.&lt;/p&gt;

&lt;p&gt;Success! We've just created and our three-tier AWS architecture using Terraform&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;When I first started using AWS I wondered why people would use the CLI or an IaC tool when creating their architecture instead of the GUI. For a beginner the GUI just seems so much easier to use, but after playing around with Terraform I can clearly see why this is the preferred way. &lt;/p&gt;

&lt;p&gt;It is much easier and quicker to create services this way, and as an added benefit you have the code that you can reuse for other projects which saves a lot of time, and while Terraform code may look daunting for someone new to the tool, it is actually really easy to understand. Also the documentation on the Terraform website really is amazing and makes using the tool so much easier.&lt;/p&gt;




&lt;h2&gt;
  
  
  Full Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~&amp;gt; 3.0"
    }
  }
}

# Configure the AWS Provider
provider "aws" {
  region     = "us-east-1"
  access_key = AWS_ACCESS_KEY_ID
  secret_key = AWS_SECRET_ACCESS_KEY
}


### VPC variable

variable "vpc_cidr" {
  description = "default VPC CIDR block"
  type        = string
  default     = "10.0.0.0/16"
}

### Availability Zone variable

variable "availability_zone_names" {
  type    = list(string)
  default = ["us-east-1a", "us-east-1b"]
}

### Web Subnet CIDR

variable "web_subnet_cidr" {
  type    = list(string)
  default = ["10.0.1.0/24", "10.0.2.0/24"]

}

### Application Subnet CIDR

variable "application_subnet_cidr" {
  type    = list(string)
  default = ["10.0.11.0/24", "10.0.12.0/24"]
}

### Database Subnet CIDR

variable "database_subnet_cidr" {
  type    = list(string)
  default = ["10.0.21.0/24", "10.0.22.0/24"]

}

### Database variables

variable "rds_instance" {
  type = map(any)
  default = {
    allocated_storage   = 10
    engine              = "mysql"
    engine_version      = "8.0.20"
    instance_class      = "db.t2.micro"
    multi_az            = true
    name                = "my_db"
    skip_final_snapshot = true
  }
}

### Create DB Variables
variable "user_information" {
  type = map(any)
  default = {
    username = "username"
    password = "password"
  }
  sensitive = true
}

### Instance variable 

variable "ami_id" {
  description = "default ami"
  type        = string
  default     = "ami-0cff7528ff583bf9a"
}

variable "instance_type" {
  description = "default instance type"
  type        = string
  default     = "t2.micro"
}
### Count variable

variable "item_count" {
  description = "default count used to set AZs and instances"
  type        = number
  default     = 2
}

### Create a VPC
resource "aws_vpc" "vpc-1" {
  cidr_block = "10.0.0.0/16"
  tags = {
    Name = "Demo VPC"
  }

}


### Create an Internet Gateway
resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.vpc-1.id

  tags = {
    Name = "IGW"
  }

}

### Create a Web Facing Routing Table
resource "aws_route_table" "public-rt" {
  vpc_id = aws_vpc.vpc-1.id


  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.igw.id

  }

  tags = {
    Name = "Public-Rt"
  }

}

### Create Subnet Association with Route Table
resource "aws_route_table_association" "a" {
  count          = var.item_count
  subnet_id      = aws_subnet.web-facing[count.index].id
  route_table_id = aws_route_table.public-rt.id
}

### Create Web Public Subnet
resource "aws_subnet" "web-facing" {
  count                   = var.item_count
  vpc_id                  = aws_vpc.vpc-1.id
  cidr_block              = var.web_subnet_cidr[count.index]
  availability_zone       = var.availability_zone_names[count.index]
  map_public_ip_on_launch = true

  tags = {
    Name = "web-${count.index + 1}"
  }

}

### Create Application Private Subnet
resource "aws_subnet" "application" {
  count                   = var.item_count
  vpc_id                  = aws_vpc.vpc-1.id
  cidr_block              = var.application_subnet_cidr[count.index]
  availability_zone       = var.availability_zone_names[count.index]
  map_public_ip_on_launch = false

  tags = {
    Name = "application-${count.index + 1}"
  }

}


### Create Database Private Subnet
resource "aws_subnet" "db" {
  count             = var.item_count
  vpc_id            = aws_vpc.vpc-1.id
  cidr_block        = var.database_subnet_cidr[count.index]
  availability_zone = var.availability_zone_names[count.index]

  tags = {
    Name = "db-${count.index + 1}"
  }

}

### Create External Load Balancer

resource "aws_lb" "external-lb" {
  name               = "External-LB"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.web-sg.id]
  subnets            = [aws_subnet.web-facing[0].id, aws_subnet.web-facing[1].id]

  enable_deletion_protection = true
}

### Create Internal Load Balancer

resource "aws_lb" "internal-lb" {
  name               = "Internal-LB"
  internal           = true
  load_balancer_type = "application"
  security_groups    = [aws_security_group.app-sg.id]
  subnets            = [aws_subnet.application[0].id, aws_subnet.application[1].id]

  enable_deletion_protection = true
}

### Create an External Target Group

resource "aws_lb_target_group" "external-elb" {
  name     = "ALB-TG"
  port     = 80
  protocol = "HTTP"
  vpc_id   = aws_vpc.vpc-1.id
}

### Create and Internal Target Group

resource "aws_lb_target_group" "internal-elb" {
  name     = "ILB-TG"
  port     = 80
  protocol = "HTTP"
  vpc_id   = aws_vpc.vpc-1.id
}
### Create Target Group Attachment

resource "aws_lb_target_group_attachment" "external-elb1" {
  count            = var.item_count
  target_group_arn = aws_lb_target_group.external-elb.arn
  target_id        = aws_instance.webserver[count.index].id
  port             = 80

  depends_on = [
    aws_instance.webserver,
  ]
}

resource "aws_lb_target_group_attachment" "internal-elb1" {
  count            = var.item_count
  target_group_arn = aws_lb_target_group.internal-elb.arn
  target_id        = aws_instance.appserver[count.index].id
  port             = 80

  depends_on = [
    aws_instance.webserver,
  ]
}


### Create LB Listener

resource "aws_lb_listener" "external-elb" {
  load_balancer_arn = aws_lb.external-lb.arn
  port              = "80"
  protocol          = "HTTP"


  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.external-elb.arn
  }
}

resource "aws_lb_listener" "internal-elb" {
  load_balancer_arn = aws_lb.internal-lb.arn
  port              = "80"
  protocol          = "HTTP"


  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.internal-elb.arn
  }
}

### Create Security Groups

resource "aws_security_group" "web-sg" {
  name        = "Web-SG"
  description = "Allow HTTP Inbound Traffic"
  vpc_id      = aws_vpc.vpc-1.id

  ingress {
    description = "HTTP from VPC"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    description = "HTTPS from VPC"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "Web-SG"
  }
}

### Create Web Server Security Group

resource "aws_security_group" "webserver-sg" {
  name        = "Webserver-SG"
  description = "Allow Inbound Traffic from ALB"
  vpc_id      = aws_vpc.vpc-1.id

  ingress {
    description     = "Allow traffic from web layer"
    from_port       = 80
    to_port         = 80
    protocol        = "tcp"
    security_groups = [aws_security_group.web-sg.id]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "Webserver-SG"
  }
}

### Create Application Security Group

resource "aws_security_group" "app-sg" {
  name        = "App-SG"
  description = "Allow SSH Inbound Traffic"
  vpc_id      = aws_vpc.vpc-1.id

  ingress {
    description     = "SSH from VPC"
    from_port       = 22
    to_port         = 22
    protocol        = "tcp"
    security_groups = [aws_security_group.web-sg.id]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "App-SG"
  }
}
### Created Database Security Group

resource "aws_security_group" "database-sg" {
  name        = "Database-SG"
  description = "Allow Inbound Traffic from application layer"
  vpc_id      = aws_vpc.vpc-1.id

  ingress {
    description     = "Allow traffic from application layer"
    from_port       = 3306
    to_port         = 3306
    protocol        = "tcp"
    security_groups = [aws_security_group.webserver-sg.id]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "Database-SG"
  }
}

### Create EC2 Instance
resource "aws_instance" "webserver" {
  count                  = var.item_count
  ami                    = var.ami_id
  instance_type          = var.instance_type
  availability_zone      = var.availability_zone_names[count.index]
  vpc_security_group_ids = [aws_security_group.webserver-sg.id]
  subnet_id              = aws_subnet.web-facing[count.index].id
  user_data              = file("install_apache.sh")

  tags = {
    Name = "Web Server-${count.index}"
  }
}

### Create App Instance
resource "aws_instance" "appserver" {
  count                  = var.item_count
  ami                    = var.ami_id
  instance_type          = var.instance_type
  availability_zone      = var.availability_zone_names[count.index]
  vpc_security_group_ids = [aws_security_group.app-sg.id]
  subnet_id              = aws_subnet.application[count.index].id

  tags = {
    Name = "App Server-${count.index}"
  }

}
### Create RDS Instance

resource "aws_db_instance" "default" {
  allocated_storage      = var.rds_instance.allocated_storage
  db_subnet_group_name   = aws_db_subnet_group.default.id
  engine                 = var.rds_instance.engine
  engine_version         = var.rds_instance.engine_version
  instance_class         = var.rds_instance.instance_class
  multi_az               = var.rds_instance.multi_az
  name                   = var.rds_instance.name
  username               = var.user_information.username
  password               = var.user_information.password
  skip_final_snapshot    = var.rds_instance.skip_final_snapshot
  vpc_security_group_ids = [aws_security_group.database-sg.id]

}

### Create RDS Subnet Group

resource "aws_db_subnet_group" "default" {
  name       = "main"
  subnet_ids = aws_subnet.db[count.index]

  tags = {
    name = "My DB subnet group"
  }
}

### Create ouput to print

output "lb_dns_name" {
  description = "The DNS name of the load balancer"
  value       = aws_lb.external-lb.dns_name

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

&lt;/div&gt;



</description>
      <category>devops</category>
      <category>cloud</category>
      <category>terraform</category>
      <category>aws</category>
    </item>
  </channel>
</rss>
