Build 3-tier Architecture of PHP-LAMP with Terraform Tool #part-2

·

16 min read

Hello Friends,

I hope u guys like my 1st part of the 3-tier php-lamp using Terraform. Now here I am going a write 2nd part of the blog.

Let's Start...👍

Prerequestance of this project:

  1. Vscode.

  2. Aws management console account.

  3. IAM user in AWS (that user has AdministratorAcess)

  4. Generate an Access key and a Secret key for that user.

  5. install terraform extension on Vscode.

Step-1

  • Login into your Aws account by giving your "user-id" and "password".

  • Then go to the search box of AWS and search it for IAM(Identity and Access Management).

  • Create an IAM user for Access to Terraform inside the AWS cloud.

  • Give the AdminstratorAccess to that user.

  • Now generate the Access key and Secret Key for that user.

  • For this go to the user and click on security credentials.

  • After clicking on Security credentials and scrolling down slowly you will get the "Access-key" option.

  • Generate the keys. Select the CLI >> Access key and Secret generated successfully.

  • Finally, save it in the notepad for further usage.

Step-2

  • Open Vscode and install Terraform extension, Aws cli tool kit

  • Now create terraform files for creating infrastructure.

  • create a file with extension of ".tf".

vpc.tf

  • Here I created 1vpc (php-vpc) > cidr range=10.0.0.0/16

  • 2 public subnets (public-sub1, public-sub2) > cidr range=10.0.1.0/24, 10.0.2.0/24 > azs = ap-south-1a, ap-south-1b.

  • 4 private subnets (web-sub1, web-sub2, db-sub1 & db-sub2) > cidr range=10.0.3.0/24, 10.0.4.0/24, 10.0.5.0/24, 10.0.6.0/24 > azs = ap-south-1a, ap-south-1b.

  • Internet gateway.

  • NAT gateway for providing internet to private servers.

  • Elastic Ip for making static IP address to the webservers.

  • 2 Route tables (Public and private)

  • route associations.

  • subnet associations.

#create vpc

resource "aws_vpc" "php-vpc" {

cidr_block = "10.0.0.0/16"

instance_tenancy = "default"

tags = {

Name = "php-vpc"

}

}

#creating Public subnets for jump-box server

#Public subnet-1

resource "aws_subnet" "public-sub1" {

vpc_id = aws_vpc.php-vpc.id

cidr_block = "10.0.1.0/24"

map_public_ip_on_launch = true

availability_zone = "ap-south-1a"

tags = {

Name = "public-sub1"

}

}

#Public subnet-2

resource "aws_subnet" "public-sub2" {

vpc_id = aws_vpc.php-vpc.id

cidr_block = "10.0.2.0/24"

map_public_ip_on_launch = true

availability_zone = "ap-south-1b"

tags = {

Name = "public-sub2"

}

}

#creating Private Subnets

#private subnet1 - Web-app

resource "aws_subnet" "Web-sub1" {

vpc_id = aws_vpc.php-vpc.id

cidr_block = "10.0.3.0/24"

availability_zone = "ap-south-1a"

tags = {

Name = "Web-sub1"

}

}

#private subnet2 - Web-app

resource "aws_subnet" "Web-sub2" {

vpc_id = aws_vpc.php-vpc.id

cidr_block = "10.0.4.0/24"

availability_zone = "ap-south-1b"

tags = {

Name = "Web-sub2"

}

}

#Private subnet1 - Database

resource "aws_subnet" "db-sub1" {

vpc_id = aws_vpc.php-vpc.id

cidr_block = "10.0.5.0/24"

availability_zone = "ap-south-1a"

tags = {

Name = "DB-sub1"

}

}

#private subnet2 - Database

resource "aws_subnet" "db-sub2" {

vpc_id = aws_vpc.php-vpc.id

cidr_block = "10.0.6.0/24"

availability_zone = "ap-south-1b"

tags = {

Name = "DB-sub2"

}

}

