Terraform-Access Private Subnet MySQL RDS over SSH

In this article, We are going to cover Terraform-Access Private Subnet MySQL RDS over SSH with custom VPC | Deploying EC2 instance and RDS databases within a custom VPC on AWS using Terraform

Create a MySQL RDS Database Instance with Terraform 9

What is Terraform?

1280px Terraform Logo.svg removebg preview 1
  • It is an open-source IaaC (Infrastructure as a code) software tool where you define and create resources using providers in the declarative configuration language example JSON.
  • With Terraform, You can package and reuse the code in the form of modules.
  • It supports a number of cloud infrastructure providers such as AWS, Azure, GCP, IBM Cloud, OCI, etc.

What is VPC in AWS?

AWS VPC is a logically isolated section of the AWS Cloud where you can deploy and run AWS resources. It allows you to create a private network in the cloud, providing control over the virtual networking environment, including IP address ranges, subnets, and configuration of route tables and network gateways.

What are the components of VPC in AWS?

  1. Private Subnet:
    • No direct internet access, suitable for resources needing enhanced security, like databases.
  2. Public Subnet:
    • Has a route to the internet via an Internet Gateway, used for resources that require public accessibility, such as web servers.
  3. Internet Gateway:
    • A scalable and redundant component that enables communication between instances in a VPC and the internet, allowing resources in a public subnet to connect to the internet and vice versa.
  4. Route Table (Public):
    • Routes traffic in and out of a public subnet, directing it through the Internet Gateway for internet access.
  5. Route Table (Private):
    • Routes traffic within the VPC, typically without a direct route to the internet. Used for communication between instances in different subnets.
  6. Security Group:
    • Acts as a virtual firewall for instances. It controls inbound and outbound traffic based on rules defined by the user, providing an additional layer of security.
  7. NAT Gateway:
    • A managed network address translation (NAT) service that allows instances in a private subnet to initiate outbound traffic to the internet while preventing inbound traffic from reaching those instances. Useful for instances without public IP addresses.

What is EC2 instance and RDS Database in AWS?

  1. EC2 instance:- Amazon Elastic Compute Cloud (EC2) is a web service that provides resizable compute capacity in the cloud. It allows users to run virtual servers, known as instances, to host applications, websites, or backend services.
  2. RDS Database:-Amazon Relational Database Service (RDS) is a managed database service that simplifies the setup, operation, and scaling of relational databases. It supports various database engines, including MySQL, PostgreSQL, Oracle, SQL Server, and MariaDB.

VPC Architecture Overview

image 3
  1. VPC Creation:
    • Begin by creating a Virtual Private Cloud (VPC) to isolate and organize your network resources.
  2. Internet Gateway Attachment:
    • Attach an Internet Gateway to the VPC to enable communication between the VPC and the internet, facilitating public-facing resources.
  3. Subnet Configuration:
    • Define three subnets within the VPC:
      • Public Subnet (for EC2):
        • Deploy an EC2 instance in the public subnet to handle public-facing workloads.
      • Private Subnets (for RDS):
        • Create two private subnets to house the MySQL RDS database instances, ensuring secure and controlled access.
  4. Route Tables:
    • Establish two route tables:
      • Public Route Table:
        • Configure routes to the Internet Gateway, allowing outbound internet access for resources in the public subnet.
      • Private Route Table:
        • Restrict outbound traffic to the internet, providing a more secure environment for resources in the private subnets.
  5. Security Groups:
    • Set up two security groups:
      • EC2 Security Group:
        • Define inbound and outbound rules for the EC2 instance in the public subnet.
      • RDS Security Group:
        • Specify rules to control traffic to and from the MySQL RDS database instances in the private subnets.
  6. DB Subnet Group:
    • Create a Database Subnet Group, specifying the private subnets where the RDS instances will be deployed.
  7. MySQL RDS Database Creation:
    • Deploy the MySQL RDS database within the private subnets, ensuring that it’s isolated from direct internet access for enhanced security.
  8. EC2 Instance Deployment:
    • Launch an EC2 instance in the public subnet, which serves as a public-facing application server. Ensure proper security group associations.
  9. Verification:
    • Validate the setup by confirming that the EC2 instance can communicate with the MySQL RDS database securely.


  • EC2 instances should be accessible anywhere on the internet via HTTP
  • Everyone should be able to access the EC2 instances via SSH
  • RDS should be on a private subnet and inaccessible via the internet
  • Only the EC2 instances should be able to communicate with RDS


Steps for Implementation

Summary view of the terraform files in VS code:

Screenshot 2023 12 08 115720

Terraform-Access Private Subnet MySQL RDS over SSH

