{"record":{"id":"4e276c1c4cd2a5b1","repo":"hashicorp/terraform","slug":"can-t-set-both-encryption-key-and-kms-encryption-k","errorCode":null,"errorMessage":"can't set both encryption_key and kms_encryption_key","messagePattern":"can't set both encryption_key and kms_encryption_key","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/backend/remote-state/gcs/backend.go","lineNumber":148,"sourceCode":"\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (b *Backend) Configure(configVal cty.Value) tfdiags.Diagnostics {\n\tif b.storageClient != nil {\n\t\treturn nil\n\t}\n\n\t// TODO: Update the Backend API to pass the real context.Context from\n\t// the running command.\n\tctx := context.TODO()\n\n\tdata := backendbase.NewSDKLikeData(configVal)\n\n\tif data.String(\"encryption_key\") != \"\" && data.String(\"kms_encryption_key\") != \"\" {\n\t\treturn backendbase.ErrorAsDiagnostics(\n\t\t\tfmt.Errorf(\"can't set both encryption_key and kms_encryption_key\"),\n\t\t)\n\t}\n\t// The above catches the main case where both of the arguments are set to\n\t// a non-empty value, but we also want to reject the situation where\n\t// both are present in the configuration regardless of what values were\n\t// assigned to them. (This check doesn't take the environment variables\n\t// into account, so must allow neither to be set in the main configuration.)\n\tif !(configVal.GetAttr(\"encryption_key\").IsNull() || configVal.GetAttr(\"kms_encryption_key\").IsNull()) {\n\t\t// This rejects a configuration like:\n\t\t//     encryption_key     = \"\"\n\t\t//     kms_encryption_key = \"\"\n\t\treturn backendbase.ErrorAsDiagnostics(\n\t\t\tfmt.Errorf(\"can't set both encryption_key and kms_encryption_key\"),\n\t\t)\n\t}\n\n\tb.bucketName = data.String(\"bucket\")\n\tb.prefix = strings.TrimLeft(data.String(\"prefix\"), \"/\")","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/backend/remote-state/gcs/backend.go#L130-L166","documentation":"Returned by the GCS remote-state backend's Configure() when both `encryption_key` and `kms_encryption_key` are configured. These are mutually exclusive ways to encrypt state in GCS — a customer-supplied key vs a Cloud KMS key — and Terraform cannot decide which to apply. The check fires on the non-empty string values and is then re-enforced on the raw cty attributes to also reject both-empty-but-present configs.","triggerScenarios":"Backend `cloud`/`gcs` block sets both `encryption_key` (or GOOGLE_ENCRYPTION_KEY env) and `kms_encryption_key` (or GOOGLE_KMS_ENCRYPTION_KEY env) to non-empty values, OR both attributes appear in the config block (even as empty strings) without one being null.","commonSituations":"Copy-pasting a backend block that grew over time and now has both keys; setting GOOGLE_ENCRYPTION_KEY in the shell while the config also declares kms_encryption_key; migrating from customer-managed keys to KMS and forgetting to remove the old attribute; both set to empty string in HCL which the second check still rejects.","solutions":["Choose ONE encryption strategy: keep either `encryption_key` (customer-supplied) or `kms_encryption_key` (KMS-managed), not both.","Remove the unused attribute from the backend block entirely so it is null in cty (not empty string).","Unset the corresponding environment variable (GOOGLE_ENCRYPTION_KEY or GOOGLE_KMS_ENCRYPTION_KEY) if it is no longer intended.","Re-run `terraform init` after editing the backend block so Configure re-evaluates."],"exampleFix":"// before: both keys configured\nbackend \"gcs\" {\n  bucket=\"tf-state\"\n  encryption_key=\"base64-key\"\n  kms_encryption_key=\"projects/p/locations/global/keyRings/kr/cryptoKeys/k\"\n}\n// after: keep only one\nbackend \"gcs\" {\n  bucket=\"tf-state\"\n  kms_encryption_key=\"projects/p/locations/global/keyRings/kr/cryptoKeys/k\"\n}","handlingStrategy":"validation","validationCode":"// Validate backend config before `terraform init` so Configure() never sees both keys\nfunc validateGCSBackend(cfg map[string]any) error {\n    enc, hasEnc := cfg[\"encryption_key\"]\n    kms, hasKms := cfg[\"kms_encryption_key\"]\n    encSet := hasEnc && enc != nil && fmt.Sprint(enc) != \"\"\n    kmsSet := hasKms && kms != nil && fmt.Sprint(kms) != \"\"\n    if encSet && kmsSet {\n        return fmt.Errorf(\"set only one of encryption_key or kms_encryption_key\")\n    }\n    // also enforce the 'both present even if empty' rule\n    if hasEnc && hasKms {\n        return fmt.Errorf(\"remove one of encryption_key/kms_encryption_key from the config\")\n    }\n    return nil\n}","typeGuard":"// Type guard over the parsed cty value: exactly one of the two is non-null\nfunc exactlyOneEncryptionKey(v cty.Value) bool {\n    encNull := v.GetAttr(\"encryption_key\").IsNull()\n    kmsNull := v.GetAttr(\"kms_encryption_key\").IsNull()\n    return encNull != kmsNull // XOR\n}","tryCatchPattern":"// Not applicable — this is a config-time validation error, not a runtime\n// exception to catch. Fix the backend block and re-run terraform init.","preventionTips":["Set exactly one of `encryption_key` or `kms_encryption_key` in the backend block.","When migrating from customer keys to KMS, delete the old attribute (do not leave it empty).","Unset the matching env var (GOOGLE_ENCRYPTION_KEY / GOOGLE_KMS_ENCRYPTION_KEY) when switching strategies.","Run `terraform init` after backend edits to validate early."],"tags":["terraform","gcs","google-cloud","encryption","config","validation","remote-state","go"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}