Skip to main content

EBS Volume Encryption

Overview

This check verifies that your Amazon EBS (Elastic Block Store) volumes have encryption enabled. EBS volumes store data for your EC2 instances, including operating systems, application data, and databases. When encryption is enabled, the data is protected at rest using AWS Key Management Service (KMS) keys.

Encrypting EBS volumes helps protect your data if the underlying storage hardware is ever accessed physically or if snapshots are shared inappropriately.

Risk

Without EBS volume encryption, your data is vulnerable to several threats:

  • Data exposure: Unencrypted volumes or snapshots can be copied, shared, or recovered, revealing raw data to unauthorized parties
  • Credential theft: Attackers with host or account access can read disks offline and harvest secrets like API keys, passwords, or certificates
  • Compliance violations: Many regulatory frameworks (PCI-DSS, HIPAA, SOC 2) require encryption of data at rest
  • Lateral movement: Adversaries can alter system images on unencrypted volumes, enabling persistent access or further compromise
  • Snapshot risks: Snapshots of unencrypted volumes are also unencrypted and can be shared across accounts

Remediation Steps

Prerequisites

You need:

  • AWS Console access with permissions to manage EC2 volumes and snapshots
  • A brief maintenance window if the volume is attached to a running instance (you will need to stop the instance or detach/reattach the volume)
Required IAM permissions (for administrators)

Your IAM user or role needs these permissions:

  • ec2:CreateSnapshot
  • ec2:CopySnapshot
  • ec2:CreateVolume
  • ec2:DescribeVolumes
  • ec2:DescribeSnapshots
  • ec2:AttachVolume
  • ec2:DetachVolume
  • ec2:DeleteVolume (for cleanup)
  • ec2:DeleteSnapshot (for cleanup)
  • kms:CreateGrant (if using customer-managed KMS keys)
  • kms:DescribeKey
  • ec2:ModifyEbsDefaultKmsKeyId (to change default encryption key)
  • ec2:EnableEbsEncryptionByDefault (to enable default encryption)

Important: You Cannot Encrypt an Existing Volume In-Place

AWS does not allow you to directly encrypt an existing unencrypted EBS volume. Instead, you must:

  1. Create a snapshot of the unencrypted volume
  2. Copy the snapshot with encryption enabled
  3. Create a new volume from the encrypted snapshot
  4. Replace the original volume with the encrypted one

AWS Console Method

First, enable default encryption so all future volumes are automatically encrypted:

  1. Go to EC2 Console in us-east-1
  2. In the left sidebar, scroll down and click EC2 Dashboard
  3. Under Account attributes, click Data protection and security
  4. Find EBS encryption and click Manage
  5. Check the box for Enable
  6. Optionally, select a custom KMS key (or use the default aws/ebs key)
  7. Click Update EBS encryption

This ensures all new volumes are encrypted by default.

Step 2: Identify Unencrypted Volumes

  1. In the EC2 Console, click Volumes in the left sidebar
  2. Add a filter: Click Add filter and select Encrypted = No
  3. Note the Volume ID, Availability Zone, and whether it is attached to an instance

Step 3: Create a Snapshot of the Unencrypted Volume

  1. Select the unencrypted volume
  2. Click Actions > Create snapshot
  3. Enter a description like Snapshot for encryption migration
  4. Click Create snapshot
  5. Go to Snapshots in the left sidebar and wait for the snapshot status to change to Completed

Step 4: Copy the Snapshot with Encryption

  1. In the Snapshots list, select your new snapshot
  2. Click Actions > Copy snapshot
  3. Keep the destination region as us-east-1 (or your current region)
  4. Check the box for Encrypt this snapshot
  5. Select your KMS key (default is fine for most cases)
  6. Enter a description like Encrypted copy of vol-xxxxx
  7. Click Copy snapshot
  8. Wait for the copy to complete (status changes to Completed)

Step 5: Create an Encrypted Volume from the Snapshot

  1. Select the encrypted snapshot copy
  2. Click Actions > Create volume from snapshot
  3. Select the same Availability Zone as the original volume
  4. Verify Encryption is enabled
  5. Click Create volume

Step 6: Replace the Original Volume

If the volume is attached to an instance:

  1. Stop the instance (if the volume is a root volume) or detach the volume
  2. Select the original unencrypted volume, click Actions > Detach volume
  3. Select the new encrypted volume, click Actions > Attach volume
  4. Select the instance and enter the same device name (e.g., /dev/sda1 for root)
  5. Click Attach volume
  6. Start the instance

Step 7: Clean Up (After Verification)

Once you have verified the instance works correctly with the encrypted volume:

  1. Delete the original unencrypted volume
  2. Optionally delete the intermediate snapshots
AWS CLI (optional)

Enable Default Encryption

Enable encryption by default for all new EBS volumes in the region:

aws ec2 enable-ebs-encryption-by-default --region us-east-1

Find Unencrypted Volumes

aws ec2 describe-volumes \
--filters Name=encrypted,Values=false \
--query 'Volumes[*].{ID:VolumeId,Size:Size,AZ:AvailabilityZone,State:State,Attached:Attachments[0].InstanceId}' \
--output table \
--region us-east-1

