How To Deploy A LAMP Stack In AWS Using Terraform

How to deploy a LAMP stack in AWS using terraform

Hello Everyone

Welcome to CloudAffaire and this is Debjeet.

In the last blog post, we have discussed terraform override.

https://cloudaffaire.com/terraform-override/

In this blog post, we will discuss how to deploy a LAMP stack in AWS using terraform.

What is a LAMP stack?

A LAMP stack is simply a web application created on Linux server using Apache, PHP and MySQL (L=Linux, A=Apache, M=MySQL, P=PHP). In this demo, we will use an EC2 Linux instance to create our frontend web application and will use AWS MySQL RDS as backend database.

How to deploy a LAMP stack in AWS using terraform:

#create a directory and get inside it
mkdir terraform && cd terraform

#create index.php (shows web pasge visit counts)
vi index.php
----------------------
<?php
$link = mysql_connect('<your_aws_mysql_rds_endpoint>', 'myuser', 'mypassword');
if (!$link)
{
die('Could not connect: ' . mysql_error());
}
else
{
$selectdb = mysql_select_db("mydb");
if (!$selectdb)
{
die('Could not connect: ' . mysql_error());
}
else
{
$data = mysql_query("SELECT visits FROM counter");
if (!$data)
{
die('Could not connect: ' . mysql_error());
}
else
{
$add=mysql_query("UPDATE counter SET visits = visits+1");
if(!$add)
{
die('Could not connect: ' . mysql_error());
}
else
{
print "<table><tr><th>Visits</th></tr>";
while($value=mysql_fetch_array($data))
{
print "<tr><td>".$value['visits']."</td></tr>";
}
print "</table>";
}
}
}
}
mysql_close($link);
?>
-----------------------
:wq

