This check flags ElastiCache Redis replication groups running without at-rest encryption, which leaves cached data readable on disk and in backups. You can only enable it at creation time, so the fix means standing up a new encrypted replication group and migrating to it.
ElastiCache often ends up holding more sensitive data than teams expect. Session tokens, cached user profiles, rate-limiting keys, partial query results, and sometimes full database rows all live in Redis. When at-rest encryption is off, all of that sits unencrypted on the underlying storage and in any snapshots you take. This check catches replication groups where that protection was never turned on.
Note: At-rest encryption in ElastiCache uses AES-256 and protects data on disk, including automatic and manual backups exported to S3. It is separate from in-transit encryption (TLS), which protects data moving between clients and nodes. You usually want both.
What this check detects
The elasticache_unencrypted check looks at every ElastiCache Redis replication group in your account and verifies that the AtRestEncryptionEnabled property is set to true. If a replication group reports false or the property is missing, the check fails.
At-rest encryption covers the data Redis persists to disk, including:
- RDB snapshots used for backups and failover
- Append-only files (AOF) when persistence is enabled
- Backups exported to Amazon S3
- Data swapped to disk during memory pressure
This applies to Redis replication groups. Memcached does not support at-rest encryption at all, so if you need encryption and you are on Memcached, that is a separate design conversation.
Why it matters
Unencrypted at-rest data is one of those problems that stays invisible right up until it is a headline. The data is fine while everything works as intended. The risk shows up when something goes sideways.
Backup and snapshot exposure
ElastiCache backups can be exported to an S3 bucket you control. If that bucket is misconfigured, or if an attacker gains read access to it, an unencrypted snapshot hands them a clean copy of your cache contents. Session tokens in that dump can be replayed to hijack live user sessions. Cached PII becomes a breach you have to report.
Disk and storage layer access
If an attacker or a misbehaving process gains access to the underlying storage, encrypted data is useless to them without the key. Unencrypted data is just sitting there. This is the exact scenario most compliance frameworks are trying to prevent.
Compliance and audit findings
Encryption at rest is an explicit requirement in PCI DSS, HIPAA, SOC 2, and most internal security baselines. An unencrypted replication group holding cardholder data or PHI is a direct audit finding, and auditors do not accept "the cache is temporary" as a justification when sensitive data passes through it.
Warning: Many teams assume Redis is "just a cache" and therefore low risk. In practice caches frequently hold authentication tokens, decrypted secrets, and full copies of database records. Treat the data sensitivity of your cache as equal to the source data it mirrors.
How to fix it
Here is the catch: at-rest encryption cannot be enabled on an existing replication group. It can only be set when the replication group is created. So remediation is a migration, not a toggle.
The general path is:
- Take a backup of the existing unencrypted replication group.
- Create a new replication group with encryption enabled, restoring from that backup.
- Cut your application over to the new endpoint.
- Delete the old replication group once you have verified the new one.
Step 1: Snapshot the existing group
aws elasticache create-snapshot \
--replication-group-id my-app-cache \
--snapshot-name my-app-cache-premigration
Wait until the snapshot status is available:
aws elasticache describe-snapshots \
--snapshot-name my-app-cache-premigration \
--query 'Snapshots[0].SnapshotStatus'
Step 2: Create an encrypted replication group from the snapshot
Note: You can use the AWS-managed key (aws/elasticache) for simplicity, or supply your own customer managed KMS key with --kms-key-id for tighter control over key rotation and access policy. Customer managed keys are the better choice for regulated workloads.
aws elasticache create-replication-group \
--replication-group-id my-app-cache-encrypted \
--replication-group-description "Encrypted cache for my-app" \
--snapshot-name my-app-cache-premigration \
--engine redis \
--cache-node-type cache.r6g.large \
--num-node-groups 1 \
--replicas-per-node-group 1 \
--at-rest-encryption-enabled \
--transit-encryption-enabled \
--kms-key-id alias/my-app-cache-key \
--cache-subnet-group-name my-cache-subnet-group \
--security-group-ids sg-0abc123def456
Warning: Enabling --transit-encryption-enabled at the same time is a good idea, but it forces clients to connect over TLS and may require an AUTH token. Update your client connection strings before you cut over, or the application will fail to connect to the new group.
Step 3: Cut over your application
Point your application at the new primary endpoint. The cleanest approach is to keep the endpoint in a config value or secret rather than hardcoded, so the cutover is a single change:
aws elasticache describe-replication-groups \
--replication-group-id my-app-cache-encrypted \
--query 'ReplicationGroups[0].NodeGroups[0].PrimaryEndpoint'
Because Redis caches are usually rebuildable, some teams skip the snapshot restore entirely and let the new encrypted cache warm up from live traffic. That avoids the migration complexity, but you accept a temporary cache-miss penalty during warm-up. Pick the approach that fits your latency budget.
Step 4: Delete the old replication group
Danger: This permanently deletes the old replication group and its nodes. Confirm the new encrypted group is serving production traffic and healthy before running this. Take a final snapshot if you want a rollback option.
aws elasticache delete-replication-group \
--replication-group-id my-app-cache \
--final-snapshot-identifier my-app-cache-final
Doing it with Infrastructure as Code
If you manage ElastiCache through Terraform, the fix is a property set, but Terraform will force a replacement because the attribute is immutable. Plan for that.
resource "aws_elasticache_replication_group" "app_cache" {
replication_group_id = "my-app-cache-encrypted"
description = "Encrypted cache for my-app"
engine = "redis"
node_type = "cache.r6g.large"
num_node_groups = 1
replicas_per_node_group = 1
at_rest_encryption_enabled = true
transit_encryption_enabled = true
kms_key_id = aws_kms_key.cache.arn
subnet_group_name = aws_elasticache_subnet_group.app.name
security_group_ids = [aws_security_group.cache.id]
}
For CloudFormation, set the same properties on the AWS::ElastiCache::ReplicationGroup resource:
{
"Type": "AWS::ElastiCache::ReplicationGroup",
"Properties": {
"ReplicationGroupId": "my-app-cache-encrypted",
"ReplicationGroupDescription": "Encrypted cache for my-app",
"Engine": "redis",
"CacheNodeType": "cache.r6g.large",
"AtRestEncryptionEnabled": true,
"TransitEncryptionEnabled": true,
"KmsKeyId": "alias/my-app-cache-key"
}
}
Tip: Set at_rest_encryption_enabled = true as the default in a shared Terraform module rather than leaving it to each team. That way new caches are encrypted by default and nobody has to remember the flag.
How to prevent it from happening again
The reason unencrypted caches exist is that encryption is opt-in at creation and easy to forget. Close that gap with a guardrail that fails loudly instead of relying on memory.
Block it in CI with policy as code
If you use Terraform, a Checkov or OPA policy in your pipeline can reject any plan that creates an unencrypted replication group. Checkov ships with this rule out of the box:
# Fails on CKV_AWS_29: ElastiCache Replication Group encryption at rest
checkov -d ./infra --check CKV_AWS_29
For OPA/Conftest, a minimal policy looks like this:
package main
deny[msg] {
resource := input.resource.aws_elasticache_replication_group[name]
not resource.at_rest_encryption_enabled
msg := sprintf("Replication group '%s' must enable at_rest_encryption_enabled", [name])
}
Enforce with an SCP or AWS Config rule
The managed AWS Config rule elasticache-redis-cluster-automatic-backup-check and the broader elasticache-rg-supported-engine checks help, and you can write a custom Config rule keyed on AtRestEncryptionEnabled to flag non-compliant groups continuously. Pair that with an alert so a drifted or manually created cache surfaces within minutes rather than at the next audit.
Tip: Lensix runs the elasticache_unencrypted check continuously across your accounts, so you catch a non-compliant replication group the moment it appears instead of waiting for a quarterly review. Wire the finding into your alerting channel and treat it as a release blocker.
Best practices
- Enable encryption at creation, always. Since it cannot be added later, treat at-rest encryption as a non-negotiable default for every new replication group.
- Turn on in-transit encryption too. At-rest protects the disk, TLS protects the wire. Sensitive data needs both to be properly covered.
- Use a customer managed KMS key for regulated data. It gives you control over rotation, granular key policies, and a clean audit trail through CloudTrail.
- Classify what your cache actually holds. If session tokens or PII ever touch Redis, the cache inherits that data's sensitivity and compliance obligations.
- Lock down backup destinations. Encrypt the S3 bucket that receives ElastiCache exports and restrict access with least-privilege policies. An encrypted cache with an open backup bucket still leaks.
- Standardize through modules. Bake encryption into a shared IaC module so secure defaults are the path of least resistance.
Encryption at rest for ElastiCache is cheap, has no measurable performance cost, and removes an entire class of breach scenario. The only real friction is that you have to plan for it up front, because retrofitting means a migration. Build the guardrails once, and this finding stops showing up.

