diff --git a/aws/data_source_aws_kms_key.go b/aws/data_source_aws_kms_key.go new file mode 100644 index 00000000000..35c231ead10 --- /dev/null +++ b/aws/data_source_aws_kms_key.go @@ -0,0 +1,110 @@ +package aws + +import ( + "fmt" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/kms" + "github.com/hashicorp/terraform/helper/schema" + "time" +) + +func dataSourceAwsKmsKey() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAwsKmsKeyRead, + Schema: map[string]*schema.Schema{ + "key_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateKmsKey, + }, + "grant_tokens": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "aws_account_id": { + Type: schema.TypeString, + Computed: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "deletion_date": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "enabled": { + Type: schema.TypeBool, + Computed: true, + }, + "expiration_model": { + Type: schema.TypeString, + Computed: true, + }, + "key_manager": { + Type: schema.TypeString, + Computed: true, + }, + "key_state": { + Type: schema.TypeString, + Computed: true, + }, + "key_usage": { + Type: schema.TypeString, + Computed: true, + }, + "origin": { + Type: schema.TypeString, + Computed: true, + }, + "valid_to": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceAwsKmsKeyRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).kmsconn + keyId := d.Get("key_id") + var grantTokens []*string + if v, ok := d.GetOk("grant_tokens"); ok { + grantTokens = aws.StringSlice(v.([]string)) + } + input := &kms.DescribeKeyInput{ + KeyId: aws.String(keyId.(string)), + GrantTokens: grantTokens, + } + output, err := conn.DescribeKey(input) + if err != nil { + return fmt.Errorf("error while describing key [%s]: %s", keyId, err) + } + d.SetId(aws.StringValue(output.KeyMetadata.KeyId)) + d.Set("arn", output.KeyMetadata.Arn) + d.Set("aws_account_id", output.KeyMetadata.AWSAccountId) + d.Set("creation_date", aws.TimeValue(output.KeyMetadata.CreationDate).Format(time.RFC3339)) + if output.KeyMetadata.DeletionDate != nil { + d.Set("deletion_date", aws.TimeValue(output.KeyMetadata.DeletionDate).Format(time.RFC3339)) + } + d.Set("description", output.KeyMetadata.Description) + d.Set("enabled", output.KeyMetadata.Enabled) + d.Set("expiration_model", output.KeyMetadata.ExpirationModel) + d.Set("key_manager", output.KeyMetadata.KeyManager) + d.Set("key_state", output.KeyMetadata.KeyState) + d.Set("key_usage", output.KeyMetadata.KeyUsage) + d.Set("origin", output.KeyMetadata.Origin) + if output.KeyMetadata.ValidTo != nil { + d.Set("valid_to", aws.TimeValue(output.KeyMetadata.ValidTo).Format(time.RFC3339)) + } + return nil +} diff --git a/aws/data_source_aws_kms_key_test.go b/aws/data_source_aws_kms_key_test.go new file mode 100644 index 00000000000..6bb239ceb27 --- /dev/null +++ b/aws/data_source_aws_kms_key_test.go @@ -0,0 +1,75 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "regexp" +) + +func TestAccDataSourceAwsKmsKey_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAwsKmsKeyConfig, + 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"), + ), + }, + }, + }) +} + +func testAccDataSourceAwsKmsKeyCheck(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, 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 +} + +data "aws_kms_key" "arbitrary" { + key_id = "${aws_kms_key.arbitrary.key_id}" +}` diff --git a/aws/provider.go b/aws/provider.go index 3f328c6761b..c1a27712def 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -207,6 +207,7 @@ func Provider() terraform.ResourceProvider { "aws_kinesis_stream": dataSourceAwsKinesisStream(), "aws_kms_alias": dataSourceAwsKmsAlias(), "aws_kms_ciphertext": dataSourceAwsKmsCiphertext(), + "aws_kms_key": dataSourceAwsKmsKey(), "aws_kms_secret": dataSourceAwsKmsSecret(), "aws_nat_gateway": dataSourceAwsNatGateway(), "aws_network_interface": dataSourceAwsNetworkInterface(), diff --git a/aws/validators.go b/aws/validators.go index 3199e3229e9..086b5008d91 100644 --- a/aws/validators.go +++ b/aws/validators.go @@ -2144,6 +2144,22 @@ func validateDxConnectionBandWidth(v interface{}, k string) (ws []string, errors return } +func validateKmsKey(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + arnPrefixPattern := `arn:[^:]+:kms:[^:]+:[^:]+:` + keyIdPattern := "[A-Za-z0-9-]+" + keyArnPattern := arnPrefixPattern + "key/" + keyIdPattern + aliasNamePattern := "alias/[a-zA-Z0-9:/_-]+" + aliasArnPattern := arnPrefixPattern + aliasNamePattern + if !regexp.MustCompile(fmt.Sprintf("^%s$", keyIdPattern)).MatchString(value) && + !regexp.MustCompile(fmt.Sprintf("^%s$", keyArnPattern)).MatchString(value) && + !regexp.MustCompile(fmt.Sprintf("^%s$", aliasNamePattern)).MatchString(value) && + !regexp.MustCompile(fmt.Sprintf("^%s$", aliasArnPattern)).MatchString(value) { + errors = append(errors, fmt.Errorf("%q must be one of the following patterns: %s, %s, %s or %s", k, keyIdPattern, keyArnPattern, aliasNamePattern, aliasArnPattern)) + } + return +} + func validateAwsElastiCacheReplicationGroupAuthToken(v interface{}, k string) (ws []string, errors []error) { value := v.(string) if (len(value) < 16) || (len(value) > 128) { diff --git a/aws/validators_test.go b/aws/validators_test.go index d9376d301a6..c2459c4fd1e 100644 --- a/aws/validators_test.go +++ b/aws/validators_test.go @@ -2888,6 +2888,53 @@ func TestValidateDxConnectionBandWidth(t *testing.T) { } } +func TestValidateKmsKey(t *testing.T) { + cases := []struct { + Value string + ErrCount int + }{ + { + Value: "arbitrary-uuid-1234", + ErrCount: 0, + }, + { + Value: "arn:aws:kms:us-west-2:111122223333:key/arbitrary-uuid-1234", + ErrCount: 0, + }, + { + Value: "alias/arbitrary-key", + ErrCount: 0, + }, + { + Value: "alias/arbitrary/key", + ErrCount: 0, + }, + { + Value: "arn:aws:kms:us-west-2:111122223333:alias/arbitrary-key", + ErrCount: 0, + }, + { + Value: "arn:aws:kms:us-west-2:111122223333:alias/arbitrary/key", + ErrCount: 0, + }, + { + Value: "$%wrongkey", + ErrCount: 1, + }, + { + Value: "arn:aws:lamda:foo:bar:key/xyz", + ErrCount: 1, + }, + } + + for _, tc := range cases { + _, errors := validateKmsKey(tc.Value, "key_id") + if len(errors) != tc.ErrCount { + t.Fatalf("%q validation failed: %v", tc.Value, errors) + } + } +} + func TestValidateCognitoUserPoolReplyEmailAddress(t *testing.T) { validTypes := []string{ "foo@gmail.com", diff --git a/website/aws.erb b/website/aws.erb index 235f8f196c1..21abb5a72a1 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -172,6 +172,9 @@ > aws_kms_alias + > + aws_kms_key + > aws_kms_ciphertext diff --git a/website/docs/d/kms_key.html.markdown b/website/docs/d/kms_key.html.markdown new file mode 100644 index 00000000000..3ef84638046 --- /dev/null +++ b/website/docs/d/kms_key.html.markdown @@ -0,0 +1,59 @@ +--- +layout: "aws" +page_title: "AWS: aws_kms_key" +sidebar_current: "docs-aws-datasource-kms-key" +description: |- + Get information on a AWS Key Management Service (KMS) Key +--- + +# aws_kms_key + +Use this data source to get detailed information about +the specified KMS Key with flexible key id input. +This can be useful to reference key alias +without having to hard code the ARN as input. + +## Example Usage + +```hcl +data "aws_kms_key" "foo" { + key_id = "alias/my-key" +} + +data "aws_kms_key" "foo" { + key_id = "1234abcd-12ab-34cd-56ef-1234567890ab" +} + +data "aws_kms_key" "foo" { + key_id = "arn:aws:kms:us-east-1:111122223333:alias/my-key" +} + +data "aws_kms_key" "foo" { + key_id = "arn:aws:kms:us-east-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab" +} +``` + +## Argument Reference + +* `key_id` - (Required) Key identifier which can be one of the following format: + * Key ID. E.g: `1234abcd-12ab-34cd-56ef-1234567890ab` + * Key ARN. E.g.: `arn:aws:kms:us-east-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab` + * Alias name. E.g.: `alias/my-key` + * Alias ARN: E.g.: `arn:aws:kms:us-east-1:111122223333:alias/my-key` +* `grant_tokens` - (Optional) List of grant tokens + +## Attributes Reference + +* `id`: The globally unique identifier for the key +* `arn`: The Amazon Resource Name (ARN) of the key +* `aws_account_id`: The twelve-digit account ID of the AWS account that owns the key +* `creation_date`: The date and time when the key was created +* `deletion_date`: The date and time after which AWS KMS deletes the key. This value is present only when `key_state` is `PendingDeletion`, otherwise this value is 0 +* `description`: The description of the key. +* `enabled`: Specifies whether the key is enabled. When `key_state` is `Enabled` this value is true, otherwise it is false +* `expiration_model`: Specifies whether the Key's key material expires. This value is present only when `origin` is `EXTERNAL`, otherwise this value is empty +* `key_manager`: The key's manager +* `key_state`: The state of the key +* `key_usage`: Currently the only allowed value is `ENCRYPT_DECRYPT` +* `origin`: When this value is `AWS_KMS`, AWS KMS created the key material. When this value is `EXTERNAL`, the key material was imported from your existing key management infrastructure or the CMK lacks key material +* `valid_to`: The time at which the imported key material expires. This value is present only when `origin` is `EXTERNAL` and whose `expiration_model` is `KEY_MATERIAL_EXPIRES`, otherwise this value is 0 \ No newline at end of file