Creating the public wordpress and private MySQL database with NAT gateway on AWS using terraform

Sriramadasu Prasanth Kumar
7 min readOct 13, 2020

In this article, I am going to demonstrate how to create a public subnet for wordpress and a private subnet for SQL server with NAT gateway and internet gateway using terraform.

What is Terraform?

Terraform is a tool for building, changing, and versioning infrastructure safely and efficiently. Terraform can manage existing and popular service providers as well as custom in-house solutions. By using the teraform we can manage the multiple clouds. By knowing 1 language we can create infrastructure in all clouds. Terraform provides the CAAS(Code As A Service) which provides the code for creating and destroying the infrastructure in just 1 click.

Problem Statement:

Performing the following steps:
1. Write an Infrastructure as code using terraform, which automatically create a VPC.
2. In that VPC we have to create 2 subnets:
1. public subnet [ Accessible for Public World! ]
2. private subnet [ Restricted for Public World! ]
3. Create a public facing internet gateway for connect our VPC/Network to the internet world and attach this gateway to our VPC.
4. Create a routing table for Internet gateway so that instance can connect to outside world, update and associate it with public subnet.
5. Create a NAT gateway for connect our VPC/Network to the internet world and attach this gateway to our VPC in the public network
6. Update the routing table of the private subnet, so that to access the internet it uses the nat gateway created in the public subnet
7. Launch an ec2 instance which has Wordpress setup already having the security group allowing port 80 sothat our client can connect to our wordpress site. Also attach the key to instance for further login into it.
8. Launch an ec2 instance which has MYSQL setup already with security group allowing port 3306 in private subnet so that our wordpress vm can connect with the same. Also attach the key with the same.

Note: Wordpress instance has to be part of public subnet so that our client can connect our site.
mysql instance has to be part of private subnet so that outside world can’t connect to it.
Don’t forgot to add auto ip assign and auto dns name assignment option to be enabled.


We are creating this infrastructure using terraform.


In this project we are using the AWS cloud so we are using the provider “AWS”.

