Skip to content

SQS queues should be encrypted with KMS CMK

SQS queues default to SSE-SQS encryption using AWS-owned keys, which means AWS controls the entire key lifecycle. A KMS CMK lets you define your own rotation schedule, restrict access through key policies, and audit every encrypt/decrypt call via CloudTrail. That distinction matters when your data classification requires customer-controlled key material or when regulations mandate that encryption keys stay under your organizational control.

Without a CMK, you cannot revoke access to queue data by disabling or deleting the key, and you lose the ability to enforce separation of duties between the team managing queues and the team managing encryption keys.

Retrofit consideration

Changing an existing SQS queue from SSE-SQS to KMS CMK is an in-place update. Producers and consumers must have kms:GenerateDataKey and kms:Decrypt on the new CMK before the change lands, or they will start throwing AccessDeniedException immediately.

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 "sqs" {
  source  = "nistcsf.compliance.tf/terraform-aws-modules/sqs/aws"
  version = ">=5.0.0"

  fifo_queue = false
  name       = "abc123"
}

If you use terraform-aws-modules/sqs/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 "sqs" {
  source  = "terraform-aws-modules/sqs/aws"
  version = ">=5.0.0"

  fifo_queue = false
  name       = "abc123"
}

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

resource "aws_sqs_queue" "this" {
  name           = "pofix-abc123"
  redrive_policy = jsonencode({ deadLetterTargetArn = "arn:aws:sqs:us-east-1:123456789012:example-queue", maxReceiveCount = 5 })

  kms_master_key_id = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
}

What this control checks

The aws_sqs_queue resource must have kms_master_key_id set to the ARN or alias of a customer-managed KMS key. A queue passes when kms_master_key_id references a valid CMK (not the AWS-managed alias/aws/sqs key). It fails if kms_master_key_id is omitted, empty, or points to the AWS-managed SQS key. Setting sqs_managed_sse_enabled = true without kms_master_key_id also fails: that enables SSE-SQS, not KMS CMK encryption. Define the CMK as a separate aws_kms_key resource and reference its ARN. Optionally, set kms_data_key_reuse_period_seconds (default 300, max 86400) to control how long SQS reuses a data key before calling KMS again, which directly affects KMS API costs.

Common pitfalls

  • AWS-managed key does not satisfy CMK requirement

    Setting kms_master_key_id to alias/aws/sqs uses the AWS-managed key. You cannot disable it, attach a custom key policy, or control its rotation schedule. Most CMK controls treat this as a failure. Use an aws_kms_key resource you own.

  • SSE-SQS is not KMS CMK encryption

    SQS queues encrypt by default with SSE-SQS. Setting sqs_managed_sse_enabled = true opts into that mode explicitly but does not satisfy a CMK requirement. The two arguments are mutually exclusive: setting both sqs_managed_sse_enabled and kms_master_key_id causes a conflict error.

  • Missing KMS permissions on producers and consumers

    Grant permissions before you apply the CMK change. Every IAM principal calling sqs:SendMessage needs kms:GenerateDataKey on the key; every principal calling sqs:ReceiveMessage needs kms:Decrypt. Lambda triggers, SNS subscriptions, and S3 event notifications are common offenders that fail with AccessDeniedException if you miss them.

  • KMS key account and Region constraints

    The CMK must be in the same account and Region as the queue. SetQueueAttributes can succeed even when key policy or IAM permissions are incomplete, so verify message operations work after the change, not just the Terraform apply.

  • Low kms_data_key_reuse_period_seconds increases cost

    Get kms_data_key_reuse_period_seconds wrong on a high-throughput queue and the bill will surprise you. The default is 300 seconds; drop it below 60 and you can generate thousands of KMS API calls per minute at $0.03 per 10,000 requests.

Audit evidence

Auditors expect AWS Config rule results (such as sqs-queue-encrypted or a custom Config rule) showing all queues as compliant, with each queue's KmsMasterKeyId attribute populated with a customer-managed key ARN. Console screenshots of the SQS encryption settings page showing the key ID and key type are supporting evidence. CloudTrail logs for sqs:SetQueueAttributes confirm when encryption was enabled or changed; kms:Decrypt and kms:GenerateDataKey events tied to the SQS service principal demonstrate active CMK usage.

For continuous compliance, a Security Hub or third-party scanner report showing the control passing across all accounts and regions fills out the evidence package. The auditor may also ask for the KMS key policy to verify access is appropriately scoped.

Framework-specific interpretation

NIST Cybersecurity Framework v2.0: PR.DS-01 asks for data-at-rest protection; PR.DS controls broadly cover encryption and key management. A customer-managed KMS key on SQS queues keeps cryptographic control inside your organization rather than delegating it to AWS, which is what the Govern function's requirement to align encryption policy with organizational risk appetite is getting at.

Tool mappings

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

  • Compliance.tf Control: sqs_queue_encrypted_with_kms_cmk

  • Checkov Check: CKV2_AWS_73

  • Powerpipe Control: aws_compliance.control.sqs_queue_encrypted_with_kms_cmk

  • Prowler Check: sqs_queues_server_side_encryption_enabled

  • AWS Security Hub Control: SQS.1

  • Trivy Checks: AWS-0096, AWS-0135

Last reviewed: 2026-03-09