Build 3-tier Architecture of PHP-LAMP with Terraform Tool #part-2
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:
Vscode.
Aws management console account.
IAM user in AWS (that user has AdministratorAcess)
Generate an Access key and a Secret key for that user.
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".
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
}
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"
}
}
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"
}
}
provider "aws" {
region = "ap-south-1"
access_key = "your_access_key"
secret_key = "<your_secret key>"
}
- 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"
}
- 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]
}
- 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