Skip to content

Commit

Permalink
Make sure KMS key "deletion" disables rotation (#705)
Browse files Browse the repository at this point in the history
Signed-off-by: Modular Magician <[email protected]>
  • Loading branch information
modular-magician authored and emilymye committed May 15, 2019
1 parent 0f0e5a3 commit da1e738
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 21 deletions.
34 changes: 26 additions & 8 deletions google-beta/resource_kms_crypto_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,12 +224,24 @@ func clearCryptoKeyVersions(cryptoKeyId *kmsCryptoKeyId, config *Config) error {
return nil
}

/*
Because KMS CryptoKey resources cannot be deleted on GCP, we are only going to remove it from state
and destroy all its versions, rendering the key useless for encryption and decryption of data.
Re-creation of this resource through Terraform will produce an error.
*/
func disableCryptoKeyRotation(cryptoKeyId *kmsCryptoKeyId, config *Config) error {
keyClient := config.clientKms.Projects.Locations.KeyRings.CryptoKeys
_, err := keyClient.Patch(cryptoKeyId.cryptoKeyId(), &cloudkms.CryptoKey{
NullFields: []string{"rotationPeriod", "nextRotationTime"},
}).
UpdateMask("rotationPeriod,nextRotationTime").Do()

return err
}

// Because KMS CryptoKey keys cannot be deleted (in GCP proper), we "delete"
// the key ring by
// a) marking all key versions for destruction (24hr soft-delete)
// b) disabling rotation of the key
// c) removing it from state
// This disables all usage of previous versions of the key and makes it
// generally useless for encryption and decryption of data.
// Re-creation of this resource through Terraform will produce an error.
func resourceKmsCryptoKeyDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

Expand All @@ -242,12 +254,18 @@ func resourceKmsCryptoKeyDelete(d *schema.ResourceData, meta interface{}) error
[WARNING] KMS CryptoKey resources cannot be deleted from GCP. The CryptoKey %s will be removed from Terraform state,
and all its CryptoKeyVersions will be destroyed, but it will still be present on the server.`, cryptoKeyId.cryptoKeyId())

err = clearCryptoKeyVersions(cryptoKeyId, config)

if err != nil {
// Delete all versions of the key
if err := clearCryptoKeyVersions(cryptoKeyId, config); err != nil {
return err
}

// Make sure automatic key rotation is disabled.
if err := disableCryptoKeyRotation(cryptoKeyId, config); err != nil {
return fmt.Errorf(
"While cryptoKeyVersions were cleared, Terraform was unable to disable automatic rotation of key due to an error: %s."+
"Please retry or manually disable automatic rotation to prevent creation of a new version of this key.", err)
}

d.SetId("")
return nil
}
Expand Down
44 changes: 31 additions & 13 deletions google-beta/resource_kms_crypto_key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ func TestAccKmsCryptoKey_basic(t *testing.T) {
Check: resource.ComposeTestCheckFunc(
testAccCheckGoogleKmsCryptoKeyWasRemovedFromState("google_kms_crypto_key.crypto_key"),
testAccCheckGoogleKmsCryptoKeyVersionsDestroyed(projectId, location, keyRingName, cryptoKeyName),
testAccCheckGoogleKmsCryptoKeyRotationDisabled(projectId, location, keyRingName, cryptoKeyName),
),
},
},
Expand Down Expand Up @@ -189,16 +190,15 @@ func TestAccKmsCryptoKey_rotation(t *testing.T) {
Check: resource.ComposeTestCheckFunc(
testAccCheckGoogleKmsCryptoKeyWasRemovedFromState("google_kms_crypto_key.crypto_key"),
testAccCheckGoogleKmsCryptoKeyVersionsDestroyed(projectId, location, keyRingName, cryptoKeyName),
testAccCheckGoogleKmsCryptoKeyRotationDisabled(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.
*/
// 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 {
return func(s *terraform.State) error {
_, ok := s.RootModule().Resources[resourceName]
Expand All @@ -211,11 +211,8 @@ func testAccCheckGoogleKmsCryptoKeyWasRemovedFromState(resourceName string) reso
}
}

/*
KMS KeyRings cannot be deleted. This ensures that the CryptoKey resource's CryptoKeyVersion
sub-resources were scheduled to be destroyed, rendering the key itself inoperable.
*/

// KMS KeyRings cannot be deleted. This ensures that the CryptoKey resource's CryptoKeyVersion
// sub-resources were scheduled to be destroyed, rendering the key itself inoperable.
func testAccCheckGoogleKmsCryptoKeyVersionsDestroyed(projectId, location, keyRingName, cryptoKeyName string) resource.TestCheckFunc {
return func(_ *terraform.State) error {
config := testAccProvider.Meta().(*Config)
Expand All @@ -239,10 +236,31 @@ func testAccCheckGoogleKmsCryptoKeyVersionsDestroyed(projectId, location, keyRin
}
}

/*
This test runs in its own project, otherwise the test project would start to get filled
with undeletable resources
*/
// KMS KeyRings cannot be deleted. This ensures that the CryptoKey autorotation
// was disabled to prevent more versions of the key from being created.
func testAccCheckGoogleKmsCryptoKeyRotationDisabled(projectId, location, keyRingName, cryptoKeyName string) resource.TestCheckFunc {
return func(_ *terraform.State) error {
config := testAccProvider.Meta().(*Config)
gcpResourceUri := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s", projectId, location, keyRingName, cryptoKeyName)

response, err := config.clientKms.Projects.Locations.KeyRings.CryptoKeys.Get(gcpResourceUri).Do()
if err != nil {
return fmt.Errorf("Unexpected failure while verifying 'deleted' crypto key: %s", err)
}

if response.NextRotationTime != "" {
return fmt.Errorf("Expected empty nextRotationTime for 'deleted' crypto key, got %s", response.NextRotationTime)
}
if response.RotationPeriod != "" {
return fmt.Errorf("Expected empty RotationPeriod for 'deleted' crypto key, got %s", response.RotationPeriod)
}

return nil
}
}

// This test runs in its own project, otherwise the test project would start to get filled
// with undeletable resources
func testGoogleKmsCryptoKey_basic(projectId, projectOrg, projectBillingAccount, keyRingName, cryptoKeyName string) string {
return fmt.Sprintf(`
resource "google_project" "acceptance" {
Expand Down

0 comments on commit da1e738

Please sign in to comment.