This check flags RDS instances whose underlying storage is not encrypted at rest, leaving database files, snapshots, and backups readable if the storage layer is ever exposed. You cannot toggle encryption on a running instance, so the fix is to create an encrypted snapshot copy and restore from it.
Encryption at rest is one of those controls that costs almost nothing to enable and is painful to retrofit later. Amazon RDS supports it natively through AWS KMS, but it is off by default unless you explicitly request it at creation time. The rds_unencrypted check looks at each RDS instance and reports any where StorageEncrypted is set to false.
What this check detects
The check inspects every RDS DB instance in the account and region, reading the StorageEncrypted attribute. When that attribute is false, the instance fails the check.
Encryption at rest in RDS covers more than just the live database volume. When you enable it, AWS encrypts:
- The underlying storage volumes for the instance
- Automated backups
- Read replicas
- Manual snapshots
- Logs written to the storage layer
Note: RDS encryption uses AES-256 and is handled transparently by the storage layer. Your application does not need code changes, and there is no measurable query performance impact for the vast majority of workloads.
You can confirm the status of a single instance from the CLI:
aws rds describe-db-instances \
--db-instance-identifier my-prod-db \
--query 'DBInstances[0].StorageEncrypted'
A return value of false means the storage is in cleartext on disk.
Why it matters
Unencrypted database storage is a quiet risk. Everything looks fine in day to day operations, and the gap only becomes visible during an audit, an incident, or a compliance review. Here is where it bites.
Snapshot and backup exposure
The most common real-world failure is not someone walking off with a physical disk. It is a snapshot that gets shared too widely. If an instance is unencrypted, its snapshots are unencrypted too. An engineer who shares a snapshot with another account, or accidentally marks one public, hands over a fully readable copy of your database. With encryption enabled, the snapshot is useless without access to the KMS key.
Danger: A public, unencrypted RDS snapshot is a full data breach waiting to happen. Anyone with an AWS account can restore it and read every row. Encrypted snapshots cannot be shared publicly at all, which is a hard safety boundary worth having.
Compliance and contractual requirements
PCI DSS, HIPAA, SOC 2, and most customer security questionnaires expect encryption at rest for any system holding sensitive data. An unencrypted production database is a finding that will surface in an audit and slow down deals. Enabling RDS encryption is usually the single fastest way to close that gap.
Defense in depth
Encryption at rest does not replace network controls or IAM, but it removes an entire class of failure. If a backup ends up in the wrong S3 bucket, or a storage volume is somehow recovered, the data is unreadable without the key. That layered protection is the point.
How to fix it
Here is the catch that trips up most teams: you cannot enable encryption on an existing RDS instance in place. There is no toggle. Encryption can only be set at creation. To encrypt an existing database, you copy a snapshot with encryption applied, then restore a new instance from that encrypted snapshot.
Warning: This process creates a new instance with a new endpoint. You will need a maintenance window to cut traffic over, or a replication strategy to minimize downtime. Plan the DNS or connection string update before you start.
Step 1: Take a snapshot of the existing instance
aws rds create-db-snapshot \
--db-instance-identifier my-prod-db \
--db-snapshot-identifier my-prod-db-snap
Step 2: Copy the snapshot with encryption enabled
This is where the encryption gets applied. Point the copy at a KMS key.
aws rds copy-db-snapshot \
--source-db-snapshot-identifier my-prod-db-snap \
--target-db-snapshot-identifier my-prod-db-snap-encrypted \
--kms-key-id alias/aws/rds
Tip: Use a customer managed KMS key (CMK) instead of the default alias/aws/rds key. A CMK lets you control key rotation, scope access with key policies, and audit usage through CloudTrail. Pass its key ID or alias to --kms-key-id.
Step 3: Restore a new instance from the encrypted snapshot
aws rds restore-db-instance-from-db-snapshot \
--db-instance-identifier my-prod-db-encrypted \
--db-snapshot-identifier my-prod-db-snap-encrypted
Match the new instance to the original on subnet group, parameter group, security groups, and instance class. Verify those settings before you send traffic to it.
Step 4: Cut traffic over and verify
Update your application connection string or the Route 53 CNAME to point at the new encrypted endpoint. Confirm the new instance reports encryption:
aws rds describe-db-instances \
--db-instance-identifier my-prod-db-encrypted \
--query 'DBInstances[0].StorageEncrypted'
Danger: Do not delete the original instance until you have confirmed the new one is serving production traffic correctly and you have a verified encrypted snapshot. Once you drop the old instance, its automated backups go with it after the retention period.
How to prevent it from happening again
The reliable fix is to make encryption the default in your provisioning code and to block unencrypted instances before they reach production.
Set encryption in Terraform
Every aws_db_instance resource should carry these two lines:
resource "aws_db_instance" "main" {
identifier = "my-prod-db"
engine = "postgres"
instance_class = "db.t3.medium"
allocated_storage = 50
storage_encrypted = true
kms_key_id = aws_kms_key.rds.arn
# ... other settings
}
Block it in CI with policy as code
Catch the misconfiguration in the pull request, not in production. With Checkov scanning your Terraform plan, the relevant rule is built in:
checkov -d . --check CKV_AWS_16
If you use OPA and Conftest, a rego policy can deny any plan that creates an unencrypted instance:
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_db_instance"
resource.change.after.storage_encrypted == false
msg := sprintf("RDS instance %v must have storage_encrypted = true", [resource.address])
}
Enforce account-wide with SCPs
For a hard guardrail, a Service Control Policy can deny rds:CreateDBInstance calls that do not request encryption, stopping the problem even when someone provisions outside your pipeline.
Tip: Pair the preventive controls with continuous detection. Lensix re-runs the rds_unencrypted check across your accounts so that anything created out of band, in a forgotten region or a sandbox account, still gets flagged.
Best practices
- Encrypt at creation, always. Since you cannot enable it in place, default to encrypted for every new instance regardless of how sensitive the data seems today.
- Use customer managed KMS keys for production databases so you control rotation, access policies, and key deletion.
- Encrypt read replicas and cross-region copies. A replica of an encrypted instance is encrypted, but cross-region snapshot copies need their own key in the destination region. Confirm it explicitly.
- Lock down snapshot sharing. Audit which snapshots are shared and to whom. Encrypted snapshots cannot be made public, which is exactly the protection you want.
- Enable encryption in transit too. At-rest encryption protects the disk, but force TLS connections to protect data on the wire. Set
rds.force_sslin the parameter group for Postgres, or require SSL in your MySQL user grants. - Scan every region. Forgotten instances in non-primary regions are a frequent source of unencrypted databases. Make sure your detection covers all of them.
Encryption at rest is cheap insurance. The only real work is remembering to set it before the instance exists, and putting a CI gate in place so nobody forgets. Once both are in place, this check stays green without anyone thinking about it.

