Skip to content

Commit

Permalink
service/kms: Support asymmetric keys (hashicorp#11062)
Browse files Browse the repository at this point in the history
Output from acceptance testing:

```
--- PASS: TestAccDataSourceAwsKmsKey_basic (28.69s)

--- PASS: TestAccAWSKmsKey_disappears (7.85s)
--- PASS: TestAccAWSKmsKey_asymmetricKey (28.29s)
--- PASS: TestAccAWSKmsKey_basic (28.83s)
--- PASS: TestAccAWSKmsKey_tags (33.60s)
--- PASS: TestAccAWSKmsKey_policy (34.15s)
--- PASS: TestAccAWSKmsKey_isEnabled (311.63s)
```
  • Loading branch information
Kit Ewbank authored and jaspervdj-luminal committed Feb 6, 2020
1 parent cc66ec3 commit 638636e
Show file tree
Hide file tree
Showing 6 changed files with 245 additions and 169 deletions.
5 changes: 5 additions & 0 deletions aws/data_source_aws_kms_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ func dataSourceAwsKmsKey() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"customer_master_key_spec": {
Type: schema.TypeString,
Computed: true,
},
"origin": {
Type: schema.TypeString,
Computed: true,
Expand Down Expand Up @@ -102,6 +106,7 @@ func dataSourceAwsKmsKeyRead(d *schema.ResourceData, meta interface{}) error {
d.Set("key_manager", output.KeyMetadata.KeyManager)
d.Set("key_state", output.KeyMetadata.KeyState)
d.Set("key_usage", output.KeyMetadata.KeyUsage)
d.Set("customer_master_key_spec", output.KeyMetadata.CustomerMasterKeySpec)
d.Set("origin", output.KeyMetadata.Origin)
if output.KeyMetadata.ValidTo != nil {
d.Set("valid_to", aws.TimeValue(output.KeyMetadata.ValidTo).Format(time.RFC3339))
Expand Down
73 changes: 30 additions & 43 deletions aws/data_source_aws_kms_key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,34 @@ import (
"fmt"
"testing"

"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"regexp"
"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/terraform"
)

func TestAccDataSourceAwsKmsKey_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
resourceName := "aws_kms_key.test"
datasourceName := "data.aws_kms_key.test"
rName := fmt.Sprintf("tf-testacc-kms-key-%s", acctest.RandStringFromCharSet(13, acctest.CharSetAlphaNum))

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccDataSourceAwsKmsKeyConfig,
Config: testAccDataSourceAwsKmsKeyConfig(rName),
Check: resource.ComposeTestCheckFunc(
testAccDataSourceAwsKmsKeyCheck("data.aws_kms_key.arbitrary"),
resource.TestMatchResourceAttr("data.aws_kms_key.arbitrary", "arn", regexp.MustCompile("^arn:[^:]+:kms:[^:]+:[^:]+:key/.+")),
resource.TestCheckResourceAttrSet("data.aws_kms_key.arbitrary", "aws_account_id"),
resource.TestCheckResourceAttrSet("data.aws_kms_key.arbitrary", "creation_date"),
resource.TestCheckResourceAttr("data.aws_kms_key.arbitrary", "description", "Terraform acc test"),
resource.TestCheckResourceAttr("data.aws_kms_key.arbitrary", "enabled", "true"),
resource.TestCheckResourceAttrSet("data.aws_kms_key.arbitrary", "key_manager"),
resource.TestCheckResourceAttrSet("data.aws_kms_key.arbitrary", "key_state"),
resource.TestCheckResourceAttr("data.aws_kms_key.arbitrary", "key_usage", "ENCRYPT_DECRYPT"),
resource.TestCheckResourceAttrSet("data.aws_kms_key.arbitrary", "origin"),
testAccDataSourceAwsKmsKeyCheck(datasourceName),
resource.TestCheckResourceAttrPair(datasourceName, "arn", resourceName, "arn"),
resource.TestCheckResourceAttrPair(datasourceName, "customer_master_key_spec", resourceName, "customer_master_key_spec"),
resource.TestCheckResourceAttrPair(datasourceName, "description", resourceName, "description"),
resource.TestCheckResourceAttrPair(datasourceName, "enabled", resourceName, "is_enabled"),
resource.TestCheckResourceAttrPair(datasourceName, "key_usage", resourceName, "key_usage"),
resource.TestCheckResourceAttrSet(datasourceName, "aws_account_id"),
resource.TestCheckResourceAttrSet(datasourceName, "creation_date"),
resource.TestCheckResourceAttrSet(datasourceName, "key_manager"),
resource.TestCheckResourceAttrSet(datasourceName, "key_state"),
resource.TestCheckResourceAttrSet(datasourceName, "origin"),
),
},
},
Expand All @@ -35,41 +40,23 @@ func TestAccDataSourceAwsKmsKey_basic(t *testing.T) {

func testAccDataSourceAwsKmsKeyCheck(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]
_, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("root module has no resource called %s", name)
}

kmsKeyRs, ok := s.RootModule().Resources["aws_kms_key.arbitrary"]
if !ok {
return fmt.Errorf("can't find aws_kms_key.arbitrary in state")
}

attr := rs.Primary.Attributes

checkProperties := []string{"arn", "key_usage", "description"}

for _, p := range checkProperties {
if attr[p] != kmsKeyRs.Primary.Attributes[p] {
return fmt.Errorf(
"%s is %s; want %s",
p,
attr[p],
kmsKeyRs.Primary.Attributes[p],
)
}
}

return nil
}
}

