Deploying AWS RDS Instances Using Terraform
A comprehensive guide to creating and managing Amazon RDS databases with Terraform, including security, backups, and monitoring
Deploying AWS RDS Instances Using Terraform
Amazon RDS (Relational Database Service) provides managed database services for various database engines. This guide demonstrates how to deploy and manage RDS instances using Terraform, incorporating security best practices and operational considerations.
Video Tutorial
Learn more about managing AWS RDS with Terraform in this comprehensive video tutorial:
Prerequisites
- AWS CLI installed and configured
- Terraform installed (version 1.0.0 or later)
- Basic understanding of RDS and database concepts
- Text editor of your choice
Project Structure
rds-terraform/
├── main.tf
├── variables.tf
├── outputs.tf
└── terraform.tfvars
Setting Up the Infrastructure
Create main.tf
:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}
provider "aws" {
region = var.aws_region
}
# VPC for RDS
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.project_name}-vpc"
}
}
# Private subnets for RDS
resource "aws_subnet" "private" {
count = 2
vpc_id = aws_vpc.main.id
cidr_block = var.private_subnet_cidrs[count.index]
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = {
Name = "${var.project_name}-private-${count.index + 1}"
}
}
# DB subnet group
resource "aws_db_subnet_group" "main" {
name = "${var.project_name}-subnet-group"
subnet_ids = aws_subnet.private[*].id
tags = {
Name = "${var.project_name}-subnet-group"
}
}
# Security group for RDS
resource "aws_security_group" "rds" {
name = "${var.project_name}-rds-sg"
description = "Security group for RDS instance"
vpc_id = aws_vpc.main.id
ingress {
description = "Database access from application"
from_port = var.db_port
to_port = var.db_port
protocol = "tcp"
security_groups = [aws_security_group.app.id]
}
tags = {
Name = "${var.project_name}-rds-sg"
}
}
# KMS key for encryption
resource "aws_kms_key" "rds" {
description = "KMS key for RDS encryption"
deletion_window_in_days = 7
enable_key_rotation = true
tags = {
Name = "${var.project_name}-rds-key"
}
}
# Parameter group
resource "aws_db_parameter_group" "main" {
name = "${var.project_name}-pg"
family = var.parameter_group_family
parameter {
name = "character_set_server"
value = "utf8"
}
parameter {
name = "character_set_client"
value = "utf8"
}
}
# Option group
resource "aws_db_option_group" "main" {
name = "${var.project_name}-og"
engine_name = var.engine
major_engine_version = var.engine_version
option {
option_name = "MARIADB_AUDIT_PLUGIN"
}
}
# RDS instance
resource "aws_db_instance" "main" {
identifier = "${var.project_name}-db"
engine = var.engine
engine_version = var.engine_version
instance_class = var.instance_class
allocated_storage = var.allocated_storage
storage_type = "gp3"
db_name = var.database_name
username = var.database_username
password = var.database_password
db_subnet_group_name = aws_db_subnet_group.main.name
vpc_security_group_ids = [aws_security_group.rds.id]
parameter_group_name = aws_db_parameter_group.main.name
option_group_name = aws_db_option_group.main.name
multi_az = var.multi_az
publicly_accessible = false
skip_final_snapshot = false
copy_tags_to_snapshot = true
deletion_protection = true
backup_retention_period = 7
backup_window = "03:00-04:00"
maintenance_window = "Mon:04:00-Mon:05:00"
storage_encrypted = true
kms_key_id = aws_kms_key.rds.arn
performance_insights_enabled = true
performance_insights_retention_period = 7
monitoring_interval = 60
monitoring_role_arn = aws_iam_role.rds_monitoring.arn
tags = {
Name = "${var.project_name}-db"
Environment = var.environment
}
}
# Enhanced monitoring role
resource "aws_iam_role" "rds_monitoring" {
name = "${var.project_name}-rds-monitoring"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "monitoring.rds.amazonaws.com"
}
}
]
})
}
resource "aws_iam_role_policy_attachment" "rds_monitoring" {
role = aws_iam_role.rds_monitoring.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonRDSEnhancedMonitoringRole"
}
Variables Configuration
Create variables.tf
:
variable "aws_region" {
description = "AWS region"
type = string
default = "us-west-2"
}
variable "project_name" {
description = "Name of the project"
type = string
}
variable "environment" {
description = "Environment name"
type = string
default = "dev"
}
variable "vpc_cidr" {
description = "CIDR block for VPC"
type = string
default = "10.0.0.0/16"
}
variable "private_subnet_cidrs" {
description = "CIDR blocks for private subnets"
type = list(string)
default = ["10.0.1.0/24", "10.0.2.0/24"]
}
variable "engine" {
description = "Database engine"
type = string
default = "mysql"
}
variable "engine_version" {
description = "Database engine version"
type = string
default = "8.0"
}
variable "instance_class" {
description = "RDS instance class"
type = string
default = "db.t3.medium"
}
variable "allocated_storage" {
description = "Allocated storage in GB"
type = number
default = 20
}
variable "database_name" {
description = "Name of the database"
type = string
}
variable "database_username" {
description = "Database master username"
type = string
}
variable "database_password" {
description = "Database master password"
type = string
sensitive = true
}
variable "multi_az" {
description = "Enable Multi-AZ deployment"
type = bool
default = false
}
variable "parameter_group_family" {
description = "Database parameter group family"
type = string
default = "mysql8.0"
}
variable "db_port" {
description = "Database port"
type = number
default = 3306
}
Output Configuration
Create outputs.tf
:
output "db_endpoint" {
description = "The connection endpoint for the database"
value = aws_db_instance.main.endpoint
}
output "db_name" {
description = "The name of the database"
value = aws_db_instance.main.db_name
}
output "db_username" {
description = "The master username for the database"
value = aws_db_instance.main.username
}
output "db_port" {
description = "The port the database is listening on"
value = aws_db_instance.main.port
}
Best Practices
-
Security
- Use private subnets
- Enable encryption at rest
- Implement proper security groups
- Use strong passwords
-
High Availability
- Consider Multi-AZ deployment
- Regular backups
- Automated snapshots
-
Monitoring
- Enable enhanced monitoring
- Use performance insights
- Set up CloudWatch alarms
Deployment Steps
- Initialize Terraform:
terraform init
- Create
terraform.tfvars
:
aws_region = "us-west-2"
project_name = "my-app"
environment = "prod"
database_name = "myappdb"
database_username = "admin"
database_password = "your-secure-password"
multi_az = true
- Review the plan:
terraform plan
- Apply the configuration:
terraform apply
Security Considerations
-
Network Security
- Use private subnets
- Restrict security group access
- Enable encryption in transit
-
Access Control
- Use IAM authentication
- Implement least privilege
- Regular password rotation
-
Encryption
- Enable encryption at rest
- Use customer-managed KMS keys
- Secure parameter storage
Monitoring and Maintenance
- CloudWatch Alarms
resource "aws_cloudwatch_metric_alarm" "database_cpu" {
alarm_name = "${var.project_name}-db-cpu-utilization"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = "2"
metric_name = "CPUUtilization"
namespace = "AWS/RDS"
period = "300"
statistic = "Average"
threshold = "80"
alarm_description = "This metric monitors database CPU utilization"
alarm_actions = [var.sns_topic_arn]
dimensions = {
DBInstanceIdentifier = aws_db_instance.main.id
}
}
-
Backup Strategy
- Enable automated backups
- Configure backup retention
- Test restore procedures
-
Performance Optimization
- Monitor slow queries
- Optimize parameter groups
- Regular maintenance updates
Conclusion
You’ve learned how to deploy and manage RDS instances using Terraform. This approach ensures:
- Consistent database deployments
- Secure configuration
- Automated provisioning
- Proper monitoring and maintenance
Remember to:
- Follow security best practices
- Implement proper monitoring
- Regular backups and testing
- Keep configurations up to date
Cleanup
To remove the RDS instance and associated resources:
terraform destroy
Note: Ensure you have backups before destruction.