This check flags Pub/Sub topics that rely on Google-managed encryption instead of a customer-managed key (CMEK). Without CMEK you lose control over key rotation, access policy, and the ability to revoke access to message data. Fix it by creating a Cloud KMS key and attaching it to the topic with gcloud pubsub topics update --topic-encryption-key.
Pub/Sub is the messaging backbone for a lot of GCP architectures. It carries everything from audit logs and CI/CD events to PII-laden order streams and healthcare records. By default, Google encrypts all message data at rest with keys it manages on your behalf. That covers the basic compliance checkbox, but it leaves the keys entirely in Google's hands. The pubsub_nocmk check looks for topics that have not been configured with a customer-managed encryption key (CMEK).
What this check detects
The check inspects every Pub/Sub topic in your GCP projects and reports any topic where the kmsKeyName field is empty. An empty value means the topic is using Google's default encryption rather than a Cloud KMS key that you own and control.
In the API, a topic configured for CMEK looks like this:
{
"name": "projects/my-project/topics/orders",
"kmsKeyName": "projects/my-project/locations/us-central1/keyRings/pubsub-ring/cryptoKeys/pubsub-key"
}
A topic that triggers the finding has no kmsKeyName at all. Lensix marks it as not encrypted with CMEK.
Note: CMEK does not change whether your data is encrypted. Pub/Sub always encrypts messages at rest. CMEK changes who controls the key and gives you a kill switch: disable or destroy the key and the data becomes unreadable, including to Google.
Why it matters
Default Google-managed encryption is fine for plenty of low-sensitivity workloads. The problem shows up when the data flowing through a topic carries regulatory weight or business risk, and you have no way to prove control over the keys protecting it.
You can't revoke access you don't control
Imagine a breach scenario where an attacker gains read access to a subscription, or a snapshot of message data ends up somewhere it shouldn't. With Google-managed keys, your incident response options are limited. With CMEK, you can disable the key version and instantly render that data unreadable while you investigate. That kill switch is one of the main reasons CMEK exists.
Compliance frameworks expect customer-controlled keys
Auditors for PCI DSS, HIPAA, FedRAMP, and many internal data-handling policies increasingly want evidence that you control the encryption keys for sensitive data and that those keys rotate on a defined schedule. "Google handles it" is not an answer that survives a serious audit for regulated workloads.
Rotation and lifecycle policy
With CMEK you set the rotation period, you decide when to disable old key versions, and you get Cloud KMS audit logs showing every encrypt and decrypt operation tied to the key. Google-managed keys give you none of that visibility or control.
Warning: Cloud KMS bills per active key version and per cryptographic operation. A topic with very high message throughput generates a lot of decrypt calls. The cost is usually small, but model it before flipping CMEK on across hundreds of high-volume topics.
How to fix it
Remediation has three steps: create a KMS key, grant the Pub/Sub service account permission to use it, then attach the key to the topic.
Step 1: Create a key ring and key
Pick a location that matches where your topic stores message data. For regional message storage, use a regional key. For multi-region or global topics, you will generally use a key in a matching multi-region.
gcloud kms keyrings create pubsub-ring \
--location=us-central1
gcloud kms keys create pubsub-key \
--location=us-central1 \
--keyring=pubsub-ring \
--purpose=encryption \
--rotation-period=90d \
--next-rotation-time=$(date -u -d "+90 days" +%Y-%m-%dT%H:%M:%SZ)
Step 2: Grant the Pub/Sub service agent access to the key
Each project has a Google-managed Pub/Sub service account that performs the encryption and decryption. It needs the roles/cloudkms.cryptoKeyEncrypterDecrypter role on the key. The service account follows a fixed format.
PROJECT_NUMBER=$(gcloud projects describe my-project --format="value(projectNumber)")
PUBSUB_SA="service-${PROJECT_NUMBER}@gcp-sa-pubsub.iam.gserviceaccount.com"
gcloud kms keys add-iam-policy-binding pubsub-key \
--location=us-central1 \
--keyring=pubsub-ring \
--member="serviceAccount:${PUBSUB_SA}" \
--role=roles/cloudkms.cryptoKeyEncrypterDecrypter
Note: If the service agent does not exist yet, trigger its creation once with gcloud beta services identity create --service=pubsub.googleapis.com --project=my-project, then re-run the IAM binding.
Step 3: Attach the key to the topic
For an existing topic:
gcloud pubsub topics update orders \
--topic-encryption-key=projects/my-project/locations/us-central1/keyRings/pubsub-ring/cryptoKeys/pubsub-key
Or set it at creation time for new topics:
gcloud pubsub topics create orders \
--topic-encryption-key=projects/my-project/locations/us-central1/keyRings/pubsub-ring/cryptoKeys/pubsub-key
Danger: Once a topic uses a CMEK, destroying or disabling the key version makes both new and previously published messages undecryptable. Do not destroy key versions a topic depends on unless you intend to make that data permanently unreadable. Keep an eye on rotation and key state before any cleanup.
Terraform example
If you manage infrastructure as code, set kms_key_name on the topic resource and wire up the IAM binding so applies don't fail on a permissions race:
resource "google_kms_key_ring" "pubsub" {
name = "pubsub-ring"
location = "us-central1"
}
resource "google_kms_crypto_key" "pubsub" {
name = "pubsub-key"
key_ring = google_kms_key_ring.pubsub.id
rotation_period = "7776000s" # 90 days
}
data "google_project" "current" {}
resource "google_kms_crypto_key_iam_member" "pubsub_sa" {
crypto_key_id = google_kms_crypto_key.pubsub.id
role = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
member = "serviceAccount:service-${data.google_project.current.number}@gcp-sa-pubsub.iam.gserviceaccount.com"
}
resource "google_pubsub_topic" "orders" {
name = "orders"
kms_key_name = google_kms_crypto_key.pubsub.id
depends_on = [google_kms_crypto_key_iam_member.pubsub_sa]
}
Tip: Wrap the key ring, key, IAM binding, and topic in a reusable module. Then every team that creates a topic gets CMEK by default without having to remember the service account format or the rotation settings.
How to prevent it from happening again
Fixing existing topics is the easy part. The harder part is making sure the next topic someone spins up doesn't skip CMEK. A few layers help here.
Organization Policy constraint
GCP ships a built-in constraint that denies creation of resources without CMEK. Apply constraints/gcp.restrictNonCmekServices and list Pub/Sub so the API rejects any topic created without a key:
cat > cmek-policy.yaml <<'EOF'
name: organizations/123456789/policies/gcp.restrictNonCmekServices
spec:
rules:
- values:
allowedValues:
- "pubsub.googleapis.com"
EOF
gcloud org-policies set-policy cmek-policy.yaml
Pair it with constraints/gcp.restrictCmekCryptoKeyProjects if you want to force keys to come from an approved set of projects rather than anywhere in the org.
CI/CD policy-as-code gate
Catch missing keys before they reach production by scanning Terraform plans in your pipeline. A simple Open Policy Agent / Conftest rule flags any Pub/Sub topic without a key set:
package main
deny[msg] {
resource := input.resource_changes[_]
resource.type == "google_pubsub_topic"
not resource.change.after.kms_key_name
msg := sprintf("Pub/Sub topic '%s' must set kms_key_name (CMEK required)", [resource.address])
}
Run it as a required check on pull requests so a topic without CMEK never merges.
Continuous detection
Org policies and CI gates close the front door, but they don't catch topics created before the policy existed or resources made out of band. Schedule the pubsub_nocmk check in Lensix so any non-compliant topic surfaces continuously, not just at audit time.
Best practices
- Use a dedicated key per sensitivity tier, not one key for everything. Grouping topics by data classification means you can revoke or rotate keys for one tier without disrupting unrelated workloads.
- Set automatic rotation (30 to 90 days is common) so you aren't relying on someone remembering to rotate manually.
- Match key location to topic message storage location to avoid latency and cross-region surprises, and to satisfy data residency requirements.
- Lock down the key, not just the topic. CMEK only protects you if access to the key itself is tightly scoped. Audit who holds
cloudkms.cryptoKeyEncrypterDecrypterandcloudkms.adminroles. - Enable Cloud KMS audit logs so every decrypt operation against the key is recorded. This is what gives you the forensic trail an investigation needs.
- Don't forget subscriptions and snapshots. CMEK on the topic covers message storage, but review your retention settings and snapshot lifecycle as part of the same data-protection review.
CMEK is not about whether your data is encrypted. It is about whether you can prove you control the key and can pull it back when something goes wrong. For sensitive Pub/Sub data, that control is the whole point.
Run the check, attach keys to the topics that need them, and put an org policy plus a CI gate in place so the problem stays fixed.

