Deploying AWS Aurora with Terraform
A comprehensive guide to setting up Amazon Aurora databases using Terraform Infrastructure as Code
Deploying AWS Aurora with Terraform
Amazon Aurora is a fully managed relational database engine that’s compatible with MySQL and PostgreSQL. This guide shows how to set up Aurora using Terraform.
Prerequisites
- AWS CLI configured
- Terraform installed
- VPC and subnets configured
- Basic understanding of relational databases
Project Structure
aws-aurora-terraform/
├── main.tf
├── variables.tf
├── outputs.tf
└── terraform.tfvars
Basic Aurora Configuration
# main.tf
provider "aws" {
region = var.aws_region
}
# Aurora Cluster
resource "aws_rds_cluster" "main" {
cluster_identifier = "${var.project_name}-cluster"
engine = "aurora-postgresql"
engine_version = "14.6"
database_name = var.database_name
master_username = var.master_username
master_password = var.master_password
backup_retention_period = 7
preferred_backup_window = "03:00-04:00"
skip_final_snapshot = true
vpc_security_group_ids = [aws_security_group.aurora.id]
db_subnet_group_name = aws_db_subnet_group.aurora.name
enabled_cloudwatch_logs_exports = ["postgresql"]
tags = {
Environment = var.environment
}
}
# Aurora Instances
resource "aws_rds_cluster_instance" "instances" {
count = var.instance_count
identifier = "${var.project_name}-instance-${count.index + 1}"
cluster_identifier = aws_rds_cluster.main.id
instance_class = var.instance_class
engine = aws_rds_cluster.main.engine
engine_version = aws_rds_cluster.main.engine_version
db_subnet_group_name = aws_db_subnet_group.aurora.name
monitoring_interval = 30
monitoring_role_arn = aws_iam_role.rds_enhanced_monitoring.arn
tags = {
Environment = var.environment
}
}
# Subnet Group
resource "aws_db_subnet_group" "aurora" {
name = "${var.project_name}-subnet-group"
subnet_ids = var.subnet_ids
tags = {
Environment = var.environment
}
}
# Security Group
resource "aws_security_group" "aurora" {
name = "${var.project_name}-aurora-sg"
description = "Security group for Aurora cluster"
vpc_id = var.vpc_id
ingress {
from_port = 5432
to_port = 5432
protocol = "tcp"
security_groups = var.allowed_security_group_ids
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Environment = var.environment
}
}
Enhanced Monitoring Configuration
# IAM Role for Enhanced Monitoring
resource "aws_iam_role" "rds_enhanced_monitoring" {
name = "${var.project_name}-rds-monitoring-role"
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_enhanced_monitoring" {
role = aws_iam_role.rds_enhanced_monitoring.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonRDSEnhancedMonitoringRole"
}
Parameter Group Configuration
# DB Cluster Parameter Group
resource "aws_rds_cluster_parameter_group" "main" {
family = "aurora-postgresql14"
name = "${var.project_name}-cluster-params"
parameter {
name = "log_statement"
value = "all"
}
parameter {
name = "log_min_duration_statement"
value = "1000"
}
}
# DB Instance Parameter Group
resource "aws_db_parameter_group" "main" {
family = "aurora-postgresql14"
name = "${var.project_name}-instance-params"
parameter {
name = "shared_preload_libraries"
value = "pg_stat_statements"
}
parameter {
name = "pg_stat_statements.track"
value = "ALL"
}
}
Variables Configuration
# variables.tf
variable "aws_region" {
description = "AWS region"
type = string
default = "us-west-2"
}
variable "project_name" {
description = "Project name"
type = string
}
variable "environment" {
description = "Environment name"
type = string
default = "dev"
}
variable "vpc_id" {
description = "VPC ID"
type = string
}
variable "subnet_ids" {
description = "List of subnet IDs"
type = list(string)
}
variable "instance_count" {
description = "Number of cluster instances"
type = number
default = 2
}
variable "instance_class" {
description = "Instance class"
type = string
default = "db.r5.large"
}
variable "database_name" {
description = "Database name"
type = string
}
variable "master_username" {
description = "Master username"
type = string
}
variable "master_password" {
description = "Master password"
type = string
sensitive = true
}
Best Practices
-
High Availability
- Deploy across multiple AZs
- Use appropriate instance count
- Configure automatic failover
- Implement proper backup strategy
-
Security
- Use encryption at rest
- Implement proper security groups
- Use IAM authentication
- Regular password rotation
-
Performance
- Choose appropriate instance size
- Monitor performance metrics
- Use parameter groups effectively
- Implement connection pooling
-
Cost Optimization
- Use appropriate instance types
- Scale instances as needed
- Monitor storage usage
- Use Aurora Serverless when appropriate
Serverless Configuration
resource "aws_rds_cluster" "serverless" {
cluster_identifier = "${var.project_name}-serverless"
engine = "aurora-postgresql"
engine_version = "14.6"
engine_mode = "provisioned"
database_name = var.database_name
master_username = var.master_username
master_password = var.master_password
serverlessv2_scaling_configuration {
min_capacity = 0.5
max_capacity = 16
}
vpc_security_group_ids = [aws_security_group.aurora.id]
db_subnet_group_name = aws_db_subnet_group.aurora.name
}
Monitoring Configuration
# CloudWatch Alarms
resource "aws_cloudwatch_metric_alarm" "cpu_utilization" {
alarm_name = "${var.project_name}-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 Aurora CPU utilization"
alarm_actions = [var.sns_topic_arn]
dimensions = {
DBClusterIdentifier = aws_rds_cluster.main.cluster_identifier
}
}
resource "aws_cloudwatch_metric_alarm" "free_storage" {
alarm_name = "${var.project_name}-free-storage"
comparison_operator = "LessThanThreshold"
evaluation_periods = "1"
metric_name = "FreeLocalStorage"
namespace = "AWS/RDS"
period = "300"
statistic = "Average"
threshold = "10000000000" # 10GB in bytes
alarm_description = "This metric monitors free storage space"
alarm_actions = [var.sns_topic_arn]
dimensions = {
DBClusterIdentifier = aws_rds_cluster.main.cluster_identifier
}
}
Deployment Steps
- Initialize Terraform:
terraform init
- Plan the deployment:
terraform plan
- Apply the configuration:
terraform apply
Clean Up
Remove all resources when done:
terraform destroy
Common Use Cases
- Global Database
resource "aws_rds_global_cluster" "global" {
global_cluster_identifier = "${var.project_name}-global"
engine = "aurora-postgresql"
engine_version = "14.6"
database_name = var.database_name
}
resource "aws_rds_cluster" "primary" {
cluster_identifier = "${var.project_name}-primary"
engine = "aurora-postgresql"
engine_version = "14.6"
global_cluster_identifier = aws_rds_global_cluster.global.id
# ... other configuration
}
resource "aws_rds_cluster" "secondary" {
provider = aws.secondary_region
cluster_identifier = "${var.project_name}-secondary"
engine = "aurora-postgresql"
engine_version = "14.6"
global_cluster_identifier = aws_rds_global_cluster.global.id
# ... other configuration
}
- Custom Backup Strategy
resource "aws_rds_cluster" "backup" {
# ... other configuration
backup_retention_period = 14
preferred_backup_window = "03:00-04:00"
preferred_maintenance_window = "mon:04:00-mon:05:00"
copy_tags_to_snapshot = true
deletion_protection = true
enabled_cloudwatch_logs_exports = ["postgresql"]
}
Conclusion
This setup provides a comprehensive foundation for deploying Aurora using Terraform. Remember to:
- Plan your database architecture carefully
- Implement proper security measures
- Monitor performance and costs
- Keep your configurations versioned
- Test thoroughly before production deployment
The complete code can be customized based on your specific requirements and use cases.