GCP Lesson 36 of 98

Google Cloud KMS & Secret Manager, In Depth: Keys, CMEK, Envelope Encryption & Secrets

Every byte you store in Google Cloud is already encrypted at rest. You did nothing to make that happen — Compute Engine disks, Cloud Storage objects, BigQuery tables and Cloud SQL volumes are all encrypted with keys Google generates, rotates and guards for you, and you never see them. So a fair first question is: if encryption is automatic and free, why does Google ship two whole products — Cloud KMS and Secret Manager — and why does every serious security review ask whether you use them?

The answer is control. “Encrypted by default” protects you against someone walking out of a data centre with a disk. It does not answer the questions auditors, regulators and your own incident-response team actually ask: Who can revoke access to the plaintext, and how quickly? Where does the key material physically live? Can you prove a key was destroyed? Where do your application’s passwords, API keys and certificates live, who can read them, and how often do they change? Default encryption gives Google the lever; Cloud KMS hands the lever to you. And the credentials your code needs at runtime — the database password, the third-party API token — do not belong in environment variables or a config.json in your repo; they belong in Secret Manager, versioned, access-controlled and rotatable.

This lesson is the exhaustive, beginner-accessible map of both services. By the end you will understand the full Cloud KMS hierarchy (key rings, keys, versions), every key purpose (symmetric, asymmetric sign, asymmetric decrypt, MAC), rotation, how Customer-Managed Encryption Keys (CMEK) wire into GCP services, the envelope encryption mechanics underneath all of it, the protection levels (SOFTWARE, HSM, EXTERNAL, EXTERNAL_VPC), IAM on keys, Cloud HSM and Autokey; and on the Secret Manager side, secrets and versions, access, rotation, the replication models, IAM and CMEK. It maps to the Associate Cloud Engineer (ACE) and Professional Cloud Security Engineer (PCSE) exams and gives a working architect everything needed to operate both services in production.

Learning objectives

By the end of this lesson you can:

Prerequisites & where this fits

You need a Google Cloud project with billing enabled (the $300 free trial is plenty for the lab), the gcloud CLI installed and authenticated, and a working grasp of IAM — principals, roles and policy bindings — because access to every key and every secret is governed by IAM. If IAM is hazy, read Google Cloud IAM Fundamentals: Roles, Service Accounts & the Allow Policy first. This is the Security lesson of the Google Cloud Zero-to-Hero course, sitting in the Intermediate tier after Pub/Sub and before the Operations Suite. Two companion lessons go deeper than this one: Cloud KMS in Depth: CMEK, Envelope Encryption, Cloud HSM and External Key Manager and Secret Manager Rotation Pipelines with Cloud Functions, IAM and CMEK — this lesson is the complete on-ramp to both.

Core concept: default encryption and the three key-management models

Start with the baseline, because it is the thing you are choosing to change. All customer data at rest in Google Cloud is encrypted by default, with no action and no extra cost. Under the hood Google uses envelope encryption (we will pull this apart in full below): your data is encrypted with a data encryption key (DEK), and that DEK is itself encrypted by a key encryption key (KEK) held in Google’s internal KMS. Encryption uses AES-256 (or AES-128 for some legacy data) in GCM or CBC/CTR mode depending on the service. You inherit strong cryptography for free.

What you are deciding when you reach for Cloud KMS is who controls the top key in that chain. There are three models, and naming them precisely is the foundation of every conversation that follows:

Model Who generates & holds the key Who can revoke access to plaintext When to choose GCP term
Google-managed (default) Google, entirely Only Google (you have no lever) Default; fine for most data with no specific compliance requirement Google default encryption
Customer-Managed Encryption Keys (CMEK) You, in Cloud KMS You — disable/destroy the key and the data becomes unreadable Compliance, key control, the ability to revoke, audit trail, separation of duties CMEK
Customer-Supplied Encryption Keys (CSEK) You, outside GCP — you pass the raw key on each request You, but you also bear the full operational burden Rare; only when you cannot let key material persist in Google at all (limited to a few services such as Cloud Storage and Compute Engine) CSEK

The practical progression is almost always default → CMEK. CMEK is the sweet spot: Google still does the heavy lifting (high availability, durability of the key, the actual crypto operations), but you own the key object, decide its rotation, control who may use it through IAM, get an audit log of every use, and — the headline capability — can make a petabyte of data instantly unreadable by disabling a single key version. CSEK is a niche tool with serious downsides (lose the key and your data is gone forever; Google cannot help) and most architectures never need it. This lesson is overwhelmingly about CMEK via Cloud KMS.

