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 keyrole: Must beroles/cloudkms.cryptoKeyEncrypterDecrypterfor both operations, or useroles/cloudkms.cryptoKeyEncrypter/roles/cloudkms.cryptoKeyDecrypterfor single operationsmember: The full IAM member identifier including theserviceAccount: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 APIwebsite/docs/r/kms_crypto_key_iam_member.html.markdown: Documents the argument reference and import syntax for the additive member resourcewebsite/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:
- Read the current IAM policy
- Generate a new policy containing only the bindings defined in your configuration
- 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_memberto 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:emailto properly identify the service account principal - Avoid
google_kms_crypto_key_iam_policyunless you intend to manage the complete IAM policy authoritatively - Reference the crypto key using
google_kms_crypto_key.my_key.idto 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →