RDS DB clusters should be encrypted at rest
RDS DB clusters often hold the most sensitive data in an AWS account: customer records, credentials, financial transactions. Without encryption at rest, anyone who gains physical or logical access to the underlying EBS volumes or snapshots can read that data in plaintext. AWS charges nothing extra for RDS encryption, so there is no cost reason to skip it.
The bigger problem is that RDS cluster encryption cannot be toggled on after creation. A cluster launched without storage_encrypted = true must be replaced entirely, which means snapshot, restore to a new encrypted cluster, DNS cutover, and downtime. Catching this at provisioning time eliminates a painful migration later.
Retrofit consideration
RDS cluster encryption is immutable after creation. Enabling it requires creating an encrypted snapshot copy, restoring to a new encrypted cluster, and migrating connections. Expect downtime or a blue-green cutover.
Implementation
Choose the approach that matches how you manage Terraform.
Use the compliance.tf module to enforce this control by default. See get started with compliance.tf.
module "rds_aurora" {
source = "nistcsf.compliance.tf/terraform-aws-modules/rds-aurora/aws"
version = ">=10.0.0,<11.0.0"
create_db_subnet_group = true
create_security_group = true
engine = "aurora-mysql"
engine_version = "8.0.mysql_aurora.3.08.0"
instances = {
one = {
instance_class = "db.t4g.medium"
}
}
manage_master_user_password = true
master_username = "root"
name = "abc123"
skip_final_snapshot = true
subnets = ["subnet-12345678", "subnet-12345678"]
vpc_id = "vpc-12345678"
}
If you use terraform-aws-modules/rds-aurora/aws, set the right module inputs for this control. You can later migrate to the compliance.tf module with minimal changes because it is compatible by design.
module "rds_aurora" {
source = "terraform-aws-modules/rds-aurora/aws"
version = ">=10.0.0,<11.0.0"
create_db_subnet_group = true
create_security_group = true
engine = "aurora-mysql"
engine_version = "8.0.mysql_aurora.3.08.0"
instances = {
one = {
instance_class = "db.t4g.medium"
}
}
manage_master_user_password = true
master_username = "root"
name = "abc123"
skip_final_snapshot = true
subnets = ["subnet-12345678", "subnet-12345678"]
vpc_id = "vpc-12345678"
storage_encrypted = true
}
Use AWS provider resources directly. See docs for the resources involved: aws_rds_cluster.
resource "aws_rds_cluster" "this" {
backtrack_window = 72
cluster_identifier = "pofix-abc123"
db_subnet_group_name = "example-db-subnet-group"
enabled_cloudwatch_logs_exports = ["audit"]
engine = "aurora-mysql"
engine_version = "8.0.mysql_aurora.3.08.0"
manage_master_user_password = true
master_username = "dbadmin"
skip_final_snapshot = true
vpc_security_group_ids = ["sg-12345678"]
storage_encrypted = true
}
What this control checks
The control checks whether the aws_rds_cluster resource has storage_encrypted set to true. It fails when the argument is omitted or explicitly set to false. You can optionally specify a kms_key_id pointing to a customer-managed KMS key ARN; if omitted, AWS falls back to the default aws/rds service key. Either key satisfies the control, provided storage_encrypted = true is present.
Common pitfalls
Encryption cannot be enabled post-creation
Unlike S3 default encryption, RDS cluster encryption is set at creation time and is immutable. If you run
terraform applychangingstorage_encryptedfromfalsetotrueon an existingaws_rds_cluster, Terraform will force a destroy and recreate, causing full data loss unless you snapshot and restore manually first.Snapshot copies inherit encryption only when explicitly set
Copying an unencrypted cluster snapshot with
aws rds copy-db-cluster-snapshotproduces an unencrypted copy by default. Pass--kms-key-idto encrypt the copy. Theaws_db_cluster_snapshotTerraform resource does not support encrypting an unencrypted source, so the migration path typically runs through the CLI.Default KMS key limits cross-account snapshot sharing
Clusters encrypted with the default
aws/rdskey cannot share snapshots across AWS accounts. If you need cross-account DR or data sharing, specify a customer-managedkms_key_idwith a key policy granting the target account access.Global clusters require encryption on all members
For Aurora global databases, encryption is configured on each regional
aws_rds_clustermember usingstorage_encryptedand an optionalkms_key_id. Theaws_rds_global_clusterresource does not set or propagate encryption to member clusters, so don't rely on it to cover this.
Audit evidence
An auditor expects AWS Config rule evaluation results for rds-cluster-encryption-at-rest-enabled (or an equivalent custom rule) returning COMPLIANT for every RDS DB cluster in scope. Supporting evidence includes a CLI export from aws rds describe-db-clusters showing "StorageEncrypted": true and a populated KmsKeyId for each cluster. If a customer-managed KMS key is used, the auditor may also pull the KMS key policy to confirm appropriate access controls.
Screenshots from the RDS console showing the "Encryption" field on each cluster's Configuration tab work as point-in-time evidence. Security Hub dashboards or a third-party CSPM tool extend coverage beyond a snapshot, showing the control held across the full audit period.
Framework-specific interpretation
NIST Cybersecurity Framework v2.0: RDS cluster encryption directly implements PR.DS-1 under the Protect function, which calls for protecting data at rest. NIST CSF v2 treats this as a baseline data security control, not an enhancement, so storage_encrypted = true is the expected starting point for any cluster holding sensitive data.
Related controls
Tool mappings
Use these identifiers to cross-reference this control across tools, reports, and evidence.
Compliance.tf Control:
rds_db_cluster_encryption_at_rest_enabledAWS Config Managed Rule:
RDS_CLUSTER_ENCRYPTED_AT_RESTCheckov Checks:
CKV_AWS_327,CKV_AWS_96Powerpipe Control:
aws_compliance.control.rds_db_cluster_encryption_at_rest_enabledProwler Check:
rds_cluster_storage_encryptedAWS Security Hub Control:
RDS.27KICS Queries:
1a690d1d-0ae7-49fa-b2db-b75ae0dd1d3e,3199c26c-7871-4cb3-99c2-10a59244ce7f,656880aa-1388-488f-a6d4-8f73c23149b2Trivy Check:
AWS-0079
Last reviewed: 2026-03-09