Core concept: the Cloud KMS hierarchy

Cloud KMS organises keys into a strict four-level hierarchy. Getting this vocabulary exact saves you from IAM, location and rotation mistakes later:

Level Resource type What it is Key properties
Project The billing and IAM root Keys live under a project; you can centralise keys in a dedicated security project
Key ring KeyRing A location-bound logical grouping of keys and an IAM boundary Immutable location; cannot be deleted or renamed; free to create
Key CryptoKey A named key with a purpose and rotation policy The thing you grant IAM on and reference from services; cannot change purpose after creation
Key version CryptoKeyVersion The actual cryptographic material Created by rotation; has a lifecycle and state; the unit you disable/destroy

A few consequences matter enormously in practice:

Locations: regional, multi-regional and global

KMS keys exist in a location, which determines where the cryptographic operations happen and where the key material is stored. The three flavours mirror the rest of GCP:

Location type Example Use it for Latency / availability note
Regional us-central1, europe-west2, asia-south1 CMEK for regional resources; data-residency requirements Lowest latency to same-region resources; survives zone failure within the region
Multi-regional us, eu, asia CMEK for multi-region buckets/datasets; higher availability Spread across a continent; matches multi-region storage
Global global Application-level encryption not tied to a region Convenient, but not ideal for data-residency-constrained workloads

The cardinal rule: the key’s location must be compatible with the resource it protects. GCP will reject a CMEK assignment where the key location and the resource location do not match, and this is the single most common first-time CMEK error.

Core concept: keys, purposes and algorithms

A CryptoKey has a purpose chosen at creation, and the purpose constrains which algorithms and operations are available. There are four purposes:

Purpose (--purpose) Operations Typical use CMEK eligible?
encryption (symmetric, ENCRYPT_DECRYPT) Encrypt / decrypt with the same key CMEK, envelope encryption, app-level secret wrapping Yes — this is the CMEK purpose
asymmetric-encryption (ASYMMETRIC_DECRYPT) Others encrypt with the public key; you decrypt with the private Receiving data encrypted to your public key No
asymmetric-signing (ASYMMETRIC_SIGN) Sign with the private key; anyone verifies with the public Code signing, document signing, JWT signing No
mac (MAC) Produce/verify a message authentication code (HMAC) Integrity/authenticity tokens, signed cookies No

Symmetric (encryption) is the workhorse — it is the only purpose that CMEK uses, and it is what you reach for unless you specifically need public-key cryptography. For symmetric keys the algorithm is the Google-managed GOOGLE_SYMMETRIC_ENCRYPTION (AES-256-GCM); you do not choose it. For asymmetric and MAC keys you do choose the algorithm and curve/size:

Purpose Representative algorithm choices
Asymmetric signing RSA_SIGN_PSS_2048/3072/4096_SHA256, RSA_SIGN_PKCS1_*, EC_SIGN_P256_SHA256, EC_SIGN_P384_SHA384, EC_SIGN_SECP256K1_SHA256 (HSM only)
Asymmetric encryption RSA_DECRYPT_OAEP_2048/3072/4096_SHA256 (and SHA512 variants)
MAC HMAC_SHA256 (also SHA1/224/384/512)

A subtle but exam-worthy point: asymmetric keys do not auto-rotate. Because the public half is distributed to verifiers/encryptors, Google cannot silently swap the version — you manage versions explicitly. Only symmetric keys support automatic rotation.

Core concept: key versions, rotation and the version lifecycle

The CryptoKeyVersion is where the real cryptographic material lives, and it has a strict lifecycle:

ENABLED  ->  DISABLED  ->  DESTROY_SCHEDULED  ->  DESTROYED
   ^___________|              (24h+ wait)

The destruction delay is a deliberate safety net against fat-fingered or malicious destruction — it gives you a window to notice and restore.

Rotation

For symmetric keys you set a rotation period. On each rotation KMS generates a new version and makes it the primary — the version used for all new encryptions. Crucially, old versions stay ENABLED so that anything encrypted previously can still be decrypted. You are not re-encrypting existing data on rotation; you are changing the key used going forward.

PROJECT=sec-kms-lab
LOCATION=us-central1

# Create a key ring (location is permanent)
gcloud kms keyrings create app-keyring \
  --project="$PROJECT" --location="$LOCATION"

# Symmetric CMEK key with 90-day automatic rotation
gcloud kms keys create app-cmek \
  --project="$PROJECT" --location="$LOCATION" \
  --keyring=app-keyring \
  --purpose=encryption \
  --protection-level=software \
  --rotation-period=90d \
  --next-rotation-time="$(date -u -d '+90 days' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null \
      || date -u -v+90d +%Y-%m-%dT%H:%M:%SZ)"

