Introduction
In this guide, I will walk you through setting up MySQL Client-Server on AWS using Terraform. This setup involves creating a Virtual Private Cloud (VPC), subnets, a NAT gateway, EC2 instances, and security groups. Additionally, we will configure MySQL as a database on one instance, and a client instance to interact with it.
Prerequisites
Before we begin, ensure you have the following tools installed:
Terraform
AWS Account
AWS CLI: Configured and authenticated with your AWS credentials.
Steps
Step 1: Set Up the AWS Provider
We begin by configuring the AWS provider with your region.
provider "aws" {
region = var.region
}
Step 2: Create an SSH Key Pair
We generate an RSA private key for SSH access to our instances.
resource "tls_private_key" "rsa" {
algorithm = "RSA"
rsa_bits = 4096
}
resource "aws_key_pair" "MEAN_SERVER" {
key_name = var.key_pair_name
public_key = tls_private_key.rsa.public_key_openssh
}
resource "local_file" "private_key" {
content = tls_private_key.rsa.private_key_pem
filename = "${var.key_pair_name}.pem"
}
Step 3: Define the VPC and Subnets
Create a VPC and both public and private subnets to host the EC2 instances.
resource "aws_vpc" "MEAN_VPC" {
cidr_block = var.vpc_cidr_block
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = var.vpc_name
}
}
resource "aws_subnet" "public" {
count = length(var.public_subnet_cidrs)
vpc_id = aws_vpc.MEAN_VPC.id
cidr_block = var.public_subnet_cidrs[count.index]
availability_zone = data.aws_availability_zones.available.names[count.index]
map_public_ip_on_launch = true
tags = {
Name = "MEAN_PUBLIC_${count.index}"
}
}
resource "aws_subnet" "private" {
count = length(var.private_subnet_cidrs)
vpc_id = aws_vpc.MEAN_VPC.id
cidr_block = var.private_subnet_cidrs[count.index]
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = {
Name = "MEAN_PRIVATE_${count.index}"
}
}
Step 4: Set Up Internet and NAT Gateway
We create an Internet Gateway for public subnet access and a NAT Gateway for private subnet access.
resource "aws_internet_gateway" "MEAN_GW" {
vpc_id = aws_vpc.MEAN_VPC.id
tags = {
Name = var.internet_gateway_name
}
}
resource "aws_eip" "nat" {
domain = "vpc"
depends_on = [aws_internet_gateway.MEAN_GW]
}
resource "aws_nat_gateway" "nat" {
allocation_id = aws_eip.nat.id
subnet_id = aws_subnet.public[0].id
depends_on = [aws_internet_gateway.MEAN_GW]
tags = {
Name = var.nat_gateway_name
}
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.MEAN_VPC.id
tags = {
Name = var.public_route_table_name
}
}
resource "aws_route_table" "private" {
vpc_id = aws_vpc.MEAN_VPC.id
tags = {
Name = var.private_route_table_name
}
}
resource "aws_route" "public_internet_access" {
route_table_id = aws_route_table.public.id
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.MEAN_GW.id
}
resource "aws_route" "private_nat_route" {
route_table_id = aws_route_table.private.id
destination_cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.nat.id
}
resource "aws_route_table_association" "public_assoc" {
count = length(aws_subnet.public)
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
}
resource "aws_route_table_association" "private_assoc" {
count = length(aws_subnet.private)
subnet_id = aws_subnet.private[count.index].id
route_table_id = aws_route_table.private.id
}
resource "aws_security_group" "MEAN_SG" {
name = var.security_group_name
description = "Allow Database and SSH"
vpc_id = aws_vpc.MEAN_VPC.id
Step 5: Configure Security Groups
The security group will allow SSH access and MySQL communication between the instances.
resource "aws_security_group" "MEAN_SG" {
name = var.security_group_name
description = "Allow Database and SSH"
vpc_id = aws_vpc.MEAN_VPC.id
ingress {
description = "Allow SSH"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [var.ssh_ingress_cidr]
}
ingress {
description = "Allow MySQL"
from_port = 3306
to_port = 3306
protocol = "tcp"
cidr_blocks = [var.vpc_cidr_block] # Allow internal access
}
egress {
description = "Allow all outbound traffic"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = var.security_group_name
}
}
Step 6: Launch EC2 Instances
Now we launch two EC2 instances, one for the server and another as the client.
resource "aws_instance" "mean_server" {
ami = var.instance_ami
instance_type = var.instance_type
key_name = aws_key_pair.MEAN_SERVER.key_name
subnet_id = aws_subnet.public[0].id
vpc_security_group_ids = [aws_security_group.MEAN_SG.id]
user_data = <<-EOF
#!/bin/bash
sudo apt update -y && sudo apt upgrade -y
sudo apt install -y mysql-server
sudo systemctl enable mysql
sudo systemctl start mysql
sudo mysql -e "CREATE USER 'client'@'%' IDENTIFIED WITH mysql_native_password BY 'NewU$er.3';"
sudo mysql -e "CREATE DATABASE test_db;"
sudo mysql -e "GRANT ALL ON test_db.* TO 'client'@'%' WITH GRANT OPTION;"
sudo mysql -e "FLUSH PRIVILEGES;"
sudo sed -i "s/^bind-address.*/bind-address = 0.0.0.0/" /etc/mysql/mysql.conf.d/mysqld.cnf
sudo systemctl restart mysql
EOF
tags = {
Name = "MEAN-SERVER"
}
}
resource "aws_instance" "mean_client" {
ami = var.instance_ami
instance_type = var.instance_type
key_name = aws_key_pair.MEAN_SERVER.key_name
subnet_id = aws_subnet.public[0].id
vpc_security_group_ids = [aws_security_group.MEAN_SG.id]
user_data = <<-EOF
#!/bin/bash
sudo apt update -y && sudo apt upgrade -y
sudo apt install -y mysql-client
EOF
tags = {
Name = "MEAN-CLIENT"
}
}
output "mean_server_public_ip" {
value = aws_instance.mean_server.public_ip
}
output "mean_client_public_ip" {
value = aws_instance.mean_client.public_ip
}
output "instance_dns_names" {
value = [aws_instance.mean_server.public_dns, aws_instance.mean_client.public_dns]
}
Step 7: Visiting Our Server from the Client
Now, from our MEAN Client, let us try to access our MEAN Server:
sudo mysql -u client -h 10.0.1.199 -p
Step 8: Playing in Our New Server (accessed from the client)
Let us now make sure we can actually do stuff in our server from our client:
First, let us see what we have:
Next, let show what I have in my terraform.tfvars and variable.tf
region = "us-east-1"
key_pair_name = "mean-server-key"
vpc_cidr_block = "10.0.0.0/16"
vpc_name = "MEAN_VPC"
public_subnet_cidrs = ["10.0.1.0/24", "10.0.2.0/24"]
private_subnet_cidrs = ["10.0.101.0/24", "10.0.102.0/24"]
internet_gateway_name = "MEAN_IGW"
nat_gateway_name = "MEAN_NAT"
public_route_table_name = "MEAN_Public_RT"
private_route_table_name = "MEAN_Private_RT"
security_group_name = "MEAN_SG"
# Choose the appropriate AMI ID for your region
instance_ami = "ami-0f9de6e2d2f067fca"
instance_type = "t2.micro"
ec2_instance_name = "MEAN_EC2"
variable "region" {
description = "AWS region to deploy into"
type = string
}
variable "vpc_cidr_block" {
description = "CIDR block for the VPC"
type = string
}
variable "public_subnet_cidrs" {
description = "List of CIDR blocks for public subnets"
type = list(string)
}
variable "private_subnet_cidrs" {
description = "List of CIDR blocks for private subnets"
type = list(string)
}
variable "instance_ami" {
description = "AMI ID for EC2 instance"
type = string
}
variable "instance_type" {
description = "EC2 instance type"
type = string
}
variable "key_pair_name" {
description = "Name for the AWS key pair"
type = string
}
variable "ec2_instance_name" {
description = "Name tag for the EC2 instance"
type = string
}
variable "vpc_name" {
description = "Name tag for the VPC"
type = string
}
variable "public_route_table_name" {
description = "Name tag for public route table"
type = string
}
variable "private_route_table_name" {
description = "Name tag for private route table"
type = string
}
variable "nat_gateway_name" {
description = "Name tag for the NAT Gateway"
type = string
}
variable "internet_gateway_name" {
description = "Name tag for Internet Gateway"
type = string
}
variable "security_group_name" {
description = "Name for the Security Group"
type = string
}
variable "ssh_ingress_cidr" {
description = "CIDR block allowed to SSH (port 22)"
type = string
default = "0.0.0.0/0"
}
variable "db_access_cidr" {
description = "CIDR block allowed for DB(port 3306)"
type = string
default = "10.0.55.0/24"
}
Conclusion
This Terraform script automates the creation of a Server-Client on AWS, including networking, instances, and database setup. Copy and paste the script into your .tf file, modify the variables, and run terraform apply to deploy your infrastructure.
Top comments (0)