Production ready AWS MySQL RDS instance using terraform
Using Terraform for AWS RDS implementation is not new. It is a well defined process with required resources already defined by Hashicorp and AWS. But one pitfall associated with IAC as a whole is the possibility that some of the production implementation considerations that might have been covered when provisioning manually might not be highlighted when the infrastructure is initiated using terraform. Idea of this post is to list down such points and provide sample terraform script for production ready AWS Mysql rds.
1. Configure backups, maintenance windows, version upgrade preference.
Make sure to include variables "rds_backupWindow" , variable "rds_maintenanceWindow", variable "rds_backupRetentionPeriod".
Ensure time windows are set as per the timezone setting of AWS data center in use. Refer 02-rds-variables.tf.
2. Use separate file for parameter management.
This should include all rds parameters such as db instance name, size and also the database level parameters such as max_connections etc.
Refer variable "rds_parameters" in file 02-rds-variables.tf below. Some of these parameters can cause db instance restart based on configuration
3. Use secret manager service to generate and store the RDS instance database user parameter.
Do not hard code the database privilege user password in your TF file. Instead use AWS secret manager service as generate a random password. refer 01-rds-sec.tf.
4. Subnet groups
Create subnet group according to availability group settings. If multi_az enabled then create the subnet group including the subnets of two zones.
5. Security groups
Create the security group with least required privileges in mind. Make sure to allow db port access only from the subnets where application will connect to the particular rds instance.
Included below is a simple set of terraform scripts the address above listed considerations.
01-rds-sec.tf
resource "random_password" "master"{
length = 16
special = true
override_special = "_!%^"
}
resource "aws_secretsmanager_secret" "password" {
name = "ctl-db-password"
}
resource "aws_secretsmanager_secret_version" "password" {
secret_id = aws_secretsmanager_secret.password.id
secret_string = random_password.master.result
}
02-rds-variables.tf
variable "rds_name" {
description = "AWS rds name"
type = string
default = "ctl-cplane-database"
}
variable "rds_instanceType" {
description = "The instance type"
default = "db.m5.xlarge"
}
variable "rds_subnetGroupName" {
description = "AWS rds subnet group name"
type = string
default = "ctl-db-subnet-group"
}
variable "rds_parameterGroupName" {
description = "The name of the DB parameter group"
type = string
default = "mysql-parameters"
}
variable "rds_storage" {
description = "Allocated storage size (in GB)"
default = 500
}
variable "rds_backupRetentionPeriod" {
description = "The number of days to retain automated backups"
default = 7
}
variable "rds_backupWindow" {
description = "The daily time range during which automated backups are created"
default = "00:30-02:00"
}
variable "rds_maintenanceWindow" {
description = "The weekly time range during which system maintenance can occur"
default = "sun:02:00-sun:03:00"
}
variable "rds_parameters" {
description = "Map of custom MySQL parameters"
type = map
default = {
"max_connections" = "750"
"wait_timeout" = "900"
"max_connect_errors" = "100"
}
}
03-rds-sg.tf
data "aws_subnet" "ctl-cplane-core-subnet-01a" {
filter {
name = "cidrBlock"
values = ["10.96.4.96/27"]
}
}
data "aws_subnet" "ctl-cplane-core-subnet-01b" {
filter {
name = "cidrBlock"
values = ["10.96.4.128/27"]
}
}
data "aws_subnet" "ctl-cplane-core-subnet-01c" {
filter {
name = "cidrBlock"
values = ["10.96.4.160/27"]
}
}
resource "aws_security_group" "rds_sg" {
name = "${var.rds_name}-sg"
description = "Security group for rds cluster"
vpc_id = var.vpc_id
// Inbound rules
ingress {
from_port = 3306
to_port = 3306
protocol = "tcp"
cidr_blocks = [data.aws_subnet.ctl-cplane-core-subnet-01a.cidr_block,data.aws_subnet.ctl-cplane-core-subnet-01b.cidr_block,data.aws_subnet.ctl-cplane-core-subnet-01c.cidr_block]
description = "Allow inbound DB Traffic from application subnets"
}
# // Outbound rules
# egress {
# from_port = 0
# to_port = 0
# protocol = "-1"
# cidr_blocks = ["0.0.0.0/0"]
# description = "Allow all outbound traffic"
# }
tags = {
Name = "${var.rds_name}-sg"
}
}
04-rds.tf
resource "aws_db_subnet_group" "dbgroup" {
name = var.rds_subnetGroupName
subnet_ids = var.rds_subnets
}
resource "aws_db_parameter_group" "mysql" {
name = var.rds_parameterGroupName
family = "mysql8.0"
description = "Custom MySQL parameters"
dynamic "parameter" {
for_each = var.rds_parameters
content {
name = parameter.key
value = parameter.value
}
}
}
data "aws_secretsmanager_secret_version" "password" {
secret_id = aws_secretsmanager_secret.password.id
}
resource "aws_db_instance" "ctl-cplane-database" {
identifier = var.rds_name
allocated_storage = var.rds_storage
storage_type = "gp2"
engine = "mysql"
engine_version = "8.0.33"
instance_class = var.rds_instanceType
username = "ctl_user"
password = data.aws_secretsmanager_secret_version.password.secret_string
db_subnet_group_name = aws_db_subnet_group.dbgroup.name
parameter_group_name = aws_db_parameter_group.mysql.name
publicly_accessible = false
skip_final_snapshot = false
final_snapshot_identifier = "before-Delete-snap-ctl-coreRDS"
vpc_security_group_ids = [aws_security_group.rds_sg.id]
multi_az = true
backup_retention_period = var.rds_backupRetentionPeriod
backup_window = var.rds_backupWindow
maintenance_window = var.rds_maintenanceWindow
apply_immediately = true
tags = {
Name = "ctl-cplane-databases"
}
}
Comments
Post a Comment