provider “aws” {
profile = “prasanth”
region = “ap-south-1”

Here profile is used for security for the access key and secret key.

Creating the profile:

aws configure --profile user

Then it will prompt for access key and secret key.


Creating the VPC

resource “aws_vpc” “main” {
cidr_block = “”
instance_tenancy = “default”
enable_dns_hostnames=”true”tags = {
Name = “main”

Step 2:

We have to create 2 subnets one private(for MySQL) and one public(for WordPress).

Private subnet:

resource "aws_subnet" "public_subnet" {depends_on=[aws_vpc.main,]vpc_id = "${}"cidr_block = ""availability_zone = "ap-south-1a"map_public_ip_on_launch="true"tags={"Name"="public_subnet"}}

Private Subnet

resource "aws_subnet" "private_subnet" {depends_on=[aws_vpc.main,]vpc_id = "${}"cidr_block = ""availability_zone = "ap-south-1b"tags={"Name"="private_subnet"}}

Step 3:

Creating the public facing internet gateway for private subnet

resource "aws_internet_gateway" "igw" {depends_on=[aws_vpc.main,]vpc_id = ""tags= {Name = "igw"}}

Step 4:

Create a routing table for Internet gateway

resource "aws_route_table" "rt_public" {depends_on = [aws_internet_gateway.igw,]vpc_id = aws_vpc.main.idroute {cidr_block = ""gateway_id =}tags = {Name = "my-routing-table"                     // name the table}}

Here, we are creating the elastic IP which is used to make the IP permanent or static.

//Creating the elastic IPresource "aws_eip" "Elastic_IP" {
depends_on = [
tags = {
"Name" = "Elastic_IP"

Step 5:

Creating the NAT gateway

resource "aws_nat_gateway" "NAT_GW" {depends_on = [aws_eip.Elastic_IP,aws_subnet.public_subnet,]allocation_id = aws_eip.Elastic_IP.idsubnet_id     = aws_subnet.public_subnet.idtags = {"Name" = "NAT_GW"}}

Routing table association with private subnet

resource "aws_route_table_association" "private_subnet" {depends_on=[aws_subnet.private_subnet,aws_route_table.rt-private,]subnet_id = "${}"route_table_id = "${}"}

Creating the routing tables for public subnet

//Creating the routing table for public subnetresource "aws_route_table" "public_subnet" {
vpc_id = ""
route {
cidr_block = ""
gateway_id = ""

Routing table association with public subnet.

resource "aws_route_table_association" "public_subnet" {
subnet_id = ""
route_table_id = "${}"

Step 6:

Creating the security group for the wordpress instance

resource "aws_security_group" "WP_SG" {depends_on = [aws_vpc.main,]name        = "WP_SG"description = "Allow TLS inbound traffic"vpc_id      = aws_vpc.main.idingress {description = "SSH"from_port   = 22to_port     = 22protocol    = "tcp"cidr_blocks = [""]}ingress {description = "http"from_port   = 80to_port     = 80protocol    = "tcp"cidr_blocks = [""]}egress {from_port   = 0to_port     = 0protocol    = "-1"cidr_blocks = [""]}tags = {Name = "WP_SG"}}

Creating the security group for the MySQL instance

resource "aws_security_group" "MYSQL_SG" {depends_on = [aws_vpc.main,aws_security_group.WP_SG,aws_security_group.bastion_SG,]name        = "SG-Database"description = "Allow SG-Wordpress inbound traffic"vpc_id      = aws_vpc.main.idingress {                               // will allow traffic by WordPressdescription = "MySQL"security_groups = [,]from_port   = 3306to_port     = 3306protocol    = "tcp"}ingress {                              // will allow SSH only from Bastion Hostdescription = "SSH"security_groups = [,]from_port = 22to_port = 22protocol = "tcp"}egress {from_port   = 0to_port     = 0protocol    = "-1"cidr_blocks = [""]}tags = {Name = "MYSQL_SG"}}

Creating the security groups for 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.

resource "aws_security_group" "bastion_SG" {depends_on = [aws_vpc.main,aws_security_group.WP_SG,]name        = "Bastion_SG"description = "SG for Bastion Host"vpc_id      = aws_vpc.main.idingress {description = "ssh"from_port   = 22to_port     = 22protocol    = "tcp"cidr_blocks = [""]}egress {from_port   = 0to_port     = 0protocol    = "-1"cidr_blocks = [""]}
tags = {Name = "bastion-sg"}}

Step 7:

Before creating the instance we need to create the key pair.

Creating the key pair

resource "tls_private_key"  "mykey"{algorithm= "RSA"}resource  "aws_key_pair"   "generated_key"{key_name= "mytask4key"public_key= "${tls_private_key.mykey.public_key_openssh}"depends_on = [tls_private_key.mykey]}

Creating the Wordpress instance

resource "aws_instance" "WP_instance" {depends_on = [aws_key_pair.generated_key,aws_subnet.public_subnet,aws_security_group.WP_SG,]ami = "ami-000cbce3e1b899ebd"instance_type = "t2.micro"associate_public_ip_address = truesubnet_id = aws_subnet.public_subnet.idvpc_security_group_ids = [                    //,          // group for WordPress]key_name = "mytask4key"                           // key to use for SSHtags ={Name = "WP_instance"                    // name the instance}}

Creating the MYSQL instance

resource "aws_instance" "MYSQL_instance" {depends_on = [aws_key_pair.generated_key,aws_security_group.MYSQL_SG,aws_subnet.private_subnet,]ami = "ami-08706cb5f68222d09"instance_type = "t2.micro"associate_public_ip_address = falsesubnet_id = aws_subnet.private_subnet.idvpc_security_group_ids = [                    //,          // group for MySQL]key_name = "mytask4key"                           // key to use for SSHtags ={Name = "mysql_instance"                        // name of instance}}

Creating the instance for Bastion host.

resource "aws_instance" "bastion_host_instance" {depends_on = [aws_key_pair.generated_key,aws_security_group.bastion_SG,aws_subnet.public_subnet,]ami = "ami-0732b62d310b80e97"instance_type = "t2.micro"availability_zone = "ap-south-1a"subnet_id = aws_subnet.public_subnet.idsecurity_groups = [                             // security,       // group for Bastion]key_name = "mytask4key"                             // key to use for SSHtags = {Name = "bastion_host_instance"                          // name of Instance}}

That’s all our code is complete

For running the code we need to use

terraform init

It initializes the terraform by installing the required resources

terraform validate

For checking the errors in code

terraform plan

This commands shows the list of services that gonna create

terraform apply --auto-approve

This is the final command for deploying our entire infrastructure into AWS

Here some of the screenshots of the deployed infrastructure

That’s all guys, we have successfully completed the task

Thank you for reading this article.I hope it helped you.



Sriramadasu Prasanth Kumar

MLOps| Hybrid Cloud | DevOps | Hadoop | Kubernets | Data Science| AWS | GCP |