#create main.tf
vi main.tf
-----------------------
#creates VPC, one public subnet, two private subnets, one EC2 instance and one MYSQL RDS instance
#declare variables
variable "access_key" {
default = "<your_access_key>"
}
variable "secret_key" {
default = "<your_secret_key>"
}
variable "region" {
default = "ap-south-1"
}
variable "vpc_cidr" {
default = "10.0.0.0/16"
}
variable "subnet_one_cidr" {
default = "10.0.1.0/24"
}
variable "subnet_two_cidr" {
default = ["10.0.2.0/24","10.0.3.0/24"]
}
variable "route_table_cidr" {
default = "0.0.0.0/0"
}
variable "web_ports" {
default = ["22","80", "443", "3306"]
}
variable "db_ports" {
default = ["22", "3306"]
}
variable "images" {
type = "map"
default = {
"us-east-1" = "ami-0937dcc711d38ef3f"
"us-east-2" = "ami-04328208f4f0cf1fe"
"us-west-1" = "ami-0799ad445b5727125"
"us-west-2" = "ami-032509850cf9ee54e"
"ap-south-1" = "ami-0937dcc711d38ef3f"
"ap-northeast-2" = "ami-018a9a930060d38aa"
"ap-southeast-1" = "ami-04677bdaa3c2b6e24"
"ap-southeast-2" = "ami-0c9d48b5db609ad6e"
"ap-northeast-1" = "ami-0d7ed3ddb85b521a6"
"ca-central-1" = "ami-0de8b8e4bc1f125fe"
"eu-central-1" = "ami-0eaec5838478eb0ba"
"eu-west-1" = "ami-0fad7378adf284ce0"
"eu-west-2" = "ami-0664a710233d7c148"
"eu-west-3" = "ami-0854d53ce963f69d8"
"eu-north-1" = "ami-6d27a913"
}
}
#aws provider
provider "aws" {
access_key = "${var.access_key}"
secret_key = "${var.secret_key}"
region = "${var.region}"
}
#get AZ's details
data "aws_availability_zones" "availability_zones" {}
#create VPC
resource "aws_vpc" "myvpc" {
cidr_block = "${var.vpc_cidr}"
enable_dns_hostnames = true
tags {
Name = "myvpc"
}
}
#create public subnet
resource "aws_subnet" "myvpc_public_subnet" {
vpc_id = "${aws_vpc.myvpc.id}"
cidr_block = "${var.subnet_one_cidr}"
availability_zone = "${data.aws_availability_zones.availability_zones.names[0]}"
map_public_ip_on_launch = true
tags {
   Name = "myvpc_public_subnet"
  }
}
#create private subnet one
resource "aws_subnet" "myvpc_private_subnet_one" {
vpc_id = "${aws_vpc.myvpc.id}"
cidr_block = "${element(var.subnet_two_cidr, 0)}"
availability_zone = "${data.aws_availability_zones.availability_zones.names[0]}"
tags {
   Name = "myvpc_private_subnet_one"
  }
}
#create private subnet two
resource "aws_subnet" "myvpc_private_subnet_two" {
vpc_id = "${aws_vpc.myvpc.id}"
cidr_block = "${element(var.subnet_two_cidr, 1)}"
availability_zone = "${data.aws_availability_zones.availability_zones.names[1]}"
tags {
   Name = "myvpc_private_subnet_two"
  }
}
#create internet gateway
resource "aws_internet_gateway" "myvpc_internet_gateway" {
vpc_id = "${aws_vpc.myvpc.id}"
tags {
Name = "myvpc_internet_gateway"
}
}
#create public route table (assosiated with internet gateway)
resource "aws_route_table" "myvpc_public_subnet_route_table" {
vpc_id = "${aws_vpc.myvpc.id}"
route {
cidr_block = "${var.route_table_cidr}"
gateway_id = "${aws_internet_gateway.myvpc_internet_gateway.id}"
}
tags {
Name = "myvpc_public_subnet_route_table"
}
}
#create private subnet route table
resource "aws_route_table" "myvpc_private_subnet_route_table" {
vpc_id = "${aws_vpc.myvpc.id}"
tags {
Name = "myvpc_private_subnet_route_table"
}
}
#create default route table
resource "aws_default_route_table" "myvpc_main_route_table" {
default_route_table_id = "${aws_vpc.myvpc.default_route_table_id}"
tags = {
Name = "myvpc_main_route_table"
}
}
#assosiate public subnet with public route table
resource "aws_route_table_association" "myvpc_public_subnet_route_table" {
subnet_id = "${aws_subnet.myvpc_public_subnet.id}"
route_table_id = "${aws_route_table.myvpc_public_subnet_route_table.id}"
}
#assosiate private subnets with private route table
resource "aws_route_table_association" "myvpc_private_subnet_one_route_table_assosiation" {
subnet_id = "${aws_subnet.myvpc_private_subnet_one.id}"
route_table_id = "${aws_route_table.myvpc_private_subnet_route_table.id}"
}
resource "aws_route_table_association" "myvpc_private_subnet_two_route_table_assosiation" {
subnet_id = "${aws_subnet.myvpc_private_subnet_two.id}"
route_table_id = "${aws_route_table.myvpc_private_subnet_route_table.id}"
}
#create security group for web
resource "aws_security_group" "web_security_group" {
name = "web_security_group"
description = "Allow all inbound traffic"
vpc_id = "${aws_vpc.myvpc.id}"
tags {
Name = "myvpc_web_security_group"
}
}
#create security group ingress rule for web
resource "aws_security_group_rule" "web_ingress" {
count = "${length(var.web_ports)}"
type = "ingress"
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
from_port = "${element(var.web_ports, count.index)}"
to_port = "${element(var.web_ports, count.index)}"
security_group_id = "${aws_security_group.web_security_group.id}"
}
#create security group egress rule for web
resource "aws_security_group_rule" "web_egress" {
count = "${length(var.web_ports)}
type = "egress"
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
from_port = "${element(var.web_ports, count.index)}"
to_port = "${element(var.web_ports, count.index)}"
security_group_id = "${aws_security_group.web_security_group.id}"
}
#create security group for db
resource "aws_security_group" "db_security_group" {
name = "db_security_group"
description = "Allow all inbound traffic"
vpc_id = "${aws_vpc.myvpc.id}"
tags {
Name = "myvpc_db_security_group"
}
}
#create security group ingress rule for db
resource "aws_security_group_rule" "db_ingress" {
count = "${length(var.db_ports)}"
type = "ingress"
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
from_port = "${element(var.db_ports, count.index)}"
to_port = "${element(var.db_ports, count.index)}"
security_group_id = "${aws_security_group.db_security_group.id}"
}
#create security group egress rule for db
resource "aws_security_group_rule" "db_egress" {
count = "${length(var.db_ports)}"
type = "egress"
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
from_port = "${element(var.db_ports, count.index)}"
to_port = "${element(var.db_ports, count.index)}"
security_group_id = "${aws_security_group.db_security_group.id}"
}
#create EC2 instance
resource "aws_instance" "my_web_instance" {
ami = "${lookup(var.images,var.region)}"
instance_type = "t2.micro"
key_name = "<your_private_key>" #make sure you have your_private_ket.pem file
vpc_security_group_ids = ["${aws_security_group.web_security_group.id}"]
subnet_id = "${aws_subnet.myvpc_public_subnet.id}"
tags = {
Name = "my_web_instance"
}
volume_tags = {
Name = "my_web_instance_volume"
}
provisioner "remote-exec" { #install apache, mysql client, php
inline = [
"sudo mkdir -p /var/www/html/",
"sudo yum update -y",
"sudo yum install -y httpd",
"sudo service httpd start",
"sudo usermod -a -G apache ec2-user",
"sudo chown -R ec2-user:apache /var/www",
"sudo yum install -y mysql php php-mysql"
]
}
provisioner "file" { #copy the index file form local to remote
source = "index.php"
destination = "/var/www/html/index.php"
}
connection {
type = "ssh"
user = "ec2-user"
password = ""
#copy <your_private_key>.pem to your local instance home directory
#restrict permission: chmod 400 <your_private_key>.pem
private_key = "${file("/home/ec2-user/<your_private_key>.pem")}"
}
}
#create aws rds subnet groups
resource "aws_db_subnet_group" "my_database_subnet_group" {
name = "mydbsg"
subnet_ids = ["${aws_subnet.myvpc_private_subnet_one.id}", "${aws_subnet.myvpc_private_subnet_two.id}"]
tags = {
Name = "my_database_subnet_group"
}
}
#create aws mysql rds instance
resource "aws_db_instance" "my_database_instance" {
allocated_storage = 20
storage_type = "gp2"
engine = "mysql"
engine_version = "5.7"
instance_class = "db.t2.micro"
port = 3306
vpc_security_group_ids = ["${aws_security_group.db_security_group.id}"]
db_subnet_group_name = "${aws_db_subnet_group.my_database_subnet_group.name}"
name = "mydb"
identifier = "mysqldb"
username = "myuser"
password = "mypassword"
parameter_group_name = "default.mysql5.7"
skip_final_snapshot = true
tags = {
Name = "my_database_instance"
}
}
#output webserver and dbserver address
output "db_server_address" {
value = "${aws_db_instance.my_database_instance.address}"
}
output "web_server_address" {
value = "${aws_instance.my_web_instance.public_dns}"
}
-----------------------
:wq