Rotation guidance worth committing to memory:

Core concept: envelope encryption (DEKs and KEKs)

This is the single most important mental model in the whole topic, and it underpins both default encryption and CMEK. You almost never use a KMS key to encrypt your data directly. Why? Because KMS has request-size limits (you cannot stream a 5 GB file through it), every call costs latency, and you do not want your key material leaving its boundary on every byte. Instead you use envelope encryption:

  1. Generate a data encryption key (DEK) locally — a fresh AES-256 key.
  2. Encrypt your data with the DEK (fast, local, any size).
  3. Send the DEK to KMS and ask the key encryption key (KEK) — your CMEK key — to encrypt it. KMS returns the wrapped (encrypted) DEK.
  4. Store the wrapped DEK alongside the ciphertext, and throw away the plaintext DEK.

To read the data: send the wrapped DEK to KMS, the KEK unwraps it, you decrypt locally with the DEK, then discard it. The genius of this design:

   plaintext data ──(AES with DEK)──► ciphertext
        DEK ──(wrap with KEK in KMS)──► wrapped DEK
   stored together: [ ciphertext | wrapped DEK ]

This is exactly what GCP services do internally when you attach a CMEK: they generate per-resource DEKs and wrap them with your KMS key (the KEK). KMS even offers generateRandomBytes and an EncryptedKey flow to help you implement this pattern yourself in application code.

Core concept: protection levels — SOFTWARE, HSM, EXTERNAL, EXTERNAL_VPC

The protection level decides where the key material physically lives and the security boundary it sits behind. It is fixed at key creation. There are four:

Protection level Where key material lives FIPS / boundary Cost When to use
SOFTWARE Google’s software KMS FIPS 140-2 Level 1 Cheapest Default; the vast majority of CMEK needs
HSM (Cloud HSM) Google-operated hardware security modules FIPS 140-2 Level 3 Higher per-key + per-operation Compliance mandating HSM-backed keys; high-assurance workloads
EXTERNAL (EKM) A third-party key manager outside Google, reached over the internet Depends on the external provider Provider-dependent Hold-your-own-key; key material must never reside in Google
EXTERNAL_VPC (EKM via VPC) A third-party key manager reached privately over VPC (no public internet) Provider-dependent Provider-dependent EKM with private connectivity for lower latency / no internet exposure

Key facts an interviewer probes:

Core concept: CMEK on GCP services — wiring the key in

CMEK is the headline use of Cloud KMS: you tell a GCP service to use your KMS key instead of Google’s default key. The pattern is the same across services and has one step everyone forgets — granting the service’s service agent permission to use your key.

Every GCP service that supports CMEK runs as a Google-managed service agent (a special service account like service-PROJECT_NUMBER@gcp-sa-storage.iam.gserviceaccount.com for Cloud Storage). That service agent must hold the roles/cloudkms.cryptoKeyEncrypterDecrypter role on your key, or the service literally cannot wrap/unwrap the DEKs and the CMEK assignment fails.

Here is the canonical Cloud Storage example end to end:

PROJECT=sec-kms-lab
PROJECT_NUMBER=$(gcloud projects describe "$PROJECT" --format='value(projectNumber)')
LOCATION=us-central1
KEY=projects/$PROJECT/locations/$LOCATION/keyRings/app-keyring/cryptoKeys/app-cmek

# 1. Grant the Cloud Storage service agent use of the key
gcloud kms keys add-iam-policy-binding app-cmek \
  --project="$PROJECT" --location="$LOCATION" --keyring=app-keyring \
  --member="serviceAccount:service-${PROJECT_NUMBER}@gs-project-accounts.iam.gserviceaccount.com" \
  --role="roles/cloudkms.cryptoKeyEncrypterDecrypter"

# 2. Create a bucket that uses the CMEK as its default encryption key
gcloud storage buckets create gs://${PROJECT}-cmek-bucket \
  --location="$LOCATION" \
  --default-encryption-key="$KEY"

CMEK support and the gotchas, service by service:

Service How CMEK attaches Notes / gotchas
Cloud Storage Bucket default encryption key, or per-object Service agent needs the role; key location must match bucket location
BigQuery Default key on the dataset (or per-table/query) Disabling the key makes the table return errors until re-enabled — instant revocation
Compute Engine / Persistent Disk --kms-key on disk creation; also for images, snapshots Boot and data disks separately; key must match disk region
Cloud SQL / AlloyDB CMEK chosen at instance creationcannot be added later Service agent grant required; replicas inherit the primary’s CMEK constraint
Pub/Sub CMEK on the topic Encrypts message payloads at rest
GKE Application-layer secrets encryption (etcd) + CMEK on node boot disks Two distinct things — etcd Secret encryption is separate from disk CMEK
Cloud Run / Cloud Functions CMEK on the service/function Encrypts the deployed container/source at rest

The two universal rules: location compatibility (key and resource in compatible locations) and the service-agent grant. If a CMEK operation fails, check those two first.

Core concept: IAM on keys and separation of duties

Access to KMS is pure IAM, and the predefined roles are deliberately fine-grained so you can separate managing a key from using it — the heart of good key hygiene:

Role Grants Give it to
roles/cloudkms.admin Full management: create/rotate/destroy keys, set IAM (but not use keys to encrypt/decrypt) Security/key administrators
roles/cloudkms.cryptoKeyEncrypterDecrypter Use a key to encrypt and decrypt (no management) Applications, service agents of CMEK services
roles/cloudkms.cryptoKeyEncrypter Encrypt only Write-only / ingest workloads
roles/cloudkms.cryptoKeyDecrypter Decrypt only Read-only consumers
roles/cloudkms.signerVerifier / signer / verifier Asymmetric sign and/or verify Signing workloads
roles/cloudkms.publicKeyViewer Read the public key of an asymmetric key Verifiers/encryptors
roles/cloudkms.viewer Read metadata (no crypto, no management) Auditors, dashboards

The separation-of-duties design that interviewers love: grant the admin role to your key administrators (who can rotate and destroy but cannot read plaintext) and the encrypter/decrypter role to the applications and service agents (who can use the key but cannot destroy it). No single principal should hold both — so a compromised application cannot destroy keys, and a compromised admin cannot exfiltrate plaintext. Grant roles at the key level (most specific) rather than project-wide whenever possible, and remember that every KMS operation is written to Cloud Audit Logs, giving you a complete trail of who used which key version when.

Core concept: Autokey

Manually creating a key ring, a key, setting rotation and wiring the service-agent grant for every resource gets tedious and error-prone at scale. Cloud KMS Autokey automates it. With Autokey configured at the folder level, when a developer creates a CMEK-enabled resource (a bucket, a disk, a Cloud SQL instance), KMS automatically provisions a dedicated key with sensible defaults — correct location, rotation policy and the service-agent IAM binding — on demand. The benefits:

Autokey is the “paved road” for CMEK at organisation scale; without it, teams either skip CMEK (too much friction) or share one key too widely. It is administered by a key administrator and consumed via a --kms-key=autokey style request in supporting services.

From KMS to secrets: why Secret Manager exists

KMS protects keys. But your application also needs secrets — database passwords, third-party API keys, OAuth client secrets, TLS private keys, service-account credentials. These do not belong in environment variables baked into an image, in a .env committed to git, or in a config file on disk. They belong in Secret Manager: a fully managed, global service that stores secret payloads, versions them, controls access with IAM, encrypts them at rest (with Google keys or your CMEK), and can rotate them on a schedule.

The relationship to KMS is direct: Secret Manager uses envelope encryption with KMS underneath to protect every secret at rest, and you can bring your own CMEK key to control that encryption — the two services compose.

Core concept: the Secret Manager object model

Get the model exact before you automate against it:

Object What it is Key properties
Secret A logical container with a name, replication policy, optional rotation schedule and IAM Holds no payload itself; it is the unit you grant access to
Secret version Holds the actual bytes (the payload) Immutable once created; numbered monotonically (1, 2, 3…)
Version state ENABLED, DISABLED, DESTROYED Disabled rejects access but can be re-enabled; destroyed deletes the payload permanently
latest alias Always resolves to the highest-numbered ENABLED version The only built-in alias — no named/staged labels
PROJECT=sec-kms-lab

# Create the container (no payload yet), automatic replication
gcloud secrets create db-app-password \
  --replication-policy="automatic" \
  --project="$PROJECT"

# Add a version — versions are immutable; "add" always makes a new number
echo -n "initial-bootstrap-pw" | \
  gcloud secrets versions add db-app-password --data-file=- \
  --project="$PROJECT"

# Access the payload: pin to a number, or use the moving 'latest'
gcloud secrets versions access latest --secret=db-app-password --project="$PROJECT"
gcloud secrets versions access 1       --secret=db-app-password --project="$PROJECT"

