Launching Wordpress on AWS(Terraform) using MySql as Database ( Using BASTION HOST )

WordPress is a free and open-source content management system written in PHP and paired with a MySQL database. It caters to business online development initiative with content management system facilities. WordPress allows the users to build a highly customizable website of their preference, and this manner makes it popular.

Usually, if the Wordpress Web portal is to be deployed for Company’s Official perspectives, it needs Cloud services for large scale contents. We have to create a web portal for our company with all the security as much as possible.

So, we use the WordPress software with a dedicated database server. The database should not be accessible from the outside world for security purposes. We only need the public WordPress for clients. We will also use BASTION HOST for more security.

For understanding the project, you need some prior knowledge that has been discussed in my previous articles.

So, let’s proceed ahead

AWS Configure

AWS Access Key ID [********************]:
AWS Secret Access Key [********************]:
Default region name [ap-south-1]:
Default output format [None]:

We configured our aws cli so that terraform can access the AWS account.

Module Approach

A module is a container for multiple resources that are used together. Modules can be used to create lightweight abstractions, so that you can describe your infrastructure in terms of its architecture, rather than directly in terms of physical objects.

The .tf files in your working directory when you run terraform plan or terraform apply together form the root module. That module may call other modules and connect them together by passing output values from one to the input values of another.

We will use this approach for better management of the project.

main.tf

provider "aws" {
region = "ap-south-1"
# profile = "thegreat"
}
module "aws_code" {
source = "./aws"

}

This is the main file which has information regarding the provider and location of other modules.

key.tf

resource "tls_private_key" "prkey" {
algorithm = "RSA"
}
resource "aws_key_pair" "webkey1" {
key_name = "webkey1"
public_key = tls_private_key.prkey.public_key_openssh
}

This will create key pair which will help in doing ssh to the instances if required

vpc.tf

resource "aws_vpc" "vpc" {
cidr_block = "192.168.0.0/16"
instance_tenancy = "default"
enable_dns_hostnames = true
tags = {
Name = "web_vpc"
}
}

A virtual private cloud (VPC) is a virtual network dedicated to your AWS account. It is logically isolated from other virtual networks in the AWS Cloud. You can launch your AWS resources, such as Amazon EC2 instances, into your VPC. A VPC spans all of the Availability Zones in the Region.

This will create a VPC.

subnets.tf

resource "aws_subnet" "public_subnet" {
depends_on=[aws_vpc.vpc]
vpc_id = "${aws_vpc.vpc.id}"
availability_zone = "ap-south-1a"
cidr_block = "192.168.0.0/24"
map_public_ip_on_launch = true
tags = { Name = "public_subnet"
}
}
resource "aws_subnet" "private_subnet" {
depends_on=[aws_vpc.vpc]
vpc_id = "${aws_vpc.vpc.id}"
availability_zone = "ap-south-1a"
cidr_block = "192.168.1.0/24"
#map_public_ip_on_launch = true
tags = { Name = "private_subnet"
}
}

A subnet is a logical subdivision of an IP network. The practice of dividing a network into two or more networks is called subnetting.

Here, we create two subnets i.e. Public and Private

Public subnet will be accessible to outside world

Private subnet will not be accessible to outside world

internet.tf

resource "aws_internet_gateway" "int_gate" {
vpc_id = "${aws_vpc.vpc.id}"
tags = {
Name = "web_gate"
}
}
resource "aws_route_table" "public_route_table" {
depends_on=[aws_subnet.public_subnet]
vpc_id = "${aws_vpc.vpc.id}"
route {
cidr_block = "0.0.0.0/0"
gateway_id = "${aws_internet_gateway.int_gate.id}"
}
tags = {
Name = "public_route_table"
}
}
resource "aws_route_table_association" "rt_association" {
subnet_id = aws_subnet.public_subnet.id
route_table_id = aws_route_table.public_route_table.id
}

Internet gateway will connect our VPC to the internet world and attach this gateway to our VPC.

Routing table for Internet gateway so that instance can connect to the outside world, update and associate it with the public subnet.

sg.tf

resource "aws_security_group" "public" {
depends_on=[aws_subnet.public_subnet]
vpc_id = "${aws_vpc.vpc.id}"
name = "public_subnet"
description = "public_subnet"
ingress {
description = "allow_http"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}ingress {
description = "allow_https"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}ingress {
description = "allow_ssh"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}ingress {
description = "allow_icmp"
from_port = -1
to_port = -1
protocol = "icmp"
cidr_blocks = [ "0.0.0.0/0" ]
ipv6_cidr_blocks = ["::/0"]
}egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}tags = {
Name = "public_subnet"
}
}

