Skip to main content

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:DescribeSnapshots
  • ec2:CopySnapshot
  • ec2:DeleteSnapshot
  • ec2:EnableEbsEncryptionByDefault
  • ec2:GetEbsEncryptionByDefault
  • kms:CreateKey (if creating a new KMS key)
  • kms:CreateAlias
  • kms: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.

This prevents future unencrypted snapshots from being created:

  1. Open EC2 in the AWS Console

  2. Navigate to EBS settings

    • In the left sidebar, scroll down to Account attributes
    • Click EBS encryption
  3. 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

  1. Go to Snapshots

    • In the EC2 Console, click Snapshots in the left sidebar under Elastic Block Store
  2. 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:

  1. Select the snapshot

    • Check the box next to the unencrypted snapshot
  2. 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
    • Add a Description like "Encrypted copy of snap-xxxxxxxx"
    • Click Copy snapshot
  3. 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

  1. Verify the encrypted copy is complete

    • Confirm the new snapshot shows completed status and is marked as Encrypted: true
  2. 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:

  1. Enable encryption by default for new volumes and snapshots
  2. 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:

  1. In the AWS Console:

    • Go to EC2 > Account attributes > EBS encryption
    • Verify Always encrypt new EBS volumes is Enabled
  2. Check your snapshots:

    • Go to EC2 > Snapshots
    • Filter by Encrypted: false
    • The list should be empty (no unencrypted snapshots)
  3. 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

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.