EBS Snapshots Encrypted
Overview
This check verifies that your Amazon EBS snapshots are encrypted at rest using AWS KMS. EBS snapshots are point-in-time copies of your EBS volumes, and encrypting them protects your data from unauthorized access.
Risk
Unencrypted EBS snapshots expose your data to significant security risks:
- Data exfiltration: Attackers who gain access to unencrypted snapshots can download complete disk images containing sensitive data
- Credential harvesting: Snapshots may contain stored credentials, SSH keys, or database passwords that can be extracted
- Volume cloning: Bad actors can create new volumes from unencrypted snapshots for offline analysis
- Lateral movement: Compromised snapshot access can enable attackers to move deeper into your infrastructure
- Compliance violations: Many frameworks (PCI-DSS, HIPAA, SOC 2) require encryption of data at rest
Remediation Steps
Prerequisites
You need:
- AWS Console access with permissions to manage EC2 snapshots and KMS keys
- Identify which snapshots are unencrypted and need remediation
Required IAM permissions (for administrators)
Your IAM user or role needs these permissions:
ec2:DescribeSnapshotsec2:CopySnapshotec2:DeleteSnapshotec2:EnableEbsEncryptionByDefaultec2:GetEbsEncryptionByDefaultkms:CreateKey(if creating a new KMS key)kms:CreateAliaskms:DescribeKey
AWS Console Method
Since you cannot encrypt an existing snapshot in place, you must create an encrypted copy and then delete the original unencrypted snapshot.
Step 1: Enable Encryption by Default (Recommended)
This prevents future unencrypted snapshots from being created:
-
Open EC2 in the AWS Console
- Go to EC2 Console in us-east-1
-
Navigate to EBS settings
- In the left sidebar, scroll down to Account attributes
- Click EBS encryption
-
Enable encryption by default
- Click Manage
- Check the box for Enable
- Optionally, select a default KMS key (or leave it as the AWS managed key
aws/ebs) - Click Update EBS encryption
Step 2: Find Unencrypted Snapshots
-
Go to Snapshots
- In the EC2 Console, click Snapshots in the left sidebar under Elastic Block Store
-
Filter for unencrypted snapshots
- Click the search/filter bar
- Select Encrypted from the dropdown
- Choose false
- This shows all unencrypted snapshots you own
Step 3: Create an Encrypted Copy
For each unencrypted snapshot:
-
Select the snapshot
- Check the box next to the unencrypted snapshot
-
Copy with encryption
- Click Actions > Copy snapshot
- In the Destination Region, select us-east-1 (or your target region)
- Check the box for Encrypt this snapshot
- Under KMS key, choose your preferred key:
- Default uses the AWS managed key
aws/ebs - Or select a customer-managed key for more control
- Default uses the AWS managed key
- Add a Description like "Encrypted copy of snap-xxxxxxxx"
- Click Copy snapshot
-
Wait for the copy to complete
- The new encrypted snapshot will appear in your list with status pending
- Wait until the status changes to completed
Step 4: Delete the Original Unencrypted Snapshot
-
Verify the encrypted copy is complete
- Confirm the new snapshot shows completed status and is marked as Encrypted: true
-
Delete the unencrypted snapshot
- Select the original unencrypted snapshot
- Click Actions > Delete snapshot
- Confirm the deletion
Warning: Before deleting the original snapshot, ensure:
- The encrypted copy is fully complete
- No AMIs depend on the unencrypted snapshot
- No automated processes reference the old snapshot ID
AWS CLI (optional)
Enable encryption by default
aws ec2 enable-ebs-encryption-by-default \
--region us-east-1
Verify it is enabled:
aws ec2 get-ebs-encryption-by-default \
--region us-east-1
List unencrypted snapshots
aws ec2 describe-snapshots \
--owner-ids self \
--filters "Name=encrypted,Values=false" \
--query 'Snapshots[*].[SnapshotId,VolumeSize,Description]' \
--output table \
--region us-east-1
Copy a snapshot with encryption
aws ec2 copy-snapshot \
--source-region us-east-1 \
--source-snapshot-id snap-0123456789abcdef0 \
--encrypted \
--kms-key-id alias/aws/ebs \
--description "Encrypted copy of snap-0123456789abcdef0" \
--region us-east-1
To use a customer-managed KMS key instead:
aws ec2 copy-snapshot \
--source-region us-east-1 \
--source-snapshot-id snap-0123456789abcdef0 \
--encrypted \
--kms-key-id alias/my-ebs-key \
--description "Encrypted copy of snap-0123456789abcdef0" \
--region us-east-1
Wait for the copy to complete
aws ec2 wait snapshot-completed \
--snapshot-ids snap-NEW-ENCRYPTED-ID \
--region us-east-1
Delete the original unencrypted snapshot
aws ec2 delete-snapshot \
--snapshot-id snap-0123456789abcdef0 \
--region us-east-1
Batch script for multiple snapshots
#!/bin/bash
REGION="us-east-1"
# Get all unencrypted snapshots
SNAPSHOTS=$(aws ec2 describe-snapshots \
--owner-ids self \
--filters "Name=encrypted,Values=false" \
--query 'Snapshots[*].SnapshotId' \
--output text \
--region $REGION)
for SNAP_ID in $SNAPSHOTS; do
echo "Copying $SNAP_ID with encryption..."
NEW_SNAP=$(aws ec2 copy-snapshot \
--source-region $REGION \
--source-snapshot-id $SNAP_ID \
--encrypted \
--description "Encrypted copy of $SNAP_ID" \
--query 'SnapshotId' \
--output text \
--region $REGION)
echo "Created encrypted snapshot: $NEW_SNAP"
echo "Waiting for completion..."
aws ec2 wait snapshot-completed \
--snapshot-ids $NEW_SNAP \
--region $REGION
echo "Deleting original: $SNAP_ID"
aws ec2 delete-snapshot \
--snapshot-id $SNAP_ID \
--region $REGION
done
CloudFormation (optional)
CloudFormation cannot directly create encrypted copies of existing snapshots. However, you can:
- Enable encryption by default for new volumes and snapshots
- Create new encrypted snapshots from running instances
Enable EBS encryption by default (StackSets for organization-wide)
AWSTemplateFormatVersion: '2010-09-09'
Description: Enable EBS encryption by default
Resources:
EnableEbsEncryption:
Type: Custom::EnableEbsEncryption
Properties:
ServiceToken: !GetAtt EnableEncryptionFunction.Arn
EnableEncryptionFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: enable-ebs-encryption-by-default
Runtime: python3.11
Handler: index.handler
Timeout: 30
Role: !GetAtt LambdaRole.Arn
Code:
ZipFile: |
import boto3
import cfnresponse
def handler(event, context):
try:
if event['RequestType'] in ['Create', 'Update']:
ec2 = boto3.client('ec2')
ec2.enable_ebs_encryption_by_default()
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
except Exception as e:
cfnresponse.send(event, context, cfnresponse.FAILED, {'Error': str(e)})
LambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: EnableEbsEncryption
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ec2:EnableEbsEncryptionByDefault
- ec2:GetEbsEncryptionByDefault
Resource: '*'
Create a KMS key for EBS encryption
AWSTemplateFormatVersion: '2010-09-09'
Description: KMS key for EBS encryption
Resources:
EbsEncryptionKey:
Type: AWS::KMS::Key
Properties:
Description: Customer-managed key for EBS volume and snapshot encryption
EnableKeyRotation: true
KeyPolicy:
Version: '2012-10-17'
Statement:
- Sid: Enable IAM User Permissions
Effect: Allow
Principal:
AWS: !Sub arn:aws:iam::${AWS::AccountId}:root
Action: kms:*
Resource: '*'
- Sid: Allow EBS to use the key
Effect: Allow
Principal:
AWS: '*'
Action:
- kms:Encrypt
- kms:Decrypt
- kms:ReEncrypt*
- kms:GenerateDataKey*
- kms:CreateGrant
- kms:DescribeKey
Resource: '*'
Condition:
StringEquals:
kms:ViaService: !Sub ec2.${AWS::Region}.amazonaws.com
kms:CallerAccount: !Ref AWS::AccountId
EbsEncryptionKeyAlias:
Type: AWS::KMS::Alias
Properties:
AliasName: alias/ebs-encryption-key
TargetKeyId: !Ref EbsEncryptionKey
Outputs:
KmsKeyArn:
Description: ARN of the KMS key for EBS encryption
Value: !GetAtt EbsEncryptionKey.Arn
KmsKeyAlias:
Description: Alias of the KMS key
Value: !Ref EbsEncryptionKeyAlias
Deploy with:
aws cloudformation deploy \
--template-file ebs-kms-key.yaml \
--stack-name ebs-encryption-key \
--capabilities CAPABILITY_IAM \
--region us-east-1
Terraform (optional)
Enable EBS encryption by default
# Enable EBS encryption by default for the region
resource "aws_ebs_encryption_by_default" "enabled" {
enabled = true
}
# Optionally set a default KMS key
resource "aws_ebs_default_kms_key" "default" {
key_arn = aws_kms_key.ebs.arn
}
# Customer-managed KMS key for EBS
resource "aws_kms_key" "ebs" {
description = "Customer-managed key for EBS encryption"
deletion_window_in_days = 30
enable_key_rotation = true
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "Enable IAM User Permissions"
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
}
Action = "kms:*"
Resource = "*"
},
{
Sid = "Allow EBS to use the key"
Effect = "Allow"
Principal = {
AWS = "*"
}
Action = [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:CreateGrant",
"kms:DescribeKey"
]
Resource = "*"
Condition = {
StringEquals = {
"kms:ViaService" = "ec2.${data.aws_region.current.name}.amazonaws.com"
"kms:CallerAccount" = data.aws_caller_identity.current.account_id
}
}
}
]
})
}
resource "aws_kms_alias" "ebs" {
name = "alias/ebs-encryption-key"
target_key_id = aws_kms_key.ebs.key_id
}
data "aws_caller_identity" "current" {}
data "aws_region" "current" {}
output "kms_key_arn" {
description = "ARN of the KMS key for EBS encryption"
value = aws_kms_key.ebs.arn
}
Copy an existing snapshot with encryption
# Copy an unencrypted snapshot to create an encrypted version
resource "aws_ebs_snapshot_copy" "encrypted" {
source_snapshot_id = "snap-0123456789abcdef0" # Replace with your snapshot ID
source_region = "us-east-1"
encrypted = true
kms_key_id = aws_kms_key.ebs.arn
description = "Encrypted copy of snap-0123456789abcdef0"
tags = {
Name = "encrypted-snapshot-copy"
}
}
Deploy with:
terraform init
terraform plan
terraform apply
Verification
After enabling encryption by default and copying snapshots:
-
In the AWS Console:
- Go to EC2 > Account attributes > EBS encryption
- Verify Always encrypt new EBS volumes is Enabled
-
Check your snapshots:
- Go to EC2 > Snapshots
- Filter by Encrypted: false
- The list should be empty (no unencrypted snapshots)
-
Verify a specific snapshot:
- Select any snapshot
- In the Details tab, confirm Encrypted shows true
- The KMS key ID or KMS key alias should be displayed
CLI verification commands
Check if encryption by default is enabled:
aws ec2 get-ebs-encryption-by-default \
--region us-east-1 \
--query 'EbsEncryptionByDefault'
Expected output: true
List any remaining unencrypted snapshots:
aws ec2 describe-snapshots \
--owner-ids self \
--filters "Name=encrypted,Values=false" \
--query 'Snapshots[*].SnapshotId' \
--output text \
--region us-east-1
If the output is empty, all your snapshots are encrypted.
Verify a specific snapshot is encrypted:
aws ec2 describe-snapshots \
--snapshot-ids snap-0123456789abcdef0 \
--query 'Snapshots[0].{SnapshotId:SnapshotId,Encrypted:Encrypted,KmsKeyId:KmsKeyId}' \
--region us-east-1
Additional Resources
- AWS Documentation: Amazon EBS encryption
- AWS Documentation: Encryption by default
- AWS Documentation: Copy an Amazon EBS snapshot
- AWS Documentation: AWS KMS keys for Amazon EBS
Notes
- Encryption is one-way: You cannot unencrypt an encrypted snapshot. Plan accordingly.
- In-place encryption is not possible: You must copy unencrypted snapshots to create encrypted versions, then delete the originals.
- AMI dependencies: Before deleting unencrypted snapshots, check if any Amazon Machine Images (AMIs) reference them. You may need to create new AMIs from encrypted snapshots.
- Cross-region copies: When copying snapshots across regions, you can specify a different KMS key in the destination region.
- Performance: Encrypting snapshots does not impact I/O performance. AWS handles encryption transparently.
- Costs: There is no additional charge for EBS encryption. You pay only for the KMS key usage if using a customer-managed key ($1/month per key plus API calls).
- Enable for all regions: Encryption by default is a per-region setting. Enable it in each region where you create EBS volumes.