Skip to content

Commit 95c8de4

Browse files
Unblock EXTERNAL/EXTERNAL_VPC Cloud KMS key creation. (#9931) (#7155)
[upstream:a1e15b7437a5b41602e434668bf07434c2873336] Signed-off-by: Modular Magician <[email protected]>
1 parent 86f6c47 commit 95c8de4

File tree

6 files changed

+397
-1
lines changed

6 files changed

+397
-1
lines changed

.changelog/9931.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
```release-note:bug
2+
kms: added top-level `external_protection_level_options` field in `google_kms_crypto_key_version` resource
3+
```
4+
```release-note:deprecation
5+
kms: `attestation.external_protection_level_options` has been deprecated in favor of `external_protection_level_options` in `google_kms_crypto_key_version`
6+
```
7+
```release-note:enhancement
8+
kms: added `crypto_key_backend` field to `google_kms_crypto_key` resource
9+
```

google-beta/services/kms/resource_kms_crypto_key.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,14 @@ Format: ''projects/{{project}}/locations/{{location}}/keyRings/{{keyRing}}''.`,
7979
ForceNew: true,
8080
Description: `The resource name for the CryptoKey.`,
8181
},
82+
"crypto_key_backend": {
83+
Type: schema.TypeString,
84+
Computed: true,
85+
Optional: true,
86+
ForceNew: true,
87+
Description: `The resource name of the backend environment associated with all CryptoKeyVersions within this CryptoKey.
88+
The resource name is in the format "projects/*/locations/*/ekmConnections/*" and only applies to "EXTERNAL_VPC" keys.`,
89+
},
8290
"destroy_scheduled_duration": {
8391
Type: schema.TypeString,
8492
Computed: true,
@@ -230,6 +238,12 @@ func resourceKMSCryptoKeyCreate(d *schema.ResourceData, meta interface{}) error
230238
} else if v, ok := d.GetOkExists("import_only"); !tpgresource.IsEmptyValue(reflect.ValueOf(importOnlyProp)) && (ok || !reflect.DeepEqual(v, importOnlyProp)) {
231239
obj["importOnly"] = importOnlyProp
232240
}
241+
cryptoKeyBackendProp, err := expandKMSCryptoKeyCryptoKeyBackend(d.Get("crypto_key_backend"), d, config)
242+
if err != nil {
243+
return err
244+
} else if v, ok := d.GetOkExists("crypto_key_backend"); !tpgresource.IsEmptyValue(reflect.ValueOf(cryptoKeyBackendProp)) && (ok || !reflect.DeepEqual(v, cryptoKeyBackendProp)) {
245+
obj["cryptoKeyBackend"] = cryptoKeyBackendProp
246+
}
233247
labelsProp, err := expandKMSCryptoKeyEffectiveLabels(d.Get("effective_labels"), d, config)
234248
if err != nil {
235249
return err
@@ -351,6 +365,9 @@ func resourceKMSCryptoKeyRead(d *schema.ResourceData, meta interface{}) error {
351365
if err := d.Set("import_only", flattenKMSCryptoKeyImportOnly(res["importOnly"], d, config)); err != nil {
352366
return fmt.Errorf("Error reading CryptoKey: %s", err)
353367
}
368+
if err := d.Set("crypto_key_backend", flattenKMSCryptoKeyCryptoKeyBackend(res["cryptoKeyBackend"], d, config)); err != nil {
369+
return fmt.Errorf("Error reading CryptoKey: %s", err)
370+
}
354371
if err := d.Set("terraform_labels", flattenKMSCryptoKeyTerraformLabels(res["labels"], d, config)); err != nil {
355372
return fmt.Errorf("Error reading CryptoKey: %s", err)
356373
}
@@ -593,6 +610,10 @@ func flattenKMSCryptoKeyImportOnly(v interface{}, d *schema.ResourceData, config
593610
return v
594611
}
595612

613+
func flattenKMSCryptoKeyCryptoKeyBackend(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
614+
return v
615+
}
616+
596617
func flattenKMSCryptoKeyTerraformLabels(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
597618
if v == nil {
598619
return v
@@ -662,6 +683,10 @@ func expandKMSCryptoKeyImportOnly(v interface{}, d tpgresource.TerraformResource
662683
return v, nil
663684
}
664685

686+
func expandKMSCryptoKeyCryptoKeyBackend(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
687+
return v, nil
688+
}
689+
665690
func expandKMSCryptoKeyEffectiveLabels(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (map[string]string, error) {
666691
if v == nil {
667692
return map[string]string{}, nil

google-beta/services/kms/resource_kms_crypto_key_test.go

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,85 @@ func TestAccKmsCryptoKeyVersion_patch(t *testing.T) {
540540
})
541541
}
542542

543+
func TestAccKmsCryptoKeyVersion_externalProtectionLevelOptions(t *testing.T) {
544+
t.Parallel()
545+
546+
projectId := fmt.Sprintf("tf-test-%d", acctest.RandInt(t))
547+
projectOrg := envvar.GetTestOrgFromEnv(t)
548+
projectBillingAccount := envvar.GetTestBillingAccountFromEnv(t)
549+
keyRingName := fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10))
550+
cryptoKeyName := fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10))
551+
keyUri := "data.google_secret_manager_secret_version.key_uri.secret_data"
552+
updatedKeyUri := "data.google_secret_manager_secret_version.key_uri_updated.secret_data"
553+
554+
acctest.VcrTest(t, resource.TestCase{
555+
PreCheck: func() { acctest.AccTestPreCheck(t) },
556+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
557+
Steps: []resource.TestStep{
558+
{
559+
Config: testGoogleKmsCryptoKeyVersion_externalProtectionLevelOptions(projectId, projectOrg, projectBillingAccount, keyRingName, cryptoKeyName, keyUri),
560+
},
561+
{
562+
ResourceName: "google_kms_crypto_key_version.crypto_key_version",
563+
ImportState: true,
564+
ImportStateVerify: true,
565+
ImportStateVerifyIgnore: []string{"labels", "terraform_labels"},
566+
},
567+
{
568+
Config: testGoogleKmsCryptoKeyVersion_externalProtectionLevelOptions(projectId, projectOrg, projectBillingAccount, keyRingName, cryptoKeyName, updatedKeyUri),
569+
},
570+
{
571+
ResourceName: "google_kms_crypto_key_version.crypto_key_version",
572+
ImportState: true,
573+
ImportStateVerify: true,
574+
ImportStateVerifyIgnore: []string{"labels", "terraform_labels"},
575+
},
576+
},
577+
})
578+
}
579+
580+
func TestAccKmsCryptoKeyVersion_externalProtectionLevelOptionsVpc(t *testing.T) {
581+
// This test relies on manual steps to set up the EkmConnection used for the
582+
// CryptoKeyVersion creation, which means we can't spin up a temporary project.
583+
// We also can't use bootstrapped keys because that would defeat the purpose of
584+
// this key creation test, so we skip this test for VCR to avoid KMS resource
585+
// accumulation in the TF test project (since KMS resources can't be deleted).
586+
acctest.SkipIfVcr(t)
587+
t.Parallel()
588+
589+
projectId := envvar.GetTestProjectFromEnv()
590+
keyRingName := fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10))
591+
cryptoKeyName := fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10))
592+
ekmConnectionName := fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10))
593+
keyPath := "data.google_secret_manager_secret_version.key_path.secret_data"
594+
updatedKeyPath := "data.google_secret_manager_secret_version.key_path_updated.secret_data"
595+
596+
acctest.VcrTest(t, resource.TestCase{
597+
PreCheck: func() { acctest.AccTestPreCheck(t) },
598+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
599+
Steps: []resource.TestStep{
600+
{
601+
Config: testGoogleKmsCryptoKeyVersion_externalProtectionLevelOptionsVpc(projectId, keyRingName, cryptoKeyName, ekmConnectionName, keyPath),
602+
},
603+
{
604+
ResourceName: "google_kms_crypto_key_version.crypto_key_version",
605+
ImportState: true,
606+
ImportStateVerify: true,
607+
ImportStateVerifyIgnore: []string{"labels", "terraform_labels"},
608+
},
609+
{
610+
Config: testGoogleKmsCryptoKeyVersion_externalProtectionLevelOptionsVpc(projectId, keyRingName, cryptoKeyName, ekmConnectionName, updatedKeyPath),
611+
},
612+
{
613+
ResourceName: "google_kms_crypto_key_version.crypto_key_version",
614+
ImportState: true,
615+
ImportStateVerify: true,
616+
ImportStateVerifyIgnore: []string{"labels", "terraform_labels"},
617+
},
618+
},
619+
})
620+
}
621+
543622
// This test runs in its own project, otherwise the test project would start to get filled
544623
// with undeletable resources
545624
func testGoogleKmsCryptoKey_basic(projectId, projectOrg, projectBillingAccount, keyRingName, cryptoKeyName string) string {
@@ -955,3 +1034,147 @@ resource "google_kms_crypto_key_version" "crypto_key_version" {
9551034
}
9561035
`, projectId, projectId, projectOrg, projectBillingAccount, keyRingName, cryptoKeyName, preventDestroy, state)
9571036
}
1037+
1038+
func testGoogleKmsCryptoKeyVersion_externalProtectionLevelOptions(projectId, projectOrg, projectBillingAccount, keyRingName, cryptoKeyName, keyUri string) string {
1039+
return fmt.Sprintf(`
1040+
resource "google_project" "acceptance" {
1041+
name = "%s"
1042+
project_id = "%s"
1043+
org_id = "%s"
1044+
billing_account = "%s"
1045+
}
1046+
1047+
resource "google_project_service" "acceptance" {
1048+
project = google_project.acceptance.project_id
1049+
service = "cloudkms.googleapis.com"
1050+
}
1051+
1052+
resource "google_kms_key_ring" "key_ring" {
1053+
project = google_project_service.acceptance.project
1054+
name = "%s"
1055+
location = "us-central1"
1056+
}
1057+
1058+
resource "google_kms_crypto_key" "crypto_key" {
1059+
name = "%s"
1060+
key_ring = google_kms_key_ring.key_ring.id
1061+
1062+
version_template {
1063+
algorithm = "EXTERNAL_SYMMETRIC_ENCRYPTION"
1064+
protection_level = "EXTERNAL"
1065+
}
1066+
1067+
labels = {
1068+
key = "value"
1069+
}
1070+
skip_initial_version_creation = true
1071+
}
1072+
1073+
data "google_secret_manager_secret_version" "key_uri" {
1074+
secret = "external-full-key-uri"
1075+
project = "315636579862"
1076+
}
1077+
data "google_secret_manager_secret_version" "key_uri_updated" {
1078+
secret = "external-full-key-uri-update-test"
1079+
project = "315636579862"
1080+
}
1081+
1082+
resource "google_kms_crypto_key_version" "crypto_key_version" {
1083+
crypto_key = google_kms_crypto_key.crypto_key.id
1084+
external_protection_level_options {
1085+
external_key_uri = %s
1086+
}
1087+
}
1088+
`, projectId, projectId, projectOrg, projectBillingAccount, keyRingName, cryptoKeyName, keyUri)
1089+
}
1090+
1091+
// EkmConnection setup and creation is based off of resource_kms_ekm_connection_test.go
1092+
func testGoogleKmsCryptoKeyVersion_externalProtectionLevelOptionsVpc(projectId, keyRingName, cryptoKeyName, ekmConnectionName, keyPath string) string {
1093+
return fmt.Sprintf(`
1094+
data "google_project" "vpc-project" {
1095+
project_id = "cloud-ekm-refekm-playground"
1096+
}
1097+
data "google_project" "project" {
1098+
project_id = "%s"
1099+
}
1100+
1101+
data "google_secret_manager_secret_version" "raw_der" {
1102+
secret = "playground-cert"
1103+
project = "315636579862"
1104+
}
1105+
data "google_secret_manager_secret_version" "hostname" {
1106+
secret = "external-uri"
1107+
project = "315636579862"
1108+
}
1109+
data "google_secret_manager_secret_version" "servicedirectoryservice" {
1110+
secret = "external-servicedirectoryservice"
1111+
project = "315636579862"
1112+
}
1113+
1114+
resource "google_project_iam_member" "add_sdviewer" {
1115+
project = data.google_project.vpc-project.number
1116+
role = "roles/servicedirectory.viewer"
1117+
member = "serviceAccount:service-${data.google_project.project.number}@gcp-sa-ekms.iam.gserviceaccount.com"
1118+
}
1119+
resource "google_project_iam_member" "add_pscAuthorizedService" {
1120+
project = data.google_project.vpc-project.number
1121+
role = "roles/servicedirectory.pscAuthorizedService"
1122+
member = "serviceAccount:service-${data.google_project.project.number}@gcp-sa-ekms.iam.gserviceaccount.com"
1123+
}
1124+
1125+
resource "google_kms_ekm_connection" "example-ekmconnection" {
1126+
name = "%s"
1127+
location = "us-central1"
1128+
key_management_mode = "MANUAL"
1129+
service_resolvers {
1130+
service_directory_service = data.google_secret_manager_secret_version.servicedirectoryservice.secret_data
1131+
hostname = data.google_secret_manager_secret_version.hostname.secret_data
1132+
server_certificates {
1133+
raw_der = data.google_secret_manager_secret_version.raw_der.secret_data
1134+
}
1135+
}
1136+
depends_on = [
1137+
google_project_iam_member.add_pscAuthorizedService,
1138+
google_project_iam_member.add_sdviewer
1139+
]
1140+
}
1141+
1142+
resource "google_kms_key_ring" "key_ring" {
1143+
project = data.google_project.project.project_id
1144+
name = "%s"
1145+
location = "us-central1"
1146+
}
1147+
1148+
resource "google_kms_crypto_key" "crypto_key" {
1149+
name = "%s"
1150+
key_ring = google_kms_key_ring.key_ring.id
1151+
1152+
version_template {
1153+
algorithm = "EXTERNAL_SYMMETRIC_ENCRYPTION"
1154+
protection_level = "EXTERNAL_VPC"
1155+
}
1156+
1157+
labels = {
1158+
key = "value"
1159+
}
1160+
crypto_key_backend = google_kms_ekm_connection.example-ekmconnection.id
1161+
skip_initial_version_creation = true
1162+
}
1163+
1164+
data "google_secret_manager_secret_version" "key_path" {
1165+
secret = "external-keypath"
1166+
project = "315636579862"
1167+
}
1168+
data "google_secret_manager_secret_version" "key_path_updated" {
1169+
secret = "external-keypath-update-test"
1170+
project = "315636579862"
1171+
}
1172+
1173+
resource "google_kms_crypto_key_version" "crypto_key_version" {
1174+
crypto_key = google_kms_crypto_key.crypto_key.id
1175+
external_protection_level_options {
1176+
ekm_connection_key_path = %s
1177+
}
1178+
}
1179+
`, projectId, ekmConnectionName, keyRingName, cryptoKeyName, keyPath)
1180+
}

0 commit comments

Comments
 (0)