# Creating Internet Gateway

resource "aws_internet_gateway" "php_igw" {

vpc_id = aws_vpc.php-vpc.id

tags = {

Name = "demoigw"

}

}

# Create NAT Gateway

resource "aws_nat_gateway" "nat_gateway" {

allocation_id = aws_eip.nat_eip.id

subnet_id = aws_subnet.public-sub1.id

tags = {

Name = "php-nat"

}

}

# Create EIP for NAT Gateway

resource "aws_eip" "nat_eip" {

vpc = true

tags = {

Name = "php-nat-eip"

}

}

# Creating Public Route Table

resource "aws_route_table" "Public_rt" {

vpc_id = aws_vpc.php-vpc.id

route {

cidr_block = "0.0.0.0/0"

gateway_id = aws_internet_gateway.php_igw.id

}

tags = {

Name = "Public Rt"

}

}

# Creating Private Route Table

resource "aws_route_table" "Private_rt" {

vpc_id = aws_vpc.php-vpc.id

tags = {

Name = "Private Rt"

}

}

# Associate NAT Gateway with the Private Route Table

resource "aws_route" "private_rt_nat_gateway" {

route_table_id = aws_route_table.Private_rt.id

destination_cidr_block = "0.0.0.0/0"

nat_gateway_id = aws_nat_gateway.nat_gateway.id

}

# Associating Route Table to jumpbox sunbets

resource "aws_route_table_association" "public_rt_association_subnet1" {

subnet_id = aws_subnet.public-sub1.id

route_table_id = aws_route_table.Public_rt.id

}

resource "aws_route_table_association" "public_rt_association_subnet2" {

subnet_id = aws_subnet.public-sub2.id

route_table_id = aws_route_table.Public_rt.id

}

# Associating Route Table to web subnets

resource "aws_route_table_association" "private_rt_assocaiation_subnet3" {

subnet_id = aws_subnet.Web-sub1.id

route_table_id = aws_route_table.Private_rt.id

}

resource "aws_route_table_association" "private_rt_assocaiation_subnet4" {

subnet_id = aws_subnet.Web-sub2.id

route_table_id = aws_route_table.Private_rt.id

}

# Associating Route Table to db sunbets

resource "aws_route_table_association" "private_rt_assocaiation_subnet5" {

subnet_id = aws_subnet.db-sub1.id

route_table_id = aws_route_table.Private_rt.id

}

resource "aws_route_table_association" "private_rt_assocaiation_subnet6" {

subnet_id = aws_subnet.db-sub2.id

route_table_id = aws_route_table.Private_rt.id

}

Pubec2.tf

  • In this file I created one jumpbox, 2 web ec2, 2sg (jumpsg & websg).

  • In user_data I given commands for installation of php, php-mysql(connection between the PHP & MySQL ), git and httpd.

  • finally in script I was given the database enpoint, database user, password and identifier.

# Creating 1st EC2 instance in Public Subnet1

resource "aws_instance" "jumpbox" {

ami = "ami-049a62eb90480f276"

instance_type = "t2.micro"

count = 1

key_name = "eminds"

vpc_security_group_ids = [aws_security_group.jumpsg.id]

subnet_id = aws_subnet.public-sub1.id

associate_public_ip_address = true

tags = {

Name = "Jumpbox-Ec2"

}

}

# Creating 1st EC2 instance in Public Subnet1

