This check flags Cloud SQL instances that rely on Google's default encryption instead of a customer-managed encryption key (CMEK). Without CMEK you cannot control the key lifecycle, rotation, or revocation. Fix it by creating a Cloud KMS key, granting the Cloud SQL service account access, and provisioning a new instance with that key.
Every Cloud SQL instance is encrypted at rest by default. That is a good baseline, but the keys are owned and rotated entirely by Google. For workloads under PCI DSS, HIPAA, or internal data-governance rules, you often need to prove you control the encryption key, not just trust that one exists. The sql_nocmk check looks for any Cloud SQL instance that has not been bound to a customer-managed key in Cloud KMS.
What this check detects
Lensix inspects each Cloud SQL instance's diskEncryptionConfiguration field. When an instance is created with CMEK, this field references a KMS key resource name. If the field is empty, the instance is using Google-managed default encryption, and the check is marked as failing.
In practice the check fires on:
- Instances created without specifying a
--disk-encryption-key - Older instances provisioned before your team adopted CMEK
- Read replicas or clones that inherited the default encryption of a non-CMEK source
Note: CMEK does not replace Google's encryption. The data is still encrypted with a Google-managed Data Encryption Key (DEK), but that DEK is itself wrapped by your Cloud KMS key (the Key Encryption Key). Revoke or disable your KMS key and the DEK can no longer be unwrapped, which renders the data inaccessible.
Why it matters
Default encryption protects you against one specific threat: someone walking out of a Google data center with a physical disk. It does nothing for the threats most teams actually worry about.
You cannot revoke access in an incident
Imagine credentials to your project are compromised and an attacker is exfiltrating data from a production database. With Google-managed keys, you have no kill switch at the encryption layer. With CMEK, disabling the KMS key version immediately cuts off the instance's ability to decrypt data, buying you time to contain the breach.
Compliance frameworks expect key ownership
Auditors for PCI DSS, HIPAA, FedRAMP, and many financial regulations increasingly ask a direct question: who controls the encryption keys for cardholder or patient data? "Google does" is rarely an acceptable answer. CMEK lets you point at a key you created, with a rotation schedule you defined and an audit trail of every decrypt operation in Cloud Audit Logs.
Centralized key lifecycle and rotation
With CMEK you set rotation periods, you decide when to disable old key versions, and you can store keys in a specific region to satisfy data-residency requirements. None of that is possible with the default scheme.
Warning: You cannot add CMEK to an existing Cloud SQL instance in place. Encryption configuration is set at creation time. Converting an existing instance means creating a new CMEK-enabled instance and migrating data, so plan for a maintenance window.
How to fix it
The fix has three stages: create a KMS key, give the Cloud SQL service agent permission to use it, then provision the instance with the key.
1. Create a key ring and key
Keep the key in the same region as the Cloud SQL instance. Cross-region CMEK is not supported for Cloud SQL.
gcloud kms keyrings create sql-keyring \
--location=us-central1
gcloud kms keys create sql-cmek \
--location=us-central1 \
--keyring=sql-keyring \
--purpose=encryption \
--rotation-period=90d \
--next-rotation-time=$(date -u -d '+90 days' +%Y-%m-%dT%H:%M:%SZ)
2. Grant the Cloud SQL service account access to the key
Cloud SQL uses a per-project service agent to perform encryption operations. First find or create it, then grant it the encrypter/decrypter role on the key.
# Get the Cloud SQL service account for your project
SA_EMAIL=$(gcloud sql instances create-instance --quiet 2>/dev/null; \
gcloud beta services identity create \
--service=sqladmin.googleapis.com \
--project=YOUR_PROJECT_ID \
--format='value(email)')
# Grant it permission to use the key
gcloud kms keys add-iam-policy-binding sql-cmek \
--location=us-central1 \
--keyring=sql-keyring \
--member="serviceAccount:${SA_EMAIL}" \
--role=roles/cloudkms.cryptoKeyEncrypterDecrypter
Note: The service agent email follows the pattern [email protected]. If gcloud beta services identity create returns nothing, that agent may not exist yet, which is why the command above triggers its creation.
3. Create the instance with the key
gcloud sql instances create prod-db \
--database-version=POSTGRES_15 \
--region=us-central1 \
--tier=db-custom-2-7680 \
--disk-encryption-key=projects/YOUR_PROJECT_ID/locations/us-central1/keyRings/sql-keyring/cryptoKeys/sql-cmek
Migrating an existing instance
Since you cannot retrofit CMEK, the path for an existing database is to create a CMEK-enabled instance and move the data into it.
- Create the new CMEK instance using the steps above.
- Export the existing database to a Cloud Storage bucket, or use Database Migration Service for minimal downtime.
- Import into the new instance and verify row counts and application connectivity.
- Cut over your connection strings, then decommission the old instance.
# Export from the old instance
gcloud sql export sql old-db gs://my-export-bucket/dump.sql \
--database=appdb
# Import into the new CMEK instance
gcloud sql import sql prod-db gs://my-export-bucket/dump.sql \
--database=appdb
Danger: Do not delete the source instance until you have confirmed the new instance is serving production traffic correctly and you have a verified backup. Deleting a Cloud SQL instance also removes its automated backups unless you have exported them elsewhere.
Terraform example
If you manage infrastructure as code, bind the key at the resource level so every new instance is CMEK by default.
resource "google_kms_key_ring" "sql" {
name = "sql-keyring"
location = "us-central1"
}
resource "google_kms_crypto_key" "sql" {
name = "sql-cmek"
key_ring = google_kms_key_ring.sql.id
rotation_period = "7776000s" # 90 days
}
resource "google_project_service_identity" "sql_sa" {
provider = google-beta
service = "sqladmin.googleapis.com"
}
resource "google_kms_crypto_key_iam_member" "sql" {
crypto_key_id = google_kms_crypto_key.sql.id
role = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
member = "serviceAccount:${google_project_service_identity.sql_sa.email}"
}
resource "google_sql_database_instance" "prod" {
name = "prod-db"
database_version = "POSTGRES_15"
region = "us-central1"
encryption_key_name = google_kms_crypto_key.sql.id
settings {
tier = "db-custom-2-7680"
}
depends_on = [google_kms_crypto_key_iam_member.sql]
}
Tip: Wrap the key ring, key, and IAM binding into a reusable Terraform module. Then a single encryption_key_name input on your database module makes CMEK the path of least resistance for every team, rather than something engineers have to remember to add.
How to prevent it from happening again
Manual fixes drift. The instances you remediate today will be joined by new ones next quarter unless you enforce CMEK at provisioning time.
Organization policy constraint
GCP ships a built-in constraint that blocks creation of resources without CMEK. Apply it at the org or folder level and Cloud SQL will reject any instance that does not specify a key.
gcloud resource-manager org-policies enable-enforce \
constraints/gcp.restrictNonCmekServices \
--organization=YOUR_ORG_ID
# Then list the services that must use CMEK
gcloud resource-manager org-policies set-policy cmek-policy.yaml \
--organization=YOUR_ORG_ID
Where cmek-policy.yaml includes sqladmin.googleapis.com in the allowed values list. You can also restrict which key projects are permitted with constraints/gcp.restrictCmekCryptoKeyProjects.
Policy-as-code in CI/CD
Catch the problem before it ever reaches GCP by scanning Terraform plans. A simple Conftest/OPA rule rejects any Cloud SQL resource missing an encryption key:
package main
deny[msg] {
resource := input.resource.google_sql_database_instance[name]
not resource.encryption_key_name
msg := sprintf("Cloud SQL instance '%s' must set encryption_key_name (CMEK)", [name])
}
Run it as a required check on every pull request so a missing key fails the build, not the audit.
Tip: Pair the org policy with continuous monitoring in Lensix. The org policy stops new violations, while the sql_nocmk check surfaces legacy instances and any resources created through paths the policy does not cover, such as imported state or manual console actions during an incident.
Best practices
- Rotate keys automatically. Set a rotation period of 90 days or less. Cloud SQL re-wraps the DEK with the new key version on the next maintenance event without downtime.
- Separate duties with a dedicated key project. Store KMS keys in a project that only your security team can administer, so application teams can use keys but not delete or disable them.
- Never grant broad KMS admin roles to service accounts. The Cloud SQL agent needs only
cryptoKeyEncrypterDecrypter, nothing more. - Monitor key usage. Enable Cloud Audit Logs for Cloud KMS and alert on unexpected
Decryptvolume or any attempt to destroy a key version. - Protect against accidental key destruction. Cloud KMS enforces a 24-hour delay before a key version is destroyed. Treat that window as a safety net, not a plan, and keep verified database exports.
- Match key region to instance region. Cross-region CMEK is unsupported and will cause instance creation to fail.
CMEK is one of the cheapest controls you can add to a database. A KMS key costs a fraction of a cent per month plus a small fee per operation, and in return you get a revocation switch, a rotation schedule, and a clean answer for every auditor who asks who holds the keys.
Adopt CMEK as the default for new Cloud SQL instances, enforce it with org policy and CI gates, and let Lensix watch for the stragglers. That combination turns key ownership from a recurring audit headache into a property of your platform.