The immutability of versions plus the latest alias is what makes zero-downtime rotation possible. Because latest follows the newest enabled version, a rotator that adds version N+1 instantly shifts latest while version N stays enabled and valid — consumers pinned to latest pick up the new value on their next read; in-flight consumers keep working. You only break something by disabling or destroying the old version too early. The discipline: add new, cut over consumers, then disable old (never destroy immediately).

Core concept: accessing secrets from workloads

A secret is useless if your code cannot read it cleanly. The access surface:

The golden rule across all of these: the workload’s identity (its service account) must hold roles/secretmanager.secretAccessor on the specific secret. Grant it at the secret level, not the project, so each workload reads only the secrets it needs.

Core concept: Secret Manager IAM

Like KMS, access is IAM and the roles separate managing from reading:

Role Grants Give it to
roles/secretmanager.admin Full control: create/delete secrets, manage versions, set IAM Secret administrators
roles/secretmanager.secretVersionManager Add/disable/destroy versions and manage the secret (but not its IAM) Rotation pipelines, CI/CD
roles/secretmanager.secretAccessor Read the payload of versions only Applications / workloads
roles/secretmanager.secretVersionAdder Add new versions only Write-only ingest
roles/secretmanager.viewer Read metadata, not the payload Auditors, dashboards

The least-privilege pattern mirrors KMS exactly: applications get secretAccessor on the precise secrets they consume; rotation logic gets secretVersionManager; humans who administer the catalogue get admin. No application should ever hold admin. And as with KMS, every access is audit-logged (enable data-access audit logs to capture reads) so you can prove who read which secret version.

Core concept: Secret Manager replication

When you create a secret you choose how its payload is replicated, and this is a permanent decision per secret:

Replication policy Where the payload lives Choose when Trade-off
Automatic (automatic) Google replicates across multiple regions automatically Default; you have no data-residency constraint and want maximum availability with zero config Simplest; you don’t control which regions
User-managed (user-managed) You list the specific regions the payload may live in Data-residency / compliance — payload must stay in named regions (e.g. only europe-west2, europe-west1) You manage region choice; slightly more setup
# User-managed replication pinned to two European regions for residency
gcloud secrets create eu-only-secret \
  --replication-policy="user-managed" \
  --locations="europe-west1,europe-west2" \
  --project="$PROJECT"

A critical CMEK interaction: if you want CMEK on a secret, the key(s) must match the replication policy. An automatically-replicated secret needs a CMEK key per replica location (or you use user-managed replication so you can pair each region with a key in that region). This is why data-residency CMEK secrets almost always use user-managed replication.

Core concept: Secret Manager rotation and CMEK

Rotation in Secret Manager is notification-driven, not magic. You attach a rotation schedule (a period and/or a next-rotation time) and a Pub/Sub topic; when the secret is due, Secret Manager publishes a message to the topic. You supply the logic — typically a Cloud Function or Cloud Run job — that mints a fresh credential, calls add-version, and (after consumers cut over) disables the old version. Secret Manager does not generate the new secret value itself; it orchestrates the timing and notification.

# A secret that emits a Pub/Sub notification every 30 days for a rotator to act on
gcloud secrets create db-app-password-rotating \
  --replication-policy="automatic" \
  --topics="projects/${PROJECT}/topics/secret-rotation" \
  --rotation-period="2592000s" \
  --next-rotation-time="$(date -u -d '+30 days' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null \
      || date -u -v+30d +%Y-%m-%dT%H:%M:%SZ)" \
  --project="$PROJECT"

For CMEK on secrets, you supply a KMS key so that your key wraps the secret’s DEK at rest (Secret Manager uses envelope encryption underneath). The Secret Manager service agent needs cryptoKeyEncrypterDecrypter on the key (the same service-agent pattern as every other CMEK service), and the key location must be compatible with the secret’s replication. The full rotation pipeline — Pub/Sub, a Cloud Functions rotator, CMEK and least-privilege IAM — is built end to end in the companion lesson; here you should retain the shape: schedule + Pub/Sub notification → your rotator adds a version → consumers on latest cut over → disable the old version, all optionally wrapped in your own CMEK.

Google Cloud KMS & Secret Manager

The diagram above ties the two services together: on the left, the Cloud KMS hierarchy (key ring → key → versions) feeding CMEK into GCP services through envelope encryption (KEK wrapping per-resource DEKs); on the right, Secret Manager’s secret → versions model with the latest alias, IAM-gated access from workloads, and a rotation loop driven by Pub/Sub — with KMS sitting underneath Secret Manager to encrypt secrets at rest.