resource "aws_instance" "Web1" {

ami = "ami-049a62eb90480f276"

instance_type = "t2.micro"

count = 1

key_name = "eminds"

vpc_security_group_ids = [aws_security_group.Websg.id]

subnet_id = aws_subnet.Web-sub1.id

associate_public_ip_address = false

user_data = <<-EOT

#!/bin/bash

yum update -y

yum install git -y

yum install -y httpd wget php-fpm php-mysqli php-json php php-devel

systemctl start httpd

systemctl enable httpd

systemctl status httpd

usermod -a -G apache ec2-user

chown -R ec2-user:apache /var/www

git clone https://github.com/Akiranred/php-lamp.git /var/www/html

cd /var/www/html/php

sudo bash -c 'echo "<?php

function Createdb(){

\$servername = \"${aws_db_instance.rds_database.endpoint}\";

\$username = \"admin\";

\$password = \"12345678\";

\$dbname = \"bookstore\";

// create connection

\$con = mysqli_connect(\$servername, \$username, \$password);

// Check Connection

if (!\$con){

die(\"Connection Failed : \" . mysqli_connect_error());

}

// create Database

\$sql = \"CREATE DATABASE IF NOT EXISTS \$dbname\";

if(mysqli_query(\$con, \$sql)){

\$con = mysqli_connect(\$servername, \$username, \$password, \$dbname);

\$sql = \"

CREATE TABLE IF NOT EXISTS books(

id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,

book_name VARCHAR (25) NOT NULL,

book_publisher VARCHAR (20),

book_price FLOAT

);

\";

if(mysqli_query(\$con, \$sql)){

return \$con;

}else{

echo \"Cannot Create table...!\";

}

}else{

echo \"Error while creating database \" . mysqli_error(\$con);

}

}

?>" > /var/www/html/php/db.php'

systemctl restart httpd

EOT

tags = {

Name = "Web-Ec2-1"

}

}

# Creating 2nd EC2 instance in Public Subnet2

resource "aws_instance" "Web2" {

ami = "ami-049a62eb90480f276"

instance_type = "t2.micro"

count = 1

key_name = "eminds"

vpc_security_group_ids = [aws_security_group.Websg.id]

subnet_id = aws_subnet.Web-sub2.id

associate_public_ip_address = false

user_data = <<-EOT

#!/bin/bash

yum update -y

yum install git -y

yum install -y httpd wget php-fpm php-mysqli php-json php php-devel

systemctl start httpd

systemctl enable httpd

systemctl status httpd

usermod -a -G apache ec2-user

chown -R ec2-user:apache /var/www

git clone https://github.com/Akiranred/php-lamp.git /var/www/html

cd /var/www/html/php

sudo bash -c 'echo "<?php

function Createdb(){

\$servername = \"${aws_db_instance.rds_database.endpoint}\";

\$username = \"admin\";

\$password = \"12345678\";

\$dbname = \"bookstore\";

// create connection

\$con = mysqli_connect(\$servername, \$username, \$password);

// Check Connection

if (!\$con){

die(\"Connection Failed : \" . mysqli_connect_error());

}

// create Database

\$sql = \"CREATE DATABASE IF NOT EXISTS \$dbname\";

if(mysqli_query(\$con, \$sql)){

\$con = mysqli_connect(\$servername, \$username, \$password, \$dbname);

\$sql = \"

CREATE TABLE IF NOT EXISTS books(

id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,

book_name VARCHAR (25) NOT NULL,

book_publisher VARCHAR (20),

book_price FLOAT

);

\";

if(mysqli_query(\$con, \$sql)){

return \$con;

}else{

echo \"Cannot Create table...!\";

}

}else{

echo \"Error while creating database \" . mysqli_error(\$con);

}

}

?>" > /var/www/html/php/db.php'

systemctl restart httpd

EOT

tags = {

Name = "Web-Ec2-2"

}

}

# Creating Security Group

resource "aws_security_group" "jumpsg" {

vpc_id = aws_vpc.php-vpc.id

#inboud rules for jump box

SSH access from anywhere

ingress {

from_port = 22

to_port = 22

protocol = "tcp"

cidr_blocks = ["0.0.0.0/0"]

}

Outbound rules

egress {

from_port = 0

to_port = 0

protocol = "-1"

cidr_blocks = ["0.0.0.0/0"]

}

}

resource "aws_security_group" "Websg" {

vpc_id = aws_vpc.php-vpc.id

Inbound Rules

HTTP access from anywhere

ingress {

from_port = 80

to_port = 80

protocol = "tcp"

cidr_blocks = ["0.0.0.0/0"]

}

ingress {

from_port = 22

to_port = 22

protocol = "tcp"

cidr_blocks = ["0.0.0.0/0"]

}

# HTTPS access from anywhere

ingress {

from_port = 443

to_port = 443

protocol = "tcp"

cidr_blocks = ["0.0.0.0/0"]

}

# for database server

ingress {

from_port = 3306

to_port = 3306

protocol = "tcp"

cidr_blocks = ["0.0.0.0/0"]

}

# Outbound Rules

Internet access to anywhere

egress {

from_port = 0

to_port = 0

protocol = "-1"

cidr_blocks = ["0.0.0.0/0"]

}

tags = {

Name = "Web-SG"

}

}

Pvt.tf

  • Here I created 2 db ec2, 1 dbsg.

  • In user_data I have given commands for the installation of MariaDB and database endpoint.

# Creating 1st EC2 instance in Private Subnet1

data "aws_db_instance" "rds" {

db_instance_identifier = aws_db_instance.rds_database.identifier

}

resource "aws_instance" "dbec2-1" {

ami = "ami-049a62eb90480f276"

instance_type = "t2.micro"

count = 1

key_name = "eminds"

vpc_security_group_ids = [aws_security_group.dbsg.id]

subnet_id = aws_subnet.db-sub1.id

associate_public_ip_address = false

user_data = <<-EOT

#!/bin/bash

yum -y install mariadb105-server

systemctl start mariadb

systemctl enable mariadb

DB_ENDPOINT="${data.aws_db_instance.rds.endpoint}

EOT

tags = {

Name = "dbec2-1"

}

}

# Creating 2nd EC2 instance in Private Subnet2

resource "aws_instance" "dbec2-2" {

ami = "ami-049a62eb90480f276"

instance_type = "t2.micro"

count = 1

key_name = "eminds"

vpc_security_group_ids = [aws_security_group.dbsg.id]

subnet_id = aws_subnet.db-sub2.id

associate_public_ip_address = false

user_data = <<-EOT

#!/bin/bash

yum -y install mariadb105-server

systemctl start mariadb

systemctl enable mariadb

DB_ENDPOINT="${data.aws_db_instance.rds.endpoint}

EOT

tags = {

Name = "dbec2-2"

}

}

# Creating Security Group

resource "aws_security_group" "dbsg" {

vpc_id = aws_vpc.php-vpc.id

#inbound rules

ingress {

from_port = 22

to_port = 22

protocol = "tcp"

cidr_blocks = ["0.0.0.0/0"]

}

ingress {

description = "Allow traffic from application layer"

from_port = 3306

to_port = 3306

protocol = "tcp"

cidr_blocks = ["0.0.0.0/0"]

}

# Outbound rules

egress {

from_port = 0

to_port = 0

protocol = "-1"

cidr_blocks = ["0.0.0.0/0"]

}

tags = {

Name = "DB SG"

}

}

Credentials.tf

provider "aws" {

region = "ap-south-1"

access_key = "your_access_key"

secret_key = "<your_secret key>"

}

variables.tf

  • Here I given the variable for target group arn for connection to load balancer.

# Declare target_group_arn variable

variable "target_group_arn" {

type = string

default = "aws_lb_target_group.target-group.arn"

}

rds.tf

  • Here I created the rds of mariadb version 10.5

# Creating RDS Instance

resource "aws_db_subnet_group" "rds_database" {

name = "main"

subnet_ids = [aws_subnet.db-sub1.id, aws_subnet.db-sub2.id]

tags = {

Name = "My DB subnet group"

}

}

resource "aws_db_instance" "rds_database" {

engine = "mariadb"

engine_version = "10.5.16"

instance_class = "db.t3.micro"

allocated_storage = 20

max_allocated_storage = 1000

storage_type = "gp2"

storage_encrypted = false

auto_minor_version_upgrade = true

identifier = "bookstore"

username = "admin"

password = "12345678"

publicly_accessible = false

db_subnet_group_name = aws_db_subnet_group.bookstore_subnet_group.name

vpc_security_group_ids = [aws_security_group.dbsg.id]

skip_final_snapshot = true

tags = {

Name = "Bookstore-DB"

}

}

resource "aws_db_subnet_group" "bookstore_subnet_group" {

name = "bookstore-subnet-group"

subnet_ids = [aws_subnet.public-sub1.id, aws_subnet.public-sub2.id]

}

Loadbalancer.tf

  • Creating the application load balancer for distributing to web-server of ec2.

# Creating External LoadBalancer

resource "aws_lb" "external-alb" {

name = "PhpLB"

internal = false

load_balancer_type = "application"

security_groups = [aws_security_group.jumpsg.id, aws_security_group.Websg.id]

subnets = [aws_subnet.public-sub1.id, aws_subnet.public-sub2.id]

tags = {

Name = "Php_Lb"

}

}

resource "aws_lb_target_group" "target-group" {

name = "bookstore"

port = 80

protocol = "HTTP"

vpc_id = aws_vpc.php-vpc.id

health_check {

path = "/index.php"

interval = 5

timeout = 2

healthy_threshold = 5

unhealthy_threshold = 2

matcher = "200"

}

}

resource "aws_lb_target_group_attachment" "attachement-1" {

target_group_arn = aws_lb_target_group.target-group.arn

target_id = aws_instance.Web1[0].id

port = 80

depends_on = [

aws_instance.Web1,

]

}

resource "aws_lb_target_group_attachment" "attachment-2" {

target_group_arn = aws_lb_target_group.target-group.arn

target_id = aws_instance.Web2[0].id

port = 80

depends_on = [

aws_instance.Web2,

]

}

resource "aws_lb_listener" "external-elb" {

load_balancer_arn = aws_lb.external-alb.arn

port = "80"

protocol = "HTTP"

default_action {

type = "forward"

target_group_arn = aws_lb_target_group.target-group.arn

}

}

# Output the Load Balancer DNS

output "load_balancer_dns" {

value = aws_lb.external-alb.dns_name

}

In this blog, I dive into the essential steps of working with Terraform, an amazing infrastructure as code tool. I cover the following key aspects of the Terraform workflow:

1️⃣ Initialization (terraform init): Learn how to set up your Terraform project, configure providers, and download necessary dependencies.

2️⃣ Planning (terraform plan): Discover how to generate an execution plan that outlines the changes Terraform will make to your infrastructure.

3️⃣ Validation (terraform validate): Explore the importance of validating your Terraform code to catch any syntax or configuration errors early on.

4️⃣ Application (terraform apply): See how to apply your Terraform configuration to provision and manage your infrastructure resources.

5️⃣ Destruction (terraform destroy): Understand the process of gracefully tearing down your infrastructure when it's no longer needed.

Now with the help of the above commands we Initialized infrastructure, Validate infrastructure, Plan infrastructure, and finally apply infrastructure.

If u successfully applied the AWS infrastructure using Terraform u will get one load balancer DNS link and paste in the browser you will get the output of "Bookstore" application.

What you filled the data here, that data will automatically reflect in mariadb database like this.

Thank you for reading my block.

That's fantastic! Successfully completing the second part of your blog on the 3-tier application on AWS with Terraform.

Thank you for reading this blog. if u like this content,

Follow me on Linkedin; https://www.linkedin.com/in/ashok-sana

Follow on Whatsapp: https://chat.whatsapp.com/BzX1aruZIH645l29LxvgO3

Follow on telegram: https://t.me/awsdevopscontent

Did you find this article valuable?

Support Ashoksana by becoming a sponsor. Any amount is appreciated!