LAB15_GCP_Encryption: Customer Managed Encryption Keys (CMEK)

Encryption: Customer Managed Encryption Keys (CMEK)

☁ GCP 🟢 Beginner Cloud KMS KeyRing CryptoKey CMEK

Scenario

By default, all Google Cloud Storage data is encrypted using Google Managed Encryption Keys. For regulatory compliance, your client requires Customer Managed Encryption Keys (CMEK). This guarantees your company controls the key rotation and the ability to instantly revoke access to data by disabling the key. You need to create a Cloud Key Management Service (KMS) KeyRing and CryptoKey.

Your Objectives
  • Create a google_kms_key_ring in the global location. Note: KMS Key Rings cannot be deleted in GCP, so append a random ID suffix to avoid name collisions.
  • Create a google_kms_crypto_key on that ring. Set its rotation period to 90 days.
  • Output the full KMS crypto key ID mapping to use when creating GCS buckets.

Additional Context

Immutable Resources: Once you create a Key Ring or Crypto Key in Google Cloud KMS, they cannot be destroyed or deleted. You can only "Destroy" the key versions (making data unreadable), but the resource namespace is permanently occupied. Terraform destroy will simply remove the resource from the Terraform state, but it will physically remain in GCP.

Time duration format: GCP expects rotation periods to be specified in seconds with an `s` appended. 90 days equals `7776000s`.

variables.tf

variable "project_id" { type = string }

main.tf

provider "google" {
  project = var.project_id
}

# KMS resources cannot be deleted. RandomID prevents conflicts if applying multiple times.
resource "random_id" "kms_suffix" {
  byte_length = 4
}

# The Key Ring (a container for crypto keys)
resource "google_kms_key_ring" "keyring" {
  name     = "app-keyring-${random_id.kms_suffix.hex}"
  # Key rings can be global or regional (e.g., "us-central1")
  location = "global"
}

# The Crypto Key (encrypts data)
resource "google_kms_crypto_key" "app_key" {
  name            = "storage-cmek"
  key_ring        = google_kms_key_ring.keyring.id
  
  # Set up rotation to occur every 90 days
  rotation_period = "7776000s"

  lifecycle {
    # It's a best practice to protect KMS keys from being inadvertently dropped from state
    prevent_destroy = false # Set false here just so the lab applies cleanly, normally true
  }
}

outputs.tf

output "crypto_key_id" {
  value       = google_kms_crypto_key.app_key.id
  description = "Use this string when setting encryption = { default_kms_key_name = ... } on GCS"
}

Workflow Commands

terraform init
terraform apply -var="project_id=my-gcp-project-123"

# List keys in the global keyring
gcloud kms keys list \
  --location=global \
  --keyring=app-keyring-$(terraform output -raw crypto_key_id | awk -F'/' '{print $6}')

# Warning: The following will remove the key from state, but the key remains in GCP!
terraform destroy -var="project_id=my-gcp-project-123"
✓ Expected Output After Apply
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

Outputs:

crypto_key_id = "projects/my-gcp-project-123/locations/global/keyRings/app-keyring-a1b2c3d4/cryptoKeys/storage-cmek"