Hands-on lab: a CMEK-encrypted bucket and a versioned secret

This lab creates a KMS key, encrypts a Cloud Storage bucket with it (CMEK end to end including the service-agent grant), demonstrates instant revocation by disabling the key version, and creates a versioned secret with least-privilege access. It runs comfortably within the $300 free trial; KMS keys cost about $0.06 per active key version per month plus a tiny per-operation fee, and the bucket/secret usage here is negligible.

Step 1 — Set up variables and enable APIs

export PROJECT=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe "$PROJECT" --format='value(projectNumber)')
export LOCATION=us-central1

gcloud services enable cloudkms.googleapis.com secretmanager.googleapis.com \
  storage.googleapis.com --project="$PROJECT"

Step 2 — Create a key ring and a rotating CMEK key

gcloud kms keyrings create lab-keyring \
  --project="$PROJECT" --location="$LOCATION"

gcloud kms keys create lab-cmek \
  --project="$PROJECT" --location="$LOCATION" --keyring=lab-keyring \
  --purpose=encryption --protection-level=software \
  --rotation-period=90d \
  --next-rotation-time="$(date -u -d '+90 days' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null \
      || date -u -v+90d +%Y-%m-%dT%H:%M:%SZ)"

Step 3 — Grant the Cloud Storage service agent use of the key, then create a CMEK bucket

SA="service-${PROJECT_NUMBER}@gs-project-accounts.iam.gserviceaccount.com"

gcloud kms keys add-iam-policy-binding lab-cmek \
  --project="$PROJECT" --location="$LOCATION" --keyring=lab-keyring \
  --member="serviceAccount:${SA}" \
  --role="roles/cloudkms.cryptoKeyEncrypterDecrypter"

KEY="projects/$PROJECT/locations/$LOCATION/keyRings/lab-keyring/cryptoKeys/lab-cmek"
gcloud storage buckets create "gs://${PROJECT}-lab-cmek" \
  --location="$LOCATION" --default-encryption-key="$KEY"

echo "hello cmek" > /tmp/lab.txt
gcloud storage cp /tmp/lab.txt "gs://${PROJECT}-lab-cmek/"

Step 4 — Prove revocation: disable the key version, watch access fail, then restore

# Disable the only (primary) version — this locks the data
gcloud kms keys versions disable 1 \
  --project="$PROJECT" --location="$LOCATION" \
  --keyring=lab-keyring --key=lab-cmek

# Reading the object now FAILS — the DEK can no longer be unwrapped
gcloud storage cat "gs://${PROJECT}-lab-cmek/lab.txt" || echo ">> Access denied: key disabled"

# Re-enable and access is restored
gcloud kms keys versions enable 1 \
  --project="$PROJECT" --location="$LOCATION" \
  --keyring=lab-keyring --key=lab-cmek
gcloud storage cat "gs://${PROJECT}-lab-cmek/lab.txt"   # -> hello cmek

Step 5 — Create a versioned secret with least-privilege access

gcloud secrets create lab-secret --replication-policy="automatic" --project="$PROJECT"
echo -n "s3cr3t-v1" | gcloud secrets versions add lab-secret --data-file=- --project="$PROJECT"
echo -n "s3cr3t-v2" | gcloud secrets versions add lab-secret --data-file=- --project="$PROJECT"

# 'latest' follows the newest enabled version
gcloud secrets versions access latest --secret=lab-secret --project="$PROJECT"   # -> s3cr3t-v2

# Grant a workload SA accessor on THIS secret only (replace with a real SA to test)
gcloud secrets add-iam-policy-binding lab-secret \
  --member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
  --role="roles/secretmanager.secretAccessor" --project="$PROJECT"

Validation

# Confirm the bucket's default key is your CMEK
gcloud storage buckets describe "gs://${PROJECT}-lab-cmek" \
  --format='value(default_kms_key)'

# Confirm key version state and rotation
gcloud kms keys describe lab-cmek --location="$LOCATION" --keyring=lab-keyring \
  --format='value(rotationPeriod,nextRotationTime)'

# List secret versions and states
gcloud secrets versions list lab-secret --project="$PROJECT"

You should see the CMEK resource name on the bucket, a 90-day rotation period on the key, and two ENABLED secret versions.

Cleanup

gcloud storage rm -r "gs://${PROJECT}-lab-cmek"
gcloud secrets delete lab-secret --project="$PROJECT" --quiet
gcloud kms keys versions destroy 1 \
  --project="$PROJECT" --location="$LOCATION" --keyring=lab-keyring --key=lab-cmek --quiet
# NOTE: key rings and keys CANNOT be deleted. Destroying the version stops billing for it.

Cost note

A key version costs roughly $0.06/month while active, plus ~$0.03 per 10,000 crypto operations; HSM and EKM keys cost more per version and per operation. Secret Manager bills about $0.06 per active secret version per month and $0.03 per 10,000 access operations, with a free allowance. Destroying the key version and deleting the secret stops those charges; the empty key ring and key remain forever at no cost.

Common mistakes & troubleshooting

Symptom Likely cause Fix
PERMISSION_DENIED when a service tries to use your CMEK The service’s service agent lacks cryptoKeyEncrypterDecrypter on the key Grant the role to the correct service-...@gcp-sa-<service>.iam.gserviceaccount.com agent
“Key location is not compatible” creating a CMEK resource Key and resource in different/incompatible locations Create the key in the resource’s region (or matching multi-region)
Cannot add CMEK to an existing Cloud SQL instance CMEK is set only at instance creation for Cloud SQL Create a new instance with CMEK and migrate data
Reads suddenly fail across a dataset/bucket A key version was disabled or destroyed Re-enable the version (if disabled); destroyed = data unrecoverable
Rotating the key did not protect old data Rotation only changes the key for new writes Rewrite/recreate the data so it re-encrypts under the new primary
App can’t read a secret despite IAM Granted at project, or the wrong SA, or missing the secretAccessor role Grant secretAccessor to the workload’s SA on the specific secret
CMEK secret creation fails CMEK key doesn’t match the secret’s replication regions Use user-managed replication with a key per region, or fix the key location
Tried to delete a key ring/key Key rings and keys are immutable / non-deletable Disable/destroy versions instead; the empty ring is harmless and free

Best practices

Security notes

The whole value proposition of these services is control under attack, so treat the controls themselves as the crown jewels. Lock down cloudkms.admin and secretmanager.admin to a tiny set of break-glass identities — anyone with admin on a key can schedule its destruction, and anyone with admin on a secret can read or delete it. Rely on the destroy-scheduled delay (raise it from the 24-hour default if your change-control cadence is slower) as a safety net, and alert on DestroyCryptoKeyVersion and on secret deletions in audit logs. Never store raw key material or secret payloads in code, images, build logs or environment files committed to source control — that single habit defeats everything KMS and Secret Manager give you. Prefer keyless access (Application Default Credentials, Workload Identity) over service-account keys, because a key file in a repo is itself the worst secret leak. For the highest assurance, use Cloud HSM for FIPS 140-2 Level 3 plus verifiable attestation, or EKM when policy demands the key material never reside in Google at all — but understand that EKM makes your data’s availability depend on the external manager.

Interview & exam questions

  1. What does “encrypted at rest by default” not give you, and how does CMEK fix it? Default encryption protects against physical media theft but leaves Google holding the only key — you cannot revoke access to plaintext. CMEK gives you the key, so you can disable/destroy it and make the data unreadable on demand, with an audit trail and your own rotation.
  2. Walk through envelope encryption. A local DEK encrypts the data (fast, any size); the DEK is wrapped by a KEK in KMS; the wrapped DEK is stored with the ciphertext; the plaintext DEK is discarded. To read, KMS unwraps the DEK and you decrypt locally. It gives performance, cheap rotation (re-wrap small DEKs, not re-encrypt data) and revocation (kill the KEK, all DEKs die).
  3. Difference between disabling and destroying a key version? Disabling is reversible — it instantly blocks use (your emergency revocation switch) and can be re-enabled. Destroying schedules permanent deletion after a mandatory delay (default 24h, up to 120 days); once destroyed, any data still encrypted with it is unrecoverable.
  4. Does rotating a key re-encrypt existing data? No. Rotation creates a new primary version used for new encryptions; old versions stay enabled to decrypt existing data. To re-encrypt old data you must rewrite/recreate it so it picks up the new primary.
  5. Name the four protection levels and the headline difference. SOFTWARE (FIPS 140-2 L1, Google software), HSM (FIPS 140-2 L3 hardware with attestation), EXTERNAL (key material in a third-party manager over the internet), EXTERNAL_VPC (the same but over private VPC). They differ in where the material lives and the assurance/cost.
  6. What is the one step people forget when enabling CMEK on a service? Granting the service’s service agent the cryptoKeyEncrypterDecrypter role on the key — without it the service cannot wrap/unwrap DEKs and CMEK fails.
  7. Which KMS roles enforce separation of duties? cloudkms.admin can manage (rotate/destroy) but not use a key; cloudkms.cryptoKeyEncrypterDecrypter can use but not manage. Splitting them means no single principal can both read plaintext and destroy the key.
  8. What does the latest alias do in Secret Manager and why does it enable zero-downtime rotation? It resolves to the highest-numbered enabled version. Adding version N+1 instantly shifts latest while N stays valid, so consumers cut over on their next read with no outage — provided you don’t disable N too early.
  9. Automatic vs user-managed replication for a secret — when each? Automatic = Google picks regions, simplest, max availability. User-managed = you list specific regions, required for data-residency; also necessary to pair region-matched CMEK keys.
  10. Does Secret Manager generate new secret values on rotation? No — it publishes a Pub/Sub notification on a schedule; you provide the rotator (e.g. a Cloud Function) that mints the new value and adds a version. Secret Manager orchestrates timing, not generation.
  11. What is Cloud KMS Autokey for? It auto-provisions a per-resource CMEK key (correct location, rotation, service-agent IAM) when a developer creates a CMEK resource, removing manual toil and enforcing consistent, policy-compliant keys at folder scale.
  12. Why store secrets in Secret Manager rather than Kubernetes Secrets or env vars? K8s Secrets are only base64-encoded by default (not encrypted); env vars/config files leak via images and source control. Secret Manager gives encryption at rest (optionally CMEK), versioning, IAM-gated access and audit logs.

Quick check

  1. True/False: A Cloud KMS key ring can be deleted once it has no keys.
  2. Which key purpose does CMEK use, and is it symmetric or asymmetric?
  3. You disable the single key version protecting a BigQuery dataset. What happens to queries, and is it reversible?
  4. Your CMEK assignment to a bucket fails with a permission error. What is the most likely missing grant?
  5. A secret needs its payload to stay only in europe-west2. Which replication policy do you choose?

Answers

  1. False. Key rings (and keys) cannot be deleted or renamed; their location is permanent. Empty ones are harmless and free.
  2. The encryption purpose — symmetric (ENCRYPT_DECRYPT, AES-256-GCM). It is the only purpose CMEK uses.
  3. Queries fail (the data is unreadable because the DEK can no longer be unwrapped). It is reversible — re-enable the version and access is restored.
  4. The Cloud Storage service agent is missing roles/cloudkms.cryptoKeyEncrypterDecrypter on the key.
  5. User-managed replication, with --locations=europe-west2 (and a region-matched CMEK key if encrypting with your own key).

Exercise

In your free-trial project, build a small “secure config” setup that an interviewer could probe:

  1. Create a key ring and a symmetric CMEK key in your region with 30-day rotation, and verify the rotation period.
  2. Create a Pub/Sub-notified, rotating secret (--topics, --rotation-period, --next-rotation-time) holding a fake API key, add two versions, and confirm latest returns the second.
  3. Wrap the secret in your CMEK key (grant the Secret Manager service agent cryptoKeyEncrypterDecrypter first) and confirm creation succeeds — then disable the key version and observe that accessing the secret now fails.
  4. Apply separation of duties: grant a “rotator” SA secretVersionManager and an “app” SA secretAccessor on the secret, and grant a “key-admin” SA cloudkms.admin (but not encrypterDecrypter) on the key. Confirm the app SA can read but cannot destroy, and the key-admin can rotate but cannot read plaintext.
  5. Clean up: delete the secret, destroy the key version, and note that the key ring remains.

Write down, in one paragraph each, why disabling the key broke secret access (envelope encryption) and why the latest alias made adding a version a zero-downtime change.

Certification mapping

Glossary

Next steps

You can now reason about the full encryption story on Google Cloud — default vs CMEK vs CSEK — build and rotate Cloud KMS keys, wire CMEK into services with the service-agent grant, explain envelope encryption and the protection levels, and operate Secret Manager with versioned, IAM-gated, optionally CMEK-encrypted secrets. To go deeper on the key side, follow Cloud KMS in Depth: CMEK, Envelope Encryption, Cloud HSM and External Key Manager, and for production rotation automation read Secret Manager Rotation Pipelines with Cloud Functions, IAM and CMEK. Next in the course, make all of this observable in Google Cloud Operations Suite, In Depth: Cloud Monitoring, Logging, Trace & Error Reporting, where audit logs for key use and secret access become the evidence behind your alerts.

GCPCloud KMSSecret ManagerCMEKEncryptionSecurity
Need this built for real?

Vinod is a Senior Cloud Architect (22+ yrs) — available for Azure / AWS / GCP architecture, landing zones, and migrations.

Work with me

Comments