This check flags SQS queues encrypted with the default AWS-managed KMS key instead of a customer-managed key (CMK). Switch to a CMK so you control the key policy, rotation, and audit trail. Fix it by setting KmsMasterKeyId on the queue to your own KMS key.
Server-side encryption on SQS is easy to enable, and most teams turn it on without thinking twice. The catch is which key does the encrypting. When you flip on SSE-SQS or accept the default, AWS quietly uses an AWS-managed KMS key (alias/aws/sqs). That ticks the "encryption enabled" box, but it hands key governance over to AWS instead of you.
The SQS Queue Not Using CMK check (sqs_nocmk) catches exactly this situation: a queue is encrypted, but with an AWS-managed key rather than a customer-managed key you own and control.
What this check detects
Lensix inspects each SQS queue's encryption configuration and looks at the KMS key in use. The check fails when one of two things is true:
- The queue uses the AWS-managed SQS key (
alias/aws/sqs), or - The queue uses SSE-SQS (Amazon's owned key) rather than KMS with a customer-managed key.
In API terms, a passing queue has a KmsMasterKeyId attribute pointing at a CMK in your account (or a shared CMK you manage). A failing queue either has no KmsMasterKeyId, has it set to alias/aws/sqs, or relies solely on SSE-SQS.
Note: SQS supports three encryption modes. SSE-SQS uses an Amazon-owned key you never see. SSE-KMS with the AWS-managed key uses alias/aws/sqs, which lives in your account but has a key policy AWS controls. SSE-KMS with a customer-managed key uses a CMK you create and govern. Only the last one passes this check.
Why it matters
"It's encrypted either way, so who cares which key?" is the natural objection. Here is what changes when you own the key.
You control the key policy
With a CMK, you decide who can use the key to decrypt messages through the key policy and IAM. That gives you a second, independent layer of access control on top of SQS resource policies. With the AWS-managed key, the key policy is fixed by AWS and grants decrypt rights broadly to principals in your account that already have SQS permissions. You cannot tighten it.
This matters in a real breach. If an attacker compromises a role that can read from a queue, an AWS-managed key does nothing to slow them down. A CMK lets you scope kms:Decrypt to only the specific roles that legitimately consume the queue, so a stolen credential outside that set is useless against the message contents.
You get a clean audit trail
Every use of a CMK shows up in CloudTrail as a kms:Decrypt or kms:GenerateDataKey event tied to your key. You can alert on unusual decrypt patterns, build detections, and prove during an audit exactly which principals accessed encrypted data. AWS-managed keys produce CloudTrail events too, but you cannot attach your own monitoring or restrict usage at the key level.
You control rotation and lifecycle
With a CMK you choose the rotation schedule, can disable the key to instantly cut off all access during an incident, and can manage the full lifecycle. Disabling a key is one of the fastest ways to contain a leak: it severs decrypt access everywhere at once without touching individual IAM policies.
Compliance frameworks expect it
Many controls in PCI DSS, HIPAA-aligned programs, SOC 2, and internal data-classification policies require that encryption keys for sensitive data be customer-managed, with documented rotation and access logging. SQS queues often carry order details, PII, or internal events. An AWS-managed key will fail those control mappings.
Warning: CMKs cost money. Each customer-managed KMS key is roughly $1 per month, plus per-request charges for Decrypt and GenerateDataKey calls. High-throughput queues can generate a lot of KMS requests. Use the data key reuse period (covered below) to keep those costs in check.
How to fix it
The fix is to point the queue at a customer-managed KMS key. You can reuse one CMK across multiple queues or create a dedicated key per workload.
Step 1: Create a CMK (if you don't have one)
aws kms create-key \
--description "CMK for SQS queue encryption" \
--key-usage ENCRYPT_DECRYPT \
--tags TagKey=purpose,TagValue=sqs-encryption
# Give it a friendly alias
aws kms create-alias \
--alias-name alias/sqs-orders \
--target-key-id
Step 2: Attach a scoped key policy
This is where the real value lives. Grant decrypt only to the roles that actually consume the queue, and allow the SQS service to use the key on behalf of producers.
{
"Version": "2012-10-17",
"Id": "sqs-cmk-policy",
"Statement": [
{
"Sid": "AllowKeyAdministration",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::111122223333:role/kms-admins" },
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "AllowConsumersToDecrypt",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111122223333:role/order-processor"
},
"Action": [
"kms:Decrypt",
"kms:GenerateDataKey"
],
"Resource": "*"
},
{
"Sid": "AllowSQSService",
"Effect": "Allow",
"Principal": { "Service": "sqs.amazonaws.com" },
"Action": [
"kms:GenerateDataKey",
"kms:Decrypt"
],
"Resource": "*"
}
]
}
Step 3: Apply the CMK to the queue
Danger: Changing encryption on a live queue affects every producer and consumer. If a consumer role lacks kms:Decrypt on the new CMK, it will stop receiving messages and they may pile up or hit the redrive policy. Confirm all consumer and producer roles are granted in the key policy before you switch, and test in staging first.
aws sqs set-queue-attributes \
--queue-url https://sqs.us-east-1.amazonaws.com/111122223333/orders \
--attributes '{
"KmsMasterKeyId": "alias/sqs-orders",
"KmsDataKeyReusePeriodSeconds": "300"
}'
The KmsDataKeyReusePeriodSeconds setting (between 60 and 86,400 seconds) controls how long SQS caches a data key before calling KMS again. A higher value reduces KMS request costs at the price of a slightly larger blast radius if a data key is ever exposed. 300 seconds is a reasonable default for most workloads.
Console steps
- Open the Amazon SQS console and select the queue.
- Choose Edit.
- Under Encryption, keep server-side encryption enabled and select AWS Key Management Service key (SSE-KMS).
- Under the key dropdown, choose your CMK (for example
alias/sqs-orders) instead ofalias/aws/sqs. - Set the Data key reuse period.
- Save.
Terraform
resource "aws_kms_key" "sqs" {
description = "CMK for SQS queue encryption"
deletion_window_in_days = 14
enable_key_rotation = true
}
resource "aws_kms_alias" "sqs" {
name = "alias/sqs-orders"
target_key_id = aws_kms_key.sqs.key_id
}
resource "aws_sqs_queue" "orders" {
name = "orders"
kms_master_key_id = aws_kms_alias.sqs.name
kms_data_key_reuse_period_seconds = 300
}
Tip: Set enable_key_rotation = true on the CMK. AWS will rotate the backing key material annually with no application changes needed, and it keeps you aligned with most compliance baselines for free.
How to prevent it from happening again
One-off fixes drift back. Bake the requirement into the places where queues get created.
Catch it in CI with policy-as-code
If you provision SQS through Terraform, fail the pipeline when a queue has no CMK. Here is an Open Policy Agent (Conftest) rule against a Terraform plan:
package sqs
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_sqs_queue"
key := resource.change.after.kms_master_key_id
not is_cmk(key)
msg := sprintf("SQS queue '%s' must use a customer-managed KMS key", [resource.address])
}
is_cmk(key) {
key != null
key != "alias/aws/sqs"
}
For CloudFormation shops, a cfn-guard rule does the same job:
let queues = Resources.*[ Type == 'AWS::SQS::Queue' ]
rule sqs_requires_cmk when %queues !empty {
%queues.Properties.KmsMasterKeyId exists
%queues.Properties.KmsMasterKeyId != "alias/aws/sqs"
}
Enforce it organization-wide with an SCP
A Service Control Policy can deny queue creation unless a KMS key is specified. This stops the AWS-managed and SSE-SQS defaults at the door across every account.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenySQSWithoutCMK",
"Effect": "Deny",
"Action": [
"sqs:CreateQueue",
"sqs:SetQueueAttributes"
],
"Resource": "*",
"Condition": {
"Null": { "sqs:RequestKmsKeyId": "true" }
}
}
]
}
Warning: Test SCPs in a non-production OU before rolling them out. A condition that is too strict can block legitimate automation, including some AWS services that create queues on your behalf. Validate against your real workloads first.
Continuous monitoring
Run the sqs_nocmk check on a schedule in Lensix so any new or modified queue that slips past CI is flagged within minutes. Pair it with an alert routed to the team that owns the workload, not a shared inbox nobody reads.
Best practices
- One CMK per data domain. Group queues that carry the same class of data under a shared CMK rather than spinning up a key per queue. It keeps key policies manageable and controls cost.
- Scope decrypt to named roles. Avoid
"Principal": "*"or account-wide grants in the key policy. List the specific consumer roles. This is the entire point of using a CMK. - Enable automatic key rotation. Annual rotation is one toggle and satisfies most audit requirements.
- Tune the data key reuse period. Balance KMS cost against blast radius. For high-throughput queues, a longer period sharply reduces request charges.
- Encrypt the dead-letter queue too. Failed messages often contain the most sensitive payloads. Apply the same CMK to your DLQ, since this check is easy to satisfy on the main queue while leaving the DLQ on a default key.
- Watch CloudTrail for the key. Set up a metric filter or detection on unexpected
kms:Decryptcalls against the queue's CMK to spot abuse early.
Switching from an AWS-managed key to a CMK is a small change with an outsized payoff: real control over who can read your messages, a usable audit trail, and an instant kill switch during an incident. Make it the default for every new queue and the cleanup work stops piling up.

