How to Update a Terraform KMS Key Policy to Grant Service Account Access

Use the google_kms_crypto_key_iam_member resource with the role roles/cloudkms.cryptoKeyEncrypterDecrypter to add a service account to an existing Google Cloud KMS key without removing current IAM bindings.

Managing encryption key access in Google Cloud requires precise IAM controls to prevent service interruptions. In the hashicorp/terraform repository, the Google Cloud provider implements specific resources for KMS key policy management that distinguish between additive and authoritative permission models. Understanding the correct terraform kms key policy syntax ensures you grant encrypt and decrypt privileges to new service accounts while preserving existing access rights.

Selecting the Appropriate Terraform KMS IAM Resource

The Google Cloud provider offers three distinct resource types for managing KMS IAM policies, each with different behaviors regarding existing permissions.

Additive Updates with google_kms_crypto_key_iam_member

The google_kms_crypto_key_iam_member resource is the recommended approach when you need to grant access to a new service account without affecting existing permissions. According to the implementation in google/kms_crypto_key_iam_member.go, this resource adds a single IAM binding to the crypto key's policy, preserving all other members assigned to the same role.

Use this resource when:

  • You need to incrementally add a service account to an existing key
  • Multiple Terraform configurations or external processes manage the same KMS key
  • You want to avoid accidental removal of existing permissions

Authoritative Role Management with google_kms_crypto_key_iam_binding

The google_kms_crypto_key_iam_binding resource manages the complete list of members for a specific role. When you use this resource, Terraform ensures that only the members defined in your configuration have that role on the key, removing any members not listed in your code.

Use this resource only when:

  • Your Terraform configuration is the single source of truth for all members of that role
  • You want to enforce that no unauthorized accounts receive permissions outside of Terraform

Correct Terraform KMS Key Policy Syntax

To grant a service account both encrypt and decrypt permissions on an existing KMS key, use the predefined role roles/cloudkms.cryptoKeyEncrypterDecrypter. This role bundles both permissions into a single IAM binding.

resource "google_kms_crypto_key_iam_member" "sa_encrypt_decrypt" {
  crypto_key_id = google_kms_crypto_key.my_key.id
  role          = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
  member        = "serviceAccount:my-service-account@my-project.iam.gserviceaccount.com"
}

Key parameters explained:

  • crypto_key_id: The self-link or ID of the existing KMS crypto key
  • role: Must be roles/cloudkms.cryptoKeyEncrypterDecrypter for both operations, or use roles/cloudkms.cryptoKeyEncrypter / roles/cloudkms.cryptoKeyDecrypter for single operations
  • member: The full IAM member identifier including the serviceAccount: prefix

Complete Implementation Example

When deploying a new service account alongside an existing key, reference the key resource directly to ensure proper dependency ordering:

resource "google_kms_key_ring" "my_ring" {
  name     = "my-key-ring"
  location = "us-central1"
  project  = "my-project"
}

resource "google_kms_crypto_key" "my_key" {
  name            = "my-crypto-key"
  key_ring        = google_kms_key_ring.my_ring.id
  rotation_period = "2592000s"
  purpose         = "ENCRYPT_DECRYPT"
}

resource "google_kms_crypto_key_iam_member" "sa_encrypt_decrypt" {
  crypto_key_id = google_kms_crypto_key.my_key.id
  role          = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
  member        = "serviceAccount:my-service-account@my-project.iam.gserviceaccount.com"
}

Source Code Implementation Details

The behavior of these resources is defined in the Terraform Google Cloud provider source code. The resource schema and API interactions are implemented in the following files:

  • google/kms_crypto_key_iam_member.go: Handles the Create, Read, Update, and Delete operations for individual IAM member bindings against the Cloud KMS API
  • website/docs/r/kms_crypto_key_iam_member.html.markdown: Documents the argument reference and import syntax for the additive member resource
  • website/docs/r/kms_crypto_key_iam_binding.html.markdown: Defines the schema for authoritative role management where Terraform controls the complete member list

These files demonstrate how Terraform translates your HCL configuration into Google Cloud KMS IAM API calls, ensuring idempotent updates that respect your existing security posture.

Avoiding Accidental Policy Overwrites

Never use google_kms_crypto_key_iam_policy for incremental updates to an existing key. This resource implements an authoritative policy model that replaces the entire IAM policy on the crypto key during every terraform apply execution.

If you use google_kms_crypto_key_iam_policy, Terraform will:

  1. Read the current IAM policy
  2. Generate a new policy containing only the bindings defined in your configuration
  3. Overwrite the existing policy, removing any permissions granted outside of Terraform

For production environments where service accounts might be granted access through multiple automation pipelines or manual processes, always prefer google_kms_crypto_key_iam_member to prevent service disruptions.

Summary

  • Use google_kms_crypto_key_iam_member to add a single service account to an existing KMS key without affecting other permissions
  • Specify role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" to grant both encryption and decryption capabilities in a single binding
  • Format the member as serviceAccount:email to properly identify the service account principal
  • Avoid google_kms_crypto_key_iam_policy unless you intend to manage the complete IAM policy authoritatively
  • Reference the crypto key using google_kms_crypto_key.my_key.id to ensure Terraform tracks dependencies correctly

Frequently Asked Questions

What is the difference between google_kms_crypto_key_iam_member and google_kms_crypto_key_iam_binding?

The google_kms_crypto_key_iam_member resource manages a single IAM binding for one specific member, leaving all other existing bindings untouched. The google_kms_crypto_key_iam_binding resource manages the complete list of members for a specific role, removing any members not defined in your Terraform configuration. Use the member resource for additive changes and the binding resource only when Terraform should control the entire membership list for that role.

Can I use Terraform to update an existing KMS key policy without recreating the key?

Yes. The google_kms_crypto_key_iam_member resource allows you to update IAM permissions on existing keys without triggering recreation. Simply reference the existing key's ID in the crypto_key_id parameter. Terraform will detect the missing IAM binding and add it during the next apply, leaving the key resource and all other permissions intact.

What IAM role provides both encrypt and decrypt permissions for Cloud KMS?

Use the predefined role roles/cloudkms.cryptoKeyEncrypterDecrypter. This role grants both cloudkms.cryptoKeyVersions.useToEncrypt and cloudkms.cryptoKeyVersions.useToDecrypt permissions. Alternatively, you can use roles/cloudkms.cryptoKeyEncrypter for encrypt-only access or roles/cloudkms.cryptoKeyDecrypter for decrypt-only access, depending on your security requirements.

Why does my terraform plan show it will remove existing service accounts from the KMS key?

This occurs when you use google_kms_crypto_key_iam_binding or google_kms_crypto_key_iam_policy instead of google_kms_crypto_key_iam_member. The binding resource manages the complete member list for a role, while the policy resource manages the entire IAM policy. Switch to google_kms_crypto_key_iam_member for each service account you need to add, ensuring Terraform only manages the specific bindings you define while preserving existing permissions.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →