Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

r/kms_key - correctly handle PendingDeletion state #19967

Merged
merged 5 commits into from
Jul 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/19967.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_kms_key: Add plan time validation to `description`.
```
60 changes: 23 additions & 37 deletions aws/resource_aws_kms_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/kms"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
Expand Down Expand Up @@ -40,35 +39,24 @@ func resourceAwsKmsKey() *schema.Resource {
Computed: true,
},
"description": {
Type: schema.TypeString,
Optional: true,
Computed: true,
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: validation.StringLenBetween(0, 8192),
},
"key_usage": {
Type: schema.TypeString,
Optional: true,
Default: kms.KeyUsageTypeEncryptDecrypt,
ForceNew: true,
ValidateFunc: validation.StringInSlice([]string{
kms.KeyUsageTypeEncryptDecrypt,
kms.KeyUsageTypeSignVerify,
}, false),
Type: schema.TypeString,
Optional: true,
Default: kms.KeyUsageTypeEncryptDecrypt,
ForceNew: true,
ValidateFunc: validation.StringInSlice(kms.KeyUsageType_Values(), false),
},
"customer_master_key_spec": {
Type: schema.TypeString,
Optional: true,
Default: kms.CustomerMasterKeySpecSymmetricDefault,
ForceNew: true,
ValidateFunc: validation.StringInSlice([]string{
kms.CustomerMasterKeySpecSymmetricDefault,
kms.CustomerMasterKeySpecRsa2048,
kms.CustomerMasterKeySpecRsa3072,
kms.CustomerMasterKeySpecRsa4096,
kms.CustomerMasterKeySpecEccNistP256,
kms.CustomerMasterKeySpecEccNistP384,
kms.CustomerMasterKeySpecEccNistP521,
kms.CustomerMasterKeySpecEccSecgP256k1,
}, false),
Type: schema.TypeString,
Optional: true,
Default: kms.CustomerMasterKeySpecSymmetricDefault,
ForceNew: true,
ValidateFunc: validation.StringInSlice(kms.CustomerMasterKeySpec_Values(), false),
},
"policy": {
Type: schema.TypeString,
Expand Down Expand Up @@ -210,7 +198,7 @@ func resourceAwsKmsKeyRead(d *schema.ResourceData, meta interface{}) error {
p := pOut.(*kms.GetKeyPolicyOutput)
policy, err := structure.NormalizeJsonString(*p.Policy)
if err != nil {
return fmt.Errorf("policy contains an invalid JSON: %s", err)
return fmt.Errorf("policy contains an invalid JSON: %w", err)
}
d.Set("policy", policy)

Expand Down Expand Up @@ -246,7 +234,7 @@ func resourceAwsKmsKeyRead(d *schema.ResourceData, meta interface{}) error {
}

if err != nil {
return fmt.Errorf("error listing tags for KMS Key (%s): %s", d.Id(), err)
return fmt.Errorf("error listing tags for KMS Key (%s): %w", d.Id(), err)
}

tags = tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig)
Expand Down Expand Up @@ -302,7 +290,7 @@ func resourceAwsKmsKeyUpdate(d *schema.ResourceData, meta interface{}) error {
o, n := d.GetChange("tags_all")

if err := keyvaluetags.KmsUpdateTags(conn, d.Id(), o, n); err != nil {
return fmt.Errorf("error updating KMS Key (%s) tags: %s", d.Id(), err)
return fmt.Errorf("error updating KMS Key (%s) tags: %w", d.Id(), err)
}
}

Expand All @@ -327,7 +315,7 @@ func resourceAwsKmsKeyDescriptionUpdate(conn *kms.KMS, d *schema.ResourceData) e
func resourceAwsKmsKeyPolicyUpdate(conn *kms.KMS, d *schema.ResourceData) error {
policy, err := structure.NormalizeJsonString(d.Get("policy").(string))
if err != nil {
return fmt.Errorf("policy contains an invalid JSON: %s", err)
return fmt.Errorf("policy contains an invalid JSON: %w", err)
}
keyId := d.Get("key_id").(string)

Expand Down Expand Up @@ -377,8 +365,7 @@ func updateKmsKeyStatus(conn *kms.KMS, id string, shouldBeEnabled bool) error {
KeyId: aws.String(id),
})
if err != nil {
awsErr, ok := err.(awserr.Error)
if ok && awsErr.Code() == "NotFoundException" {
if isAWSErr(err, kms.ErrCodeNotFoundException, "") {
return nil, fmt.Sprintf("%t", !shouldBeEnabled), nil
}
return resp, "FAILED", err
Expand All @@ -392,7 +379,7 @@ func updateKmsKeyStatus(conn *kms.KMS, id string, shouldBeEnabled bool) error {

_, err = wait.WaitForState()
if err != nil {
return fmt.Errorf("Failed setting KMS key status to %t: %s", shouldBeEnabled, err)
return fmt.Errorf("Failed setting KMS key status to %t: %w", shouldBeEnabled, err)
}

return nil
Expand All @@ -405,11 +392,10 @@ func updateKmsKeyRotationStatus(conn *kms.KMS, d *schema.ResourceData) error {
err := handleKeyRotation(conn, shouldEnableRotation, aws.String(d.Id()))

if err != nil {
awsErr, ok := err.(awserr.Error)
if ok && awsErr.Code() == "DisabledException" {
if isAWSErr(err, kms.ErrCodeDisabledException, "") {
return resource.RetryableError(err)
}
if ok && awsErr.Code() == "NotFoundException" {
if isAWSErr(err, kms.ErrCodeNotFoundException, "") {
return resource.RetryableError(err)
}

Expand Down Expand Up @@ -438,7 +424,7 @@ func updateKmsKeyRotationStatus(conn *kms.KMS, d *schema.ResourceData) error {
log.Printf("[DEBUG] Checking if KMS key %s rotation status is %t",
d.Id(), shouldEnableRotation)

out, err := retryOnAwsCode("NotFoundException", func() (interface{}, error) {
out, err := retryOnAwsCode(kms.ErrCodeNotFoundException, func() (interface{}, error) {
return conn.GetKeyRotationStatus(&kms.GetKeyRotationStatusInput{
KeyId: aws.String(d.Id()),
})
Expand Down
39 changes: 15 additions & 24 deletions aws/resource_aws_kms_key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,34 +23,37 @@ func init() {
func testSweepKmsKeys(region string) error {
client, err := sharedClientForRegion(region)
if err != nil {
return fmt.Errorf("error getting client: %s", err)
return fmt.Errorf("error getting client: %w", err)
}
conn := client.(*AWSClient).kmsconn

err = conn.ListKeysPages(&kms.ListKeysInput{Limit: aws.Int64(int64(1000))}, func(out *kms.ListKeysOutput, lastPage bool) bool {
for _, k := range out.Keys {
kKeyId := aws.StringValue(k.KeyId)
kOut, err := conn.DescribeKey(&kms.DescribeKeyInput{
KeyId: k.KeyId,
})
if err != nil {
log.Printf("Error: Failed to describe key %q: %s", *k.KeyId, err)
log.Printf("Error: Failed to describe key %q: %s", kKeyId, err)
return false
}
if *kOut.KeyMetadata.KeyManager == kms.KeyManagerTypeAws {
if aws.StringValue(kOut.KeyMetadata.KeyManager) == kms.KeyManagerTypeAws {
// Skip (default) keys which are managed by AWS
continue
}
if *kOut.KeyMetadata.KeyState == kms.KeyStatePendingDeletion {
if aws.StringValue(kOut.KeyMetadata.KeyState) == kms.KeyStatePendingDeletion {
// Skip keys which are already scheduled for deletion
continue
}

_, err = conn.ScheduleKeyDeletion(&kms.ScheduleKeyDeletionInput{
KeyId: k.KeyId,
PendingWindowInDays: aws.Int64(int64(7)),
})
r := resourceAwsKmsKey()
d := r.Data(nil)
d.SetId(kKeyId)
d.Set("key_id", kKeyId)
d.Set("deletion_window_in_days", "7")
err = r.Delete(d, client)
if err != nil {
log.Printf("Error: Failed to schedule key %q for deletion: %s", *k.KeyId, err)
log.Printf("Error: Failed to schedule key %q for deletion: %s", kKeyId, err)
return false
}
}
Expand All @@ -61,7 +64,7 @@ func testSweepKmsKeys(region string) error {
log.Printf("[WARN] Skipping KMS Key sweep for %s: %s", region, err)
return nil
}
return fmt.Errorf("Error describing KMS keys: %s", err)
return fmt.Errorf("Error describing KMS keys: %w", err)
}

return nil
Expand All @@ -84,6 +87,7 @@ func TestAccAWSKmsKey_basic(t *testing.T) {
testAccCheckAWSKmsKeyExists(resourceName, &key),
resource.TestCheckResourceAttr(resourceName, "customer_master_key_spec", "SYMMETRIC_DEFAULT"),
resource.TestCheckResourceAttr(resourceName, "key_usage", "ENCRYPT_DECRYPT"),
resource.TestCheckResourceAttr(resourceName, "tags.%", "0"),
),
},
{
Expand Down Expand Up @@ -134,7 +138,7 @@ func TestAccAWSKmsKey_disappears(t *testing.T) {
Config: testAccAWSKmsKey(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSKmsKeyExists(resourceName, &key),
testAccCheckAWSKmsKeyDisappears(&key),
testAccCheckResourceDisappears(testAccProvider, resourceAwsKmsKey(), resourceName),
),
ExpectNonEmptyPlan: true,
},
Expand Down Expand Up @@ -417,19 +421,6 @@ func testAccCheckAWSKmsKeyIsEnabled(key *kms.KeyMetadata, isEnabled bool) resour
}
}

func testAccCheckAWSKmsKeyDisappears(key *kms.KeyMetadata) resource.TestCheckFunc {
return func(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).kmsconn

_, err := conn.ScheduleKeyDeletion(&kms.ScheduleKeyDeletionInput{
KeyId: key.KeyId,
PendingWindowInDays: aws.Int64(int64(7)),
})

return err
}
}

func testAccAWSKmsKey(rName string) string {
return fmt.Sprintf(`
resource "aws_kms_key" "test" {
Expand Down
2 changes: 1 addition & 1 deletion website/docs/r/kms_key.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ description: |-

# Resource: aws_kms_key

Provides a KMS customer master key.
Provides a KMS single-Region customer master key (CMK).

## Example Usage

Expand Down