Step 1:- Create provider.tf file

The provider.tf file in Terraform is a configuration file that specifies the cloud provider and its corresponding plugin that Terraform will use to manage resources in that provider.

Provider.tf file is used for the following:-

  • Provider Declaration: Declares the cloud or infrastructure provider, such as AWS or Azure.
  • Authentication Details: Includes credentials (e.g., access and secret keys) for connecting to the chosen provider.
  • Region Specification: Specifies the geographic region where resources will be provisioned.
  • Version Constraints: Optionally sets constraints on the provider version for compatibility.
  • Additional Settings: May include other provider-specific configurations, like endpoints or custom parameters.
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"

provider "aws" {
  region = "ap-south-1"
  profile = "default"
data "aws_availability_zones" "available" {
  state = "available"
Screenshot 2023 12 08 132351

Step 2:- Create vpc.tf file

In Terraform, the vpc.tf file typically contains the configuration code for creating and defining an Amazon Virtual Private Cloud (VPC) along with its associated resources.

// Create a VPC named "tutorial_vpc"
resource "aws_vpc" "tutorial_vpc" {
  // Here we are setting the CIDR block of the VPC
  // to the "vpc_cidr_block" variable
  cidr_block           = var.vpc_cidr_block
  // We want DNS hostnames enabled for this VPC
  enable_dns_hostnames = true
  tags = {
    Name = "tutorial_vpc"
Screenshot 2023 12 08 132412

Step 3:- Create subnets.tf file

A subnets.tf file in Terraform defines and configures subnets within an AWS Virtual Private Cloud (VPC), specifying attributes such as CIDR blocks, Availability Zones, and route table associations.

//Create Public Subnet
resource "aws_subnet" "tutorial_public_subnet" { 
  count             = var.subnet_count.public  
  vpc_id            = aws_vpc.tutorial_vpc.id   
  cidr_block        = var.public_subnet_cidr_blocks[count.index]
  availability_zone = data.aws_availability_zones.available.names[count.index]
  tags = {
    Name = "tutorial_public_subnet_${count.index}"

//Create Private Subnet
resource "aws_subnet" "tutorial_private_subnet" {
  count             = var.subnet_count.private
  vpc_id            = aws_vpc.tutorial_vpc.id
  cidr_block        = var.private_subnet_cidr_blocks[count.index]
  availability_zone = data.aws_availability_zones.available.names[count.index]
  tags = {
    Name = "tutorial_private_subnet_${count.index}"
Screenshot 2023 12 08 132445

Step 4:- Create security_groups.tf file

The security_groups.tf file in Terraform is vital for configuring application security, focusing on security groups for EC2 instances and RDS instance. Within this file, you define security group configurations for EC2 instances and RDS Database instance, specifying open ports and allowed protocols for inbound and outbound traffic.

// Create a security for the EC2 instances called "tutorial_web_sg"
resource "aws_security_group" "tutorial_web_sg" {
  name        = "tutorial_web_sg"
  description = "Security group for tutorial web servers"
  vpc_id      = aws_vpc.tutorial_vpc.id

  ingress {
    description = "Allow all traffic through HTTP"
    from_port   = "80"
    to_port     = "80"
    protocol    = "tcp"
    cidr_blocks = [""]

  ingress {
    description = "Allow SSH from my computer"
    from_port   = "22"
    to_port     = "22"
    protocol    = "tcp"
    // This is using the variable "my_ip"
    cidr_blocks = [""]

  egress {
    description = "Allow all outbound traffic"
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = [""]

  tags = {
    Name = "tutorial_web_sg"

// Create a security group for the RDS instances called "tutorial_db_sg"
resource "aws_security_group" "tutorial_db_sg" {
  name        = "tutorial_db_sg"
  description = "Security group for tutorial databases"
  vpc_id      = aws_vpc.tutorial_vpc.id

  ingress {
    description     = "Allow MySQL traffic from only the web sg"
    from_port       = "3306"
    to_port         = "3306"
    protocol        = "tcp"
    security_groups = [aws_security_group.tutorial_web_sg.id]

  tags = {
    Name = "tutorial_db_sg"

// Create a db subnet group named "tutorial_db_subnet_group"
resource "aws_db_subnet_group" "tutorial_db_subnet_group" {
  name        = "tutorial_db_subnet_group"
  description = "DB subnet group for tutorial"
  subnet_ids  = [for subnet in aws_subnet.tutorial_private_subnet : subnet.id]
Screenshot 2023 12 08 132516

Step 5:- Create ec2_instance.tf file

The ec2_instance.tf file in Terraform typically contains configurations for creating Amazon EC2 instances. It defines the instance type, Amazon Machine Image (AMI), key pair for SSH access, security groups, and other settings. Additionally, it may include outputs to provide information about the created EC2 instances, such as their public IP addresses or DNS names. This file plays a crucial role in defining the virtual machines within the infrastructure.

// Create an EC2 instance named "tutorial_web"
resource "aws_instance" "tutorial_web" {
  count = var.settings.web_app.count
  ami                    = "ami-0287a05f0ef0e9d9a"
  instance_type          = var.settings.web_app.instance_type
  subnet_id              = aws_subnet.tutorial_public_subnet[count.index].id 
  key_name               = "three-tier"   
  vpc_security_group_ids = [aws_security_group.tutorial_web_sg.id]
  tags = {
    Name = "tutorial_web_${count.index}"

Step 6:- Create db_intsance.tf file

The db_instance.tf file typically contains Terraform configurations for creating an Amazon RDS instance. It defines settings such as the database engine, instance class, allocated storage, and other parameters. Additionally, it associates the RDS instance with a subnet group, specifies security groups, and outputs details like the database address and port for reference in other parts of the infrastructure.

resource "aws_db_instance" "tutorial_database" {
 allocated_storage      = 10
  engine                 = "mysql"
  engine_version         = "5.7"
  instance_class         = "db.t2.micro"
  username = "admin"
  password = "admin12345678"
  db_name                = "tutorial"
  parameter_group_name   = "default.mysql5.7"
  skip_final_snapshot    = true
  db_subnet_group_name   = aws_db_subnet_group.tutorial_db_subnet_group.id
  vpc_security_group_ids = [aws_security_group.tutorial_db_sg.id]

Step 7:- Create route_table.tf file

The route_table.tf file in Terraform is responsible for configuring route tables, crucial for directing network traffic within a Virtual Private Cloud (VPC). By creating a route table to route traffic through the internet gateway we will give internet access to the VPC.

// Create a public route table named "tutorial_public_rt"
resource "aws_route_table" "tutorial_public_rt" {
  vpc_id = aws_vpc.tutorial_vpc.id
  route {
    cidr_block = ""
    gateway_id = aws_internet_gateway.tutorial_igw.id

// Here we are going to add the public subnets to the 
// "tutorial_public_rt" route table
resource "aws_route_table_association" "public" {
  count          = var.subnet_count.public
  route_table_id = aws_route_table.tutorial_public_rt.id
  subnet_id      = 	aws_subnet.tutorial_public_subnet[count.index].id

// Create a private route table named "tutorial_private_rt"
resource "aws_route_table" "tutorial_private_rt" {
  vpc_id = aws_vpc.tutorial_vpc.id

// Here we are going to add the private subnets to the
// route table "tutorial_private_rt"
resource "aws_route_table_association" "private" {
  count          = var.subnet_count.private
  route_table_id = aws_route_table.tutorial_private_rt.id
  subnet_id      = aws_subnet.tutorial_private_subnet[count.index].id
Screenshot 2023 12 08 132536

Step 8:- Create internet_gateway.tf file

An internet_gateway.tf file in Terraform typically configures the creation of an Internet Gateway for an Amazon Virtual Private Cloud (VPC) in AWS, enabling communication between instances within the VPC and the internet.

resource "aws_internet_gateway" "tutorial_igw" {
  vpc_id = aws_vpc.tutorial_vpc.id
  tags = {
    Name = "tutorial_igw"
Screenshot 2023 12 08 132557

Step 9:- Create elastic_ip.tf file

An elastic_ip.tf file in Terraform is used to define and configure Elastic IP (EIP) resources in AWS, providing static public IP addresses that can be associated with EC2 instances or other resources, allowing them to be accessed consistently over the internet.

// Create an Elastic IP named "tutorial_web_eip" for each
// EC2 instance
resource "aws_eip" "tutorial_web_eip" {
  count    = var.settings.web_app.count
  instance = aws_instance.tutorial_web[count.index].id
  tags = {
    Name = "tutorial_web_eip_${count.index}"
Screenshot 2023 12 08 132611

Step 10:- Create variables.tf file

A variables.tf file in Terraform is where you define input variables for your infrastructure code, allowing for customization and flexibility by parameterizing values that can be easily modified without changing the core code.

variable "vpc_cidr_block" {
  description = "CIDR block for VPC"
  type        = string
  default     = ""

variable "subnet_count" {
  description = "Number of subnets"
  type        = map(number)
  default = {
    public  = 1,
    private = 2

variable "settings" {
  description = "Configuration settings"
  type        = map(any)
  default = {
    "database" = {
      allocated_storage   = 10            
      engine              = "mysql"       
      engine_version      = "5.7"     
      instance_class      = "db.t2.micro" 
      skip_final_snapshot = true
    "web_app" = {
      count         = 1          
      instance_type = "t2.micro" 

variable "public_subnet_cidr_blocks" {
  description = "Available CIDR blocks for public subnets"
  type        = list(string)
  default = [

variable "private_subnet_cidr_blocks" {
  description = "Available CIDR blocks for private subnets"
  type        = list(string)
  default = [
Screenshot 2023 12 08 132630

Step 11:- Create outputs.tf file

An outputs.tf file in Terraform defines output values that you want to display after the infrastructure deployment, providing a way to expose specific information, such as resource IDs or computed results, for easier access or use in subsequent processes.

// This will output the public IP of the web server
output "web_public_ip" {
  description = "The public IP address of the web server"
  value       = aws_eip.tutorial_web_eip[0].public_ip
  depends_on = [aws_eip.tutorial_web_eip]

// This will output the the public DNS address of the web server
output "web_public_dns" {
  description = "The public DNS address of the web server"
  value       = aws_eip.tutorial_web_eip[0].public_dns
  depends_on = [aws_eip.tutorial_web_eip]

// This will output the database endpoint
output "database_endpoint" {
  description = "The endpoint of the database"
  value       = aws_db_instance.tutorial_database.address

// This will output the database port
output "database_port" {
  description = "The port of the database"
  value       = aws_db_instance.tutorial_database.port
Screenshot 2023 12 08 132648

Step 12:- Let’s deploy!

 Let’s run our configuration and make sure everything works correctly. On the command line, run the following commands:

  • terraform init

The terraform init the command is used to initialize a new or existing Terraform configuration. This command downloads the required provider plugins and sets up the backend for storing state.

Screenshot 2023 12 08 123855
  • terraform plan

The terraform plan the command is used to create an execution plan for the Terraform configuration. This command shows what resources Terraform will create, modify, or delete when applied.

Screenshot 2023 12 08 123932
Screenshot 2023 12 08 123945
  • terraform apply

The terraform apply the command is used to apply the Terraform configuration and create or modify resources in the target environment.

Screenshot 2023 12 08 124509
Screenshot 2023 12 08 124530
  • terraform destroy

The primary purpose of terraform destroy is to remove all the resources that were created by the Terraform configuration. 

Screenshot 2023 12 08 142051
Screenshot 2023 12 08 142106

Step 13:- Connect to the RDS instance

First, Connect the EC2 instance using Mobaxterm to perform operations.

Once connected to the EC2 instance, let’s try connecting to the RDS instance. First, we will need to install the MySQL client. Run the following command:

sudo apt-get update -y && sudo apt install mysql-client -y
Screenshot 2023 12 08 133613

Once that MySQL client is installed, let’s try connecting to the RDS instance.

  • The <database-endpoint> is going to be your database_endpoint from the terraform output
  • The <database-port> is going to be your database_port from the terraform output
  • The <db-username> is going to be your DB username, which was the db_username that you created in your secrets
mysql -h <database-endpoint> -P <database-port> -u <db-username> -p

Now, enter the password which is


We are connected to the MySQL RDS database. Let’s see if our database was created. Run the following command in the MySQL terminal

Screenshot 2023 12 08 133515

Step 14:- Go to the AWS console and verify

EC2 Instance:-

Screenshot 2023 12 08 125151
Screenshot 2023 12 08 125342


Screenshot 2023 12 08 125530

Security Groups:-

Screenshot 2023 12 08 131452
Screenshot 2023 12 08 131512

Internet Gateway:-

Screenshot 2023 12 08 131612

RDS Database Instance:-

Screenshot 2023 12 08 131650
Screenshot 2023 12 08 131712

We have covered Terraform-Access Private Subnet MySQL RDS over SSH.


In conclusion, utilizing Terraform to create an EC2 instance and an RDS database within a custom VPC on AWS streamlines infrastructure deployment, enhancing scalability, security, and automation. This approach ensures a well-architected environment, seamlessly integrating compute and database resources while adhering to best practices in cloud infrastructure management.


For reference visit the official website TerraformRegistry.

Any queries pls contact us @Devopshint.

Related Articles:

Terraform State File Management | Terraform Local State File | Terraform Remote State

About Akash Bhujbal

Hey, I am Akash Bhujbal, I am an aspiring DevOps and Cloud enthusiast who is eager to embark on a journey into the world of DevOps and Cloud. With a strong passion for technology and a keen interest in DevOps and Cloud based solutions, I am driven to learn and contribute to the ever-evolving field of DevOps and Cloud.

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Share via
Copy link