Attached EBS volumes should have encryption enabled
EBS volumes frequently store operating system swap files, application logs, database journals, and temporary processing data containing sensitive information. Without encryption, that data sits on shared physical storage hardware in plaintext, exposed to risks from improper disk decommissioning or unauthorized snapshot copying.
Encryption at the volume level costs nothing extra in AWS and adds negligible latency on modern Nitro-based instances. Leaving it off is a pure oversight with real consequences, especially when a volume gets snapshotted and shared across accounts.
Retrofit consideration
Existing unencrypted EBS volumes cannot be encrypted in place. You must create an encrypted snapshot, create a new encrypted volume from that snapshot, detach the old volume, and attach the new one. This requires instance downtime or careful live-migration planning. For root volumes, the instance must be stopped.
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 = "soc2.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 = "pcidss.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 = "hipaa.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 = "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 = "cisv80ig1.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 = "nist800171.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 = "awscontroltower.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 = "cisacyberessentials.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 = "nydfs23.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 = "cisv140.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 = "ffiec.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 = "eugmpannex11.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 = "cfrpart11.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
}
}
module "ec2_instance" {
source = "cisv130.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 = "hipaasecurity2003.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 = "nistcsfv11.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 = "nist80053rev4.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 = "pcidssv321.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
This control validates that every aws_ebs_volume resource and every inline ebs_block_device or root_block_device block within aws_instance or aws_launch_template has encrypted set to true. A volume passes when encrypted = true is explicitly configured, either using the default aws/ebs service key or a customer-managed KMS key via kms_key_id. It fails when encrypted is omitted (defaults to false unless the account-level default encryption setting overrides it) or explicitly set to false. To enforce this broadly without per-resource configuration, enable account-level EBS encryption via aws_ebs_encryption_by_default and optionally set aws_ebs_default_kms_key, but the control still evaluates the resulting encryption state on each attached volume.
Common pitfalls
Account default encryption does not retroactively encrypt existing volumes
Enabling
aws_ebs_encryption_by_defaultonly affects volumes created after the setting is turned on. Pre-existing unencrypted volumes remain unencrypted and will still fail this control. You must migrate them manually.Inline ebs_block_device blocks may silently omit encryption
When using
ebs_block_deviceinsideaws_instanceinstead of separateaws_ebs_volumeandaws_volume_attachmentresources,encrypteddefaults tofalseif not specified. This is easy to miss in large instance definitions with several block devices.Snapshots from unencrypted volumes create unencrypted volumes by default
Restore a volume from an unencrypted snapshot using
aws_ebs_volumewithsnapshot_idand you get an unencrypted volume unless you explicitly setencrypted = true. EBS re-encrypts the data during the copy operation, which requires a KMS key to be available.Multi-region KMS key availability
A
kms_key_idreferencing a KMS key from a different region will cause volume creation to fail outright. Each region needs its own KMS key or a multi-region replica key configured before the volume is provisioned.Launch templates may override encryption settings
An
aws_launch_templatewithblock_device_mappingscan setencrypted = falseon anebsblock, overriding the account default. Get this wrong and Auto Scaling groups using that template will produce non-compliant volumes at scale before anyone notices.
Audit evidence
An auditor expects Config rule evaluation results for the managed rule ENCRYPTED_VOLUMES, showing all attached EBS volumes as COMPLIANT. Supporting evidence includes an EC2 console screenshot of the volume list filtered to "in-use" state with the "Encrypted" column showing "Yes" for every entry. CloudTrail CreateVolume and AttachVolume events can confirm that encryption was specified at creation time.
For stronger evidence, provide the output of aws ec2 describe-volumes --filters Name=attachment.status,Values=attached --query "Volumes[?Encrypted==\false`]"returning an empty list, along with confirmation thataws ec2 get-ebs-encryption-by-defaultreturnsEbsEncryptionByDefault: true` for each region in scope.
Framework-specific interpretation
SOC 2: CC6 and CC7 both call for logical and technical safeguards protecting data at rest. Encrypted EBS volumes are a standard control activity auditors look for during walkthroughs of EC2-based workloads, and the absence of encryption is a straightforward finding.
PCI DSS v4.0: Requirement 3.4 says PAN must be rendered unreadable wherever it is stored. EBS encryption with proper key management is one way to satisfy this for cardholder data on block storage, though key access controls matter as much as the encryption itself.
HIPAA Omnibus Rule 2013: 45 CFR 164.312(a)(2)(iv) lists encryption as an addressable specification for ePHI at rest. Addressable doesn't mean optional: any EBS volume attached to an instance processing ePHI needs encryption enabled, or you must document an equivalent alternative safeguard and formally accept the risk. In practice, there is no reasonable equivalent when AWS provides native encryption at no additional cost.
GDPR: Article 32 requires appropriate technical measures to protect personal data, and encryption is explicitly cited as an example. Encrypting EBS volumes storing EU personal data reduces breach impact and can factor into the Article 34(3)(a) exemption from notification obligations when the data remains unintelligible to unauthorized parties.
NIST SP 800-53 Rev 5: SC-28 requires cryptographic mechanisms to prevent unauthorized disclosure and modification of data stored on digital media. EBS volume encryption with AES-256 through KMS satisfies this for block storage.
NIST Cybersecurity Framework v2.0: EBS encryption maps to PR.DS, the data security subcategory covering protection of data at rest. When physical media is compromised or a snapshot is copied without authorization, an encrypted volume is unreadable without access to the KMS key.
FedRAMP Moderate Baseline Rev 4: At the Moderate baseline, SC-28 applies to federal data stored on EBS volumes. EBS encryption through KMS uses FIPS 140-2 validated cryptographic modules, which is what FedRAMP needs for approved mechanisms under this control.
Related controls
Tool mappings
Use these identifiers to cross-reference this control across tools, reports, and evidence.
Compliance.tf Control:
ebs_attached_volume_encryption_enabledAWS Config Managed Rule:
ENCRYPTED_VOLUMESCheckov Checks:
CKV2_AWS_2,CKV_AWS_3Powerpipe Controls:
aws_compliance.control.ebs_attached_volume_encryption_enabled,aws_compliance.control.ebs_volume_encryption_at_rest_enabledProwler Check:
ec2_ebs_volume_encryptionAWS Security Hub Control:
EC2.3KICS Queries:
cc997676-481b-4e93-aa81-d19f8c5e9b12,e6b4b943-6883-47a9-9739-7ada9568f8caTrivy Checks:
AWS-0026,AWS-0131
Last reviewed: 2026-03-09