#format code
terraform fmt

#Initialized terraform (downloads provider [AWS] packages)
terraform init

#create resource
terraform apply

Copy the db_server_address and web_server_address from the output.

How to deploy a LAMP stack in AWS using terraform

Connect to EC2 instance (web server)

ssh -i "/home/ec2-user/<your_private_key>.pem" <web_server_address>

Edit index.php in EC2 instance (web server)

vi /var/www/html/index.php
----------------------
#change only <your_aws_mysql_rds_endpoint> with db_server_address from output
$link = mysql_connect('<your_aws_mysql_rds_endpoint>', 'myuser', 'mypassword');
----------------------
:wq

How to deploy a LAMP stack in AWS using terraform

#connect to mysql rds instance
mysql -h <db_server_address> -P 3306 -u myuser -p
#create table and insert data
USE mydb;
CREATE TABLE counter (visits int(10) NOT NULL);
INSERT INTO counter(visits) values(1);

How to deploy a LAMP stack in AWS using terraform

Open web_server_address in the browser, hit refresh and check

How to deploy a LAMP stack in AWS using terraform

LAMP stack successfully deployed in AWS using terraform.

Note: You may need to restart your web server.

#cleanup
#exit mysql rds instance
quit

#exit EC2 instance (web server)
exit

#destory
terraform destory

#remove terraform directory
cd .. && rm -rf terraform

Hope you have enjoyed this article. In the next blog post, we will modularize our LAMP stack code.

To get more details on terraform, please refer below terraform documentation

https://www.terraform.io/docs/index.html

 

Leave a Reply

Close Menu