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