const testAccDataSourceAwsKmsKeyConfig = `
resource "aws_kms_key" "arbitrary" {
description = "Terraform acc test"
deletion_window_in_days = 7
func testAccDataSourceAwsKmsKeyConfig(rName string) string {
return fmt.Sprintf(`
resource "aws_kms_key" "test" {
description = %[1]q
deletion_window_in_days = 7
}
data "aws_kms_key" "arbitrary" {
key_id = "${aws_kms_key.arbitrary.key_id}"
}`
data "aws_kms_key" "test" {
key_id = "${aws_kms_key.test.key_id}"
}`, rName)
}
52 changes: 35 additions & 17 deletions aws/resource_aws_kms_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,27 @@ func resourceAwsKmsKey() *schema.Resource {
"key_usage": {
Type: schema.TypeString,
Optional: true,
Computed: true,
Default: kms.KeyUsageTypeEncryptDecrypt,
ForceNew: true,
ValidateFunc: validation.StringInSlice([]string{
"",
kms.KeyUsageTypeEncryptDecrypt,
kms.KeyUsageTypeSignVerify,
}, 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),
},
"policy": {
Expand Down Expand Up @@ -87,13 +103,13 @@ func resourceAwsKmsKeyCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).kmsconn

// Allow aws to chose default values if we don't pass them
var req kms.CreateKeyInput
req := &kms.CreateKeyInput{
CustomerMasterKeySpec: aws.String(d.Get("customer_master_key_spec").(string)),
KeyUsage: aws.String(d.Get("key_usage").(string)),
}
if v, exists := d.GetOk("description"); exists {
req.Description = aws.String(v.(string))
}
if v, exists := d.GetOk("key_usage"); exists {
req.KeyUsage = aws.String(v.(string))
}
if v, exists := d.GetOk("policy"); exists {
req.Policy = aws.String(v.(string))
}
Expand All @@ -108,17 +124,20 @@ func resourceAwsKmsKeyCreate(d *schema.ResourceData, meta interface{}) error {
// http://docs.aws.amazon.com/kms/latest/APIReference/API_CreateKey.html
err := resource.Retry(30*time.Second, func() *resource.RetryError {
var err error
resp, err = conn.CreateKey(&req)
if isAWSErr(err, "MalformedPolicyDocumentException", "") {
resp, err = conn.CreateKey(req)
if isAWSErr(err, kms.ErrCodeMalformedPolicyDocumentException, "") {
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
})
if isResourceTimeoutError(err) {
resp, err = conn.CreateKey(req)
}
if err != nil {
return err
}

d.SetId(*resp.KeyMetadata.KeyId)
d.SetId(aws.StringValue(resp.KeyMetadata.KeyId))
d.Set("key_id", resp.KeyMetadata.KeyId)

return resourceAwsKmsKeyUpdate(d, meta)
Expand All @@ -135,7 +154,7 @@ func resourceAwsKmsKeyRead(d *schema.ResourceData, meta interface{}) error {
var err error
if d.IsNewResource() {
var out interface{}
out, err = retryOnAwsCode("NotFoundException", func() (interface{}, error) {
out, err = retryOnAwsCode(kms.ErrCodeNotFoundException, func() (interface{}, error) {
return conn.DescribeKey(req)
})
resp, _ = out.(*kms.DescribeKeyOutput)
Expand All @@ -147,23 +166,22 @@ func resourceAwsKmsKeyRead(d *schema.ResourceData, meta interface{}) error {
}
metadata := resp.KeyMetadata

if *metadata.KeyState == "PendingDeletion" {
if aws.StringValue(metadata.KeyState) == kms.KeyStatePendingDeletion {
log.Printf("[WARN] Removing KMS key %s because it's already gone", d.Id())
d.SetId("")
return nil
}

d.SetId(*metadata.KeyId)

d.Set("arn", metadata.Arn)
d.Set("key_id", metadata.KeyId)
d.Set("description", metadata.Description)
d.Set("key_usage", metadata.KeyUsage)
d.Set("customer_master_key_spec", metadata.CustomerMasterKeySpec)
d.Set("is_enabled", metadata.Enabled)

pOut, err := retryOnAwsCode("NotFoundException", func() (interface{}, error) {
pOut, err := retryOnAwsCode(kms.ErrCodeNotFoundException, func() (interface{}, error) {
return conn.GetKeyPolicy(&kms.GetKeyPolicyInput{
KeyId: metadata.KeyId,
KeyId: aws.String(d.Id()),
PolicyName: aws.String("default"),
})
})
Expand Down Expand Up @@ -469,8 +487,8 @@ func resourceAwsKmsKeyDelete(d *schema.ResourceData, meta interface{}) error {

// Wait for propagation since KMS is eventually consistent
wait := resource.StateChangeConf{
Pending: []string{"Enabled", "Disabled"},
Target: []string{"PendingDeletion"},
Pending: []string{kms.KeyStateEnabled, kms.KeyStateDisabled},
Target: []string{kms.KeyStatePendingDeletion},
Timeout: 20 * time.Minute,
MinTimeout: 2 * time.Second,
ContinuousTargetOccurence: 10,
Expand Down
Loading

0 comments on commit 638636e

Please sign in to comment.