This security group will allow to connect to website(port 80 and 443), ssh and icmp(for pinging)

resource "aws_security_group" "bositon_ssh" {
depends_on=[aws_subnet.public_subnet]
name = "bositon_ssh"
description = "Allow ssh bositon inbound traffic"
vpc_id = aws_vpc.vpc.id
ingress {
description = "allow_ssh"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}
tags = {
Name = "bositon_ssh"
}
}

This security group will allow ssh.

resource "aws_security_group" "sql_web" {
depends_on=[aws_subnet.public_subnet]
name = "sql_web"
description = "Allow only sql inbound traffic"
vpc_id = aws_vpc.vpc.id
ingress {
description = "mysql_port"
from_port = 3306
to_port = 3306
protocol = "tcp"
security_groups=[aws_security_group.public.id]

}
ingress {
description = "allow_icmp"
from_port = -1
to_port = -1
protocol = "icmp"
security_groups=[aws_security_group.public.id]
ipv6_cidr_blocks=["::/0"]

}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}
tags = {
Name = "sql_web"
}
}

This will allow to connect to mysql and allow icmp.

resource "aws_security_group" "sql_bositon_ssh" {
depends_on=[aws_subnet.public_subnet]
name = "only_ssh_sql_bositon"
description = "Allow ssh only from bositon"
vpc_id = aws_vpc.vpc.id
ingress {
description = "allow_ssh"
from_port = 22
to_port = 22
protocol = "tcp"
security_groups=[aws_security_group.bositon_ssh.id]

}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}
tags = {
Name = "sql_bositon_ssh"
}
}

This will allow ssh but from the instance only which has bositon_ssh security group.

ec2.tf

I had created AMI for WordPress and MYSQL and I am going to use that AMI for the task. You can make one for yourself or use pre-created one.

Wordpress

resource "aws_instance" "wordpress" {
depends_on=[aws_security_group.public, aws_key_pair.webkey1]
ami = "ami-0f37a73ce5f0d08fc"
instance_type = "t2.micro"
availability_zone = "ap-south-1a"
key_name = "webkey1"
subnet_id = "${aws_subnet.public_subnet.id}"
security_groups = ["${aws_security_group.public.id}"]

tags = {
Name = "wordpress"
}
}

MySQL

resource "aws_instance" "mysql" {
depends_on=[aws_security_group.sql_web,aws_security_group.sql_bositon_ssh, aws_key_pair.webkey1]
ami = "ami-0415c57c5a21a4d71"
instance_type = "t2.micro"
subnet_id= aws_subnet.private_subnet.id
vpc_security_group_ids = [aws_security_group.sql_web.id ,aws_security_group.sql_bositon_ssh.id]
key_name = "webkey1"
tags = {
Name = "mysql"
}
}

BASTION HOST

A bastion host is a special-purpose computer on a network specifically designed and configured to withstand attacks. The computer generally hosts a single application, for example, a proxy server, and all other services are removed or limited to reduce the threat to the computer.

I have launched an instance in the public subnet and attached a security group that will allow only the ssh from the public world to that instance and then created a security group for Mysql instance which will allow ssh from that Bastion host only. In this way, we can make the instance running in the Private subnet to be more secure.

resource "aws_instance" "bositon_host" {
depends_on=[aws_security_group.bositon_ssh, aws_key_pair.webkey1]
ami = "ami-0732b62d310b80e97"
instance_type = "t2.micro"
subnet_id = "${aws_subnet.public_subnet.id}"
vpc_security_group_ids = ["${aws_security_group.bositon_ssh.id}"]
key_name = "webkey1"
tags = {
Name = "bositon_host"
}
}

We need to do some configuration before proceding. We have to configure databse for wordpress in MySql. We have to import key into bastion_host because we can do ssh into mysql only from bastion_host

After that it will look something like this in wordpress and mysql resp.

How to run the codes in modules

  1. We will run terraform init command where our main.tf file is present
  2. We will run terraform plan command to see the execution plan. Terraform performs a refresh, unless explicitly disabled, and then determines what actions are necessary to achieve the desired state specified in the configuration files.
  3. Terraform apply -auto-approve command is used to apply the changes required to reach the desired state of the configuration, or the predetermined set of actions generated by a terraform plan execution plan. We use -auto-approve command, so that confirmation will not be asked
  4. Terraform destroy -auto-approve command is used to destroy the Terraform-managed infrastructure.

Output

I hope that the above article was useful to you. The complete code is provided in the GitHub link above.

In case of any queries or suggestions, DM me or comment below.

Thank You for reading

Thank You for your valuable time