Skip to content

EBS volumes should have encryption at rest enabled

EBS volumes store database files, application state, OS credentials, and temporary data that can outlive the instance they were attached to. Without encryption, anyone with physical access to the underlying storage or access to an unencrypted snapshot can read that data in cleartext. KMS-based encryption at rest costs nothing in throughput on Nitro-based instance types and eliminates an entire class of data exposure.

Enabling account-level default EBS encryption per region prevents engineers from accidentally creating unencrypted volumes. Retrofitting requires snapshot-copy-replace workflows with downtime, which is why enabling the default from day one is worth the effort.

Retrofit consideration

Existing unencrypted volumes cannot be encrypted in place. You must snapshot the volume, create an encrypted copy of the snapshot, then create a new volume from it. This requires detaching the original volume, causing downtime for attached instances.

Implementation

Choose the approach that matches how you manage Terraform.

Use the compliance.tf module to enforce this control by default. See get started with compliance.tf.

module "ec2_instance" {
  source  = "gdpr.compliance.tf/terraform-aws-modules/ec2-instance/aws"
  version = ">=6.0.0"

  ami_ssm_parameter = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64"
  instance_type     = "t4g.nano"
  subnet_id         = "subnet-abc123"

  ebs_volumes = {
    encrypted = true
  }
}

module "ec2_instance" {
  source  = "nist80053.compliance.tf/terraform-aws-modules/ec2-instance/aws"
  version = ">=6.0.0"

  ami_ssm_parameter = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64"
  instance_type     = "t4g.nano"
  subnet_id         = "subnet-abc123"

  ebs_volumes = {
    encrypted = true
  }
}

module "ec2_instance" {
  source  = "nistcsf.compliance.tf/terraform-aws-modules/ec2-instance/aws"
  version = ">=6.0.0"

  ami_ssm_parameter = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64"
  instance_type     = "t4g.nano"
  subnet_id         = "subnet-abc123"

  ebs_volumes = {
    encrypted = true
  }
}

module "ec2_instance" {
  source  = "fedrampmoderate.compliance.tf/terraform-aws-modules/ec2-instance/aws"
  version = ">=6.0.0"

  ami_ssm_parameter = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64"
  instance_type     = "t4g.nano"
  subnet_id         = "subnet-abc123"

  ebs_volumes = {
    encrypted = true
  }
}

module "ec2_instance" {
  source  = "rbicybersecurity.compliance.tf/terraform-aws-modules/ec2-instance/aws"
  version = ">=6.0.0"

  ami_ssm_parameter = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64"
  instance_type     = "t4g.nano"
  subnet_id         = "subnet-abc123"

  ebs_volumes = {
    encrypted = true
  }
}

If you use terraform-aws-modules/ec2-instance/aws, set the right module inputs for this control. You can later migrate to the compliance.tf module with minimal changes because it is compatible by design.

module "ec2_instance" {
  source  = "terraform-aws-modules/ec2-instance/aws"
  version = ">=6.0.0"

  ami_ssm_parameter = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64"
  instance_type     = "t4g.nano"
  subnet_id         = "subnet-abc123"

  ebs_volumes = {
    encrypted = true
  }
}

Use AWS provider resources directly. See docs for the resources involved: aws_ebs_volume.

resource "aws_ebs_volume" "this" {
  availability_zone = local.availability_zone
  size              = 10

  encrypted = true
}

What this control checks

The aws_ebs_volume resource must have encrypted = true. Optionally, kms_key_id specifies a customer-managed KMS key ARN; if omitted, the AWS-managed alias/aws/ebs key is used. For volumes created through aws_instance, the encrypted argument within root_block_device and ebs_block_device blocks must also be true.

Alternatively, aws_ebs_encryption_by_default with enabled = true causes all new volumes in the region to be encrypted automatically, but this control evaluates each volume individually. A volume passes when its encrypted attribute is true. It fails when encrypted is false or left unset and no account-level default applies.

Common pitfalls

  • Account-level default does not retroactively encrypt existing volumes

    The account-level default only affects volumes created after it's switched on. Pre-existing unencrypted volumes stay unencrypted and keep failing this control until you run the snapshot-copy replacement workflow on each one.

  • Root volumes from unencrypted AMIs

    An unencrypted AMI produces an unencrypted root volume regardless of your account default, unless you explicitly set encrypted = true in the root_block_device block. Use aws_ami_copy with encrypted = true to create an encrypted copy of the AMI before referencing it in your instance config.

  • Cross-region snapshot copies lose encryption settings

    Source-region encryption settings don't carry to the destination when you use aws_ebs_snapshot_copy. Set encrypted = true and provide a kms_key_id valid in the target region explicitly, or the copy lands unencrypted.

  • Terraform import of unencrypted volumes forces replacement

    Adding encrypted = true to an imported unencrypted volume's config won't encrypt it in place. Terraform flags it as a force-replacement, meaning the volume gets destroyed on apply. Plan the data migration before you run it.

Audit evidence

An auditor expects AWS Config rule evaluation results for the managed rule encrypted-volumes, showing all EBS volumes as compliant. The EC2 console Volumes page should display "Encrypted: Yes" for every volume. CloudTrail CreateVolume events should include "encrypted": true in the response element, confirming encryption was applied at creation time.

Security Hub aggregates this finding across accounts and regions into a single artifact. Output from aws ec2 get-ebs-encryption-by-default for each region shows the preventive posture. If customer-managed KMS keys are in use, include key policies and key rotation status from aws kms describe-key in the audit package.

Framework-specific interpretation

GDPR: Article 32 requires appropriate technical measures to protect personal data. Encryption covers the technical-measures requirement. Article 34 is relevant here too: encrypted data that leaks may not trigger the individual notification obligation if it's unintelligible to the recipient.

NIST SP 800-53 Rev 5: SC-28 requires protection of information at rest, and EBS encryption directly satisfies it. When you specify a customer-managed KMS key, you also address SC-12 by demonstrating deliberate key lifecycle governance rather than relying entirely on AWS-managed keys.

NIST Cybersecurity Framework v2.0: EBS encryption at rest maps to PR.DS under the Protect function, covering unauthorized disclosure of stored data if physical or logical access controls are bypassed.

FedRAMP Moderate Baseline Rev 4: At the Moderate baseline, SC-28 applies and federal data on EBS volumes must use FIPS 140-validated cryptographic modules. AWS KMS meets that requirement.

Tool mappings

Use these identifiers to cross-reference this control across tools, reports, and evidence.

  • Compliance.tf Control: ebs_volume_encryption_at_rest_enabled

  • AWS Config Managed Rule: ENCRYPTED_VOLUMES

  • Checkov Checks: CKV2_AWS_2, CKV_AWS_3

  • Powerpipe Controls: aws_compliance.control.ebs_attached_volume_encryption_enabled, aws_compliance.control.ebs_volume_encryption_at_rest_enabled

  • Prowler Check: ec2_ebs_volume_encryption

  • AWS Security Hub Control: EC2.3

  • KICS Queries: 1f624961-9a18-4387-91c8-3856e1974b6f, cc997676-481b-4e93-aa81-d19f8c5e9b12

  • Trivy Check: AWS-0026

Last reviewed: 2026-03-09