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)