From 9cffe88760e65c6136da6c8ad3cd6d5b25f077aa Mon Sep 17 00:00:00 2001 From: The Magician Date: Wed, 25 Aug 2021 12:04:22 -0500 Subject: [PATCH] Add soft deletion retention to KMS key (#5131) (#9911) Signed-off-by: Modular Magician --- .changelog/5131.txt | 3 + google/resource_kms_crypto_key.go | 25 ++++++++ google/resource_kms_crypto_key_test.go | 66 +++++++++++++++++++++ website/docs/r/kms_crypto_key.html.markdown | 5 ++ 4 files changed, 99 insertions(+) create mode 100644 .changelog/5131.txt diff --git a/.changelog/5131.txt b/.changelog/5131.txt new file mode 100644 index 00000000000..1a1b68bffe6 --- /dev/null +++ b/.changelog/5131.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +kms: added support for `destroy_scheduled_duration` to `google_kms_crypto_key` +``` diff --git a/google/resource_kms_crypto_key.go b/google/resource_kms_crypto_key.go index b2ab2e789f5..fdaf2ddb9a6 100644 --- a/google/resource_kms_crypto_key.go +++ b/google/resource_kms_crypto_key.go @@ -68,6 +68,14 @@ Format: ''projects/{{project}}/locations/{{location}}/keyRings/{{keyRing}}''.`, ForceNew: true, Description: `The resource name for the CryptoKey.`, }, + "destroy_scheduled_duration": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + Description: `The period of time that versions of this key spend in the DESTROY_SCHEDULED state before transitioning to DESTROYED. +If not specified at creation time, the default duration is 24 hours.`, + }, "labels": { Type: schema.TypeMap, Optional: true, @@ -168,6 +176,12 @@ func resourceKMSCryptoKeyCreate(d *schema.ResourceData, meta interface{}) error } else if v, ok := d.GetOkExists("version_template"); !isEmptyValue(reflect.ValueOf(versionTemplateProp)) && (ok || !reflect.DeepEqual(v, versionTemplateProp)) { obj["versionTemplate"] = versionTemplateProp } + destroyScheduledDurationProp, err := expandKMSCryptoKeyDestroyScheduledDuration(d.Get("destroy_scheduled_duration"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("destroy_scheduled_duration"); !isEmptyValue(reflect.ValueOf(destroyScheduledDurationProp)) && (ok || !reflect.DeepEqual(v, destroyScheduledDurationProp)) { + obj["destroyScheduledDuration"] = destroyScheduledDurationProp + } obj, err = resourceKMSCryptoKeyEncoder(d, meta, obj) if err != nil { @@ -260,6 +274,9 @@ func resourceKMSCryptoKeyRead(d *schema.ResourceData, meta interface{}) error { if err := d.Set("version_template", flattenKMSCryptoKeyVersionTemplate(res["versionTemplate"], d, config)); err != nil { return fmt.Errorf("Error reading CryptoKey: %s", err) } + if err := d.Set("destroy_scheduled_duration", flattenKMSCryptoKeyDestroyScheduledDuration(res["destroyScheduledDuration"], d, config)); err != nil { + return fmt.Errorf("Error reading CryptoKey: %s", err) + } return nil } @@ -436,6 +453,10 @@ func flattenKMSCryptoKeyVersionTemplateProtectionLevel(v interface{}, d *schema. return v } +func flattenKMSCryptoKeyDestroyScheduledDuration(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + func expandKMSCryptoKeyLabels(v interface{}, d TerraformResourceData, config *Config) (map[string]string, error) { if v == nil { return map[string]string{}, nil @@ -489,6 +510,10 @@ func expandKMSCryptoKeyVersionTemplateProtectionLevel(v interface{}, d Terraform return v, nil } +func expandKMSCryptoKeyDestroyScheduledDuration(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + func resourceKMSCryptoKeyEncoder(d *schema.ResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) { // if rotationPeriod is set, nextRotationTime must also be set. if d.Get("rotation_period") != "" { diff --git a/google/resource_kms_crypto_key_test.go b/google/resource_kms_crypto_key_test.go index 8c96cb06767..a6bce4edafd 100644 --- a/google/resource_kms_crypto_key_test.go +++ b/google/resource_kms_crypto_key_test.go @@ -300,6 +300,41 @@ func TestAccKmsCryptoKey_template(t *testing.T) { }) } +func TestAccKmsCryptoKey_destroyDuration(t *testing.T) { + t.Parallel() + + projectId := fmt.Sprintf("tf-test-%d", randInt(t)) + projectOrg := getTestOrgFromEnv(t) + location := getTestRegionFromEnv() + projectBillingAccount := getTestBillingAccountFromEnv(t) + keyRingName := fmt.Sprintf("tf-test-%s", randString(t, 10)) + cryptoKeyName := fmt.Sprintf("tf-test-%s", randString(t, 10)) + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testGoogleKmsCryptoKey_destroyDuration(projectId, projectOrg, projectBillingAccount, keyRingName, cryptoKeyName), + }, + { + ResourceName: "google_kms_crypto_key.crypto_key", + ImportState: true, + ImportStateVerify: true, + }, + // Use a separate TestStep rather than a CheckDestroy because we need the project to still exist. + { + Config: testGoogleKmsCryptoKey_removed(projectId, projectOrg, projectBillingAccount, keyRingName), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleKmsCryptoKeyWasRemovedFromState("google_kms_crypto_key.crypto_key"), + testAccCheckGoogleKmsCryptoKeyVersionsDestroyed(t, projectId, location, keyRingName, cryptoKeyName), + testAccCheckGoogleKmsCryptoKeyRotationDisabled(t, projectId, location, keyRingName, cryptoKeyName), + ), + }, + }, + }) +} + // KMS KeyRings cannot be deleted. This ensures that the CryptoKey resource was removed from state, // even though the server-side resource was not removed. func testAccCheckGoogleKmsCryptoKeyWasRemovedFromState(resourceName string) resource.TestCheckFunc { @@ -502,3 +537,34 @@ resource "google_kms_key_ring" "key_ring" { } `, projectId, projectId, projectOrg, projectBillingAccount, keyRingName) } + +func testGoogleKmsCryptoKey_destroyDuration(projectId, projectOrg, projectBillingAccount, keyRingName, cryptoKeyName string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + name = "%s" + project_id = "%s" + org_id = "%s" + billing_account = "%s" +} + +resource "google_project_service" "acceptance" { + project = google_project.acceptance.project_id + service = "cloudkms.googleapis.com" +} + +resource "google_kms_key_ring" "key_ring" { + project = google_project_service.acceptance.project + name = "%s" + location = "us-central1" +} + +resource "google_kms_crypto_key" "crypto_key" { + name = "%s" + key_ring = google_kms_key_ring.key_ring.self_link + labels = { + key = "value" + } + destroy_scheduled_duration = "129600s" +} +`, projectId, projectId, projectOrg, projectBillingAccount, keyRingName, cryptoKeyName) +} diff --git a/website/docs/r/kms_crypto_key.html.markdown b/website/docs/r/kms_crypto_key.html.markdown index 81ee836018a..84db8b4ca36 100644 --- a/website/docs/r/kms_crypto_key.html.markdown +++ b/website/docs/r/kms_crypto_key.html.markdown @@ -125,6 +125,11 @@ The following arguments are supported: A template describing settings for new crypto key versions. Structure is documented below. +* `destroy_scheduled_duration` - + (Optional) + The period of time that versions of this key spend in the DESTROY_SCHEDULED state before transitioning to DESTROYED. + If not specified at creation time, the default duration is 24 hours. + * `skip_initial_version_creation` - (Optional) If set to true, the request will create a CryptoKey without any CryptoKeyVersions.