Complete Migration Script

Replace the placeholder values before running:

# Set variables
VOLUME_ID="vol-xxxxxxxxxxxxxxxxx"
REGION="us-east-1"

# Step 1: Create snapshot
SNAPSHOT_ID=$(aws ec2 create-snapshot \
--volume-id $VOLUME_ID \
--description "Snapshot for encryption migration" \
--query 'SnapshotId' \
--output text \
--region $REGION)

echo "Created snapshot: $SNAPSHOT_ID"

# Step 2: Wait for snapshot to complete
aws ec2 wait snapshot-completed \
--snapshot-ids $SNAPSHOT_ID \
--region $REGION

echo "Snapshot completed"

# Step 3: Copy snapshot with encryption
ENCRYPTED_SNAPSHOT_ID=$(aws ec2 copy-snapshot \
--source-region $REGION \
--source-snapshot-id $SNAPSHOT_ID \
--encrypted \
--description "Encrypted copy of $VOLUME_ID" \
--query 'SnapshotId' \
--output text \
--region $REGION)

echo "Created encrypted snapshot: $ENCRYPTED_SNAPSHOT_ID"

# Step 4: Wait for encrypted snapshot to complete
aws ec2 wait snapshot-completed \
--snapshot-ids $ENCRYPTED_SNAPSHOT_ID \
--region $REGION

echo "Encrypted snapshot completed"

# Step 5: Get original volume details
VOLUME_INFO=$(aws ec2 describe-volumes \
--volume-ids $VOLUME_ID \
--query 'Volumes[0].{AZ:AvailabilityZone,Type:VolumeType,Size:Size}' \
--output json \
--region $REGION)

AZ=$(echo $VOLUME_INFO | jq -r '.AZ')
VOLUME_TYPE=$(echo $VOLUME_INFO | jq -r '.Type')

# Step 6: Create encrypted volume
ENCRYPTED_VOLUME_ID=$(aws ec2 create-volume \
--snapshot-id $ENCRYPTED_SNAPSHOT_ID \
--availability-zone $AZ \
--volume-type $VOLUME_TYPE \
--encrypted \
--query 'VolumeId' \
--output text \
--region $REGION)

echo "Created encrypted volume: $ENCRYPTED_VOLUME_ID"

# Step 7: Wait for volume to be available
aws ec2 wait volume-available \
--volume-ids $ENCRYPTED_VOLUME_ID \
--region $REGION

echo "Encrypted volume is available"
echo "Next steps: Detach original volume, attach encrypted volume, then delete original"

Using a Custom KMS Key

To use a customer-managed KMS key instead of the default:

aws ec2 copy-snapshot \
--source-region us-east-1 \
--source-snapshot-id snap-xxxxxxxxxxxxxxxxx \
--encrypted \
--kms-key-id alias/my-ebs-key \
--region us-east-1
CloudFormation (optional)

CloudFormation automatically creates encrypted volumes when you specify the Encrypted property. Here is an example template:

AWSTemplateFormatVersion: '2010-09-09'
Description: Create an encrypted EBS volume

Parameters:
AvailabilityZone:
Type: AWS::EC2::AvailabilityZone::Name
Default: us-east-1a
Description: Availability zone for the volume

VolumeSize:
Type: Number
Default: 100
MinValue: 1
MaxValue: 16384
Description: Volume size in GiB

VolumeType:
Type: String
Default: gp3
AllowedValues:
- gp2
- gp3
- io1
- io2
- st1
- sc1
Description: EBS volume type

KmsKeyId:
Type: String
Default: ''
Description: KMS key ID for encryption (leave empty for default aws/ebs key)

Conditions:
UseCustomKmsKey: !Not [!Equals [!Ref KmsKeyId, '']]

Resources:
EncryptedVolume:
Type: AWS::EC2::Volume
Properties:
AvailabilityZone: !Ref AvailabilityZone
Size: !Ref VolumeSize
VolumeType: !Ref VolumeType
Encrypted: true
KmsKeyId: !If [UseCustomKmsKey, !Ref KmsKeyId, !Ref 'AWS::NoValue']
Tags:
- Key: Name
Value: encrypted-volume
- Key: Environment
Value: production

Outputs:
VolumeId:
Description: ID of the encrypted volume
Value: !Ref EncryptedVolume

VolumeArn:
Description: ARN of the encrypted volume
Value: !Sub arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:volume/${EncryptedVolume}

Deploy with:

aws cloudformation deploy \
--template-file encrypted-volume.yaml \
--stack-name encrypted-ebs-volume \
--parameter-overrides \
AvailabilityZone=us-east-1a \
VolumeSize=100 \
VolumeType=gp3 \
--region us-east-1

Enabling Default Encryption via CloudFormation

Note: AWS does not provide a native CloudFormation resource for enabling EBS encryption by default. Use a Custom Resource with Lambda or configure it via AWS CLI/Console separately.

Terraform (optional)

Enable Default Encryption

# Enable EBS encryption by default for the region
resource "aws_ebs_encryption_by_default" "enabled" {
enabled = true
}

# Optionally set a custom default KMS key
resource "aws_ebs_default_kms_key" "custom" {
key_arn = aws_kms_key.ebs.arn
}

# Create a KMS key for EBS encryption
resource "aws_kms_key" "ebs" {
description = "KMS key for EBS volume encryption"
deletion_window_in_days = 30
enable_key_rotation = true

tags = {
Name = "ebs-encryption-key"
}
}

resource "aws_kms_alias" "ebs" {
name = "alias/ebs-encryption-key"
target_key_id = aws_kms_key.ebs.key_id
}

Create an Encrypted Volume

variable "availability_zone" {
description = "Availability zone for the volume"
type = string
default = "us-east-1a"
}

variable "volume_size" {
description = "Volume size in GiB"
type = number
default = 100
}

resource "aws_ebs_volume" "encrypted" {
availability_zone = var.availability_zone
size = var.volume_size
type = "gp3"
encrypted = true
# Optionally specify a KMS key
# kms_key_id = aws_kms_key.ebs.arn

tags = {
Name = "encrypted-volume"
Environment = "production"
}
}

output "volume_id" {
description = "ID of the encrypted volume"
value = aws_ebs_volume.encrypted.id
}

Migrate Existing Unencrypted Volume

# This example shows how to create an encrypted copy of an existing volume
# Note: You still need to manually detach/attach volumes for running instances

variable "source_volume_id" {
description = "ID of the unencrypted source volume"
type = string
}

# Create a snapshot of the source volume (do this outside Terraform or import)
resource "aws_ebs_snapshot" "source" {
volume_id = var.source_volume_id
description = "Snapshot for encryption migration"

tags = {
Name = "migration-snapshot"
}
}

# Copy the snapshot with encryption
resource "aws_ebs_snapshot_copy" "encrypted" {
source_snapshot_id = aws_ebs_snapshot.source.id
source_region = "us-east-1"
encrypted = true
description = "Encrypted copy for migration"

tags = {
Name = "encrypted-migration-snapshot"
}
}

# Create encrypted volume from the encrypted snapshot
resource "aws_ebs_volume" "encrypted_replacement" {
availability_zone = "us-east-1a" # Must match original volume's AZ
snapshot_id = aws_ebs_snapshot_copy.encrypted.id
encrypted = true

tags = {
Name = "encrypted-replacement-volume"
}
}

Deploy with:

terraform init
terraform plan
terraform apply

Verification

After enabling encryption or migrating volumes, verify the changes:

  1. Check default encryption is enabled:

    • Go to EC2 Console > EC2 Dashboard
    • Under Account attributes, click Data protection and security
    • Verify EBS encryption shows Enabled
  2. Check individual volumes:

    • Go to EC2 Console > Volumes
    • Select a volume and check the Details tab
    • Verify Encryption shows Encrypted and displays the KMS key
  3. Test instance functionality:

    • If you replaced a volume on a running instance, start the instance and verify it boots correctly
    • Check that applications can read and write data normally
CLI verification commands

Check if default encryption is enabled:

aws ec2 get-ebs-encryption-by-default \
--query 'EbsEncryptionByDefault' \
--region us-east-1

Expected output: true

List all unencrypted volumes (should be empty after remediation):

aws ec2 describe-volumes \
--filters Name=encrypted,Values=false \
--query 'Volumes[*].VolumeId' \
--output text \
--region us-east-1

Expected output: Empty (no unencrypted volumes)

Check encryption status of a specific volume:

aws ec2 describe-volumes \
--volume-ids vol-xxxxxxxxxxxxxxxxx \
--query 'Volumes[0].{Encrypted:Encrypted,KmsKeyId:KmsKeyId}' \
--region us-east-1

Expected output shows Encrypted: true with a KMS key ID.

Additional Resources

Notes

  • No direct encryption: You cannot encrypt an existing unencrypted volume directly. The snapshot-copy-create workflow is required.

  • Instance downtime: For root volumes, you must stop the instance to detach and reattach volumes. Plan for a maintenance window.

  • Same Availability Zone: The new encrypted volume must be created in the same Availability Zone as the original to attach it to the same instance.

  • Performance impact: EBS encryption uses AES-256 encryption, but the performance impact is minimal because encryption/decryption happens on the EC2 host.

  • Snapshots inherit encryption: Snapshots of encrypted volumes are automatically encrypted. Snapshots of unencrypted volumes are unencrypted unless you copy them with encryption enabled.

  • Default encryption recommended: Enabling default encryption ensures all new volumes and snapshot copies are automatically encrypted, preventing future unencrypted volumes.

  • Customer-managed keys: For additional control, use a customer-managed KMS key instead of the default aws/ebs key. This allows you to manage key rotation and access policies.

  • Cross-account sharing: When sharing encrypted snapshots across accounts, the receiving account must have access to the KMS key used for encryption.

  • Cost: There is no additional cost for EBS encryption. KMS key usage incurs standard KMS charges.