From 7903c329763075cf62440e037c3ef7febe9bb255 Mon Sep 17 00:00:00 2001 From: saravanan30erd Date: Fri, 26 Oct 2018 15:57:33 +0400 Subject: [PATCH 1/3] add name_prefix in aws_secretsmanager_secret --- aws/resource_aws_secretsmanager_secret.go | 24 +++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/aws/resource_aws_secretsmanager_secret.go b/aws/resource_aws_secretsmanager_secret.go index 947d2668779d..b6b41e01f940 100644 --- a/aws/resource_aws_secretsmanager_secret.go +++ b/aws/resource_aws_secretsmanager_secret.go @@ -37,9 +37,16 @@ func resourceAwsSecretsManagerSecret() *schema.Resource { Optional: true, }, "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"name_prefix"}, + }, + "name_prefix": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"name"}, }, "policy": { Type: schema.TypeString, @@ -92,9 +99,18 @@ func resourceAwsSecretsManagerSecret() *schema.Resource { func resourceAwsSecretsManagerSecretCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).secretsmanagerconn + var secretName string + if v, ok := d.GetOk("name"); ok { + secretName = v.(string) + } else if v, ok := d.GetOk("name_prefix"); ok { + secretName = resource.PrefixedUniqueId(v.(string)) + } else { + secretName = resource.UniqueId() + } + input := &secretsmanager.CreateSecretInput{ Description: aws.String(d.Get("description").(string)), - Name: aws.String(d.Get("name").(string)), + Name: aws.String(secretName), } if v, ok := d.GetOk("kms_key_id"); ok && v.(string) != "" { From 22c8cc5632c3ef7693f93113a59f5954a42ab33a Mon Sep 17 00:00:00 2001 From: saravanan30erd Date: Fri, 26 Oct 2018 16:29:13 +0400 Subject: [PATCH 2/3] add acceptance test and update documentation --- aws/resource_aws_secretsmanager_secret.go | 2 ++ ...resource_aws_secretsmanager_secret_test.go | 36 +++++++++++++++++++ .../r/secretsmanager_secret.html.markdown | 3 +- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/aws/resource_aws_secretsmanager_secret.go b/aws/resource_aws_secretsmanager_secret.go index b6b41e01f940..064402ad42d2 100644 --- a/aws/resource_aws_secretsmanager_secret.go +++ b/aws/resource_aws_secretsmanager_secret.go @@ -39,12 +39,14 @@ func resourceAwsSecretsManagerSecret() *schema.Resource { "name": { Type: schema.TypeString, Optional: true, + Computed: true, ForceNew: true, ConflictsWith: []string{"name_prefix"}, }, "name_prefix": { Type: schema.TypeString, Optional: true, + Computed: true, ForceNew: true, ConflictsWith: []string{"name"}, }, diff --git a/aws/resource_aws_secretsmanager_secret_test.go b/aws/resource_aws_secretsmanager_secret_test.go index c991202d5c97..cc069064b880 100644 --- a/aws/resource_aws_secretsmanager_secret_test.go +++ b/aws/resource_aws_secretsmanager_secret_test.go @@ -103,6 +103,34 @@ func TestAccAwsSecretsManagerSecret_Basic(t *testing.T) { }) } +func TestAccAwsSecretsManagerSecret_withNamePrefix(t *testing.T) { + var secret secretsmanager.DescribeSecretOutput + rName := "tf-acc-test-" + resourceName := "aws_secretsmanager_secret.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsSecretsManagerSecretDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsSecretsManagerSecretConfig_withNamePrefix(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecretsManagerSecretExists(resourceName, &secret), + resource.TestMatchResourceAttr(resourceName, "arn", regexp.MustCompile(fmt.Sprintf("^arn:[^:]+:secretsmanager:[^:]+:[^:]+:secret:%s.+$", rName))), + resource.TestMatchResourceAttr(resourceName, "name", regexp.MustCompile(fmt.Sprintf("^%s", rName))), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"recovery_window_in_days", "name_prefix"}, + }, + }, + }) +} + func TestAccAwsSecretsManagerSecret_Description(t *testing.T) { var secret secretsmanager.DescribeSecretOutput rName := acctest.RandomWithPrefix("tf-acc-test") @@ -495,6 +523,14 @@ resource "aws_secretsmanager_secret" "test" { `, rName) } +func testAccAwsSecretsManagerSecretConfig_withNamePrefix(rName string) string { + return fmt.Sprintf(` +resource "aws_secretsmanager_secret" "test" { + name_prefix = "%s" +} +`, rName) +} + func testAccAwsSecretsManagerSecretConfig_KmsKeyID(rName string) string { return fmt.Sprintf(` resource "aws_kms_key" "test1" { diff --git a/website/docs/r/secretsmanager_secret.html.markdown b/website/docs/r/secretsmanager_secret.html.markdown index 61e36665de04..e73ce86ca120 100644 --- a/website/docs/r/secretsmanager_secret.html.markdown +++ b/website/docs/r/secretsmanager_secret.html.markdown @@ -43,7 +43,8 @@ resource "aws_secretsmanager_secret" "rotation-example" { The following arguments are supported: -* `name` - (Required) Specifies the friendly name of the new secret. The secret name can consist of uppercase letters, lowercase letters, digits, and any of the following characters: `/_+=.@-` Spaces are not permitted. +* `name` - (Optional) Specifies the friendly name of the new secret. The secret name can consist of uppercase letters, lowercase letters, digits, and any of the following characters: `/_+=.@-` Conflicts with `name_prefix`. +* `name_prefix` - (Optional) Creates a unique name beginning with the specified prefix. Conflicts with `name`. * `description` - (Optional) A description of the secret. * `kms_key_id` - (Optional) Specifies the ARN or alias of the AWS KMS customer master key (CMK) to be used to encrypt the secret values in the versions stored in this secret. If you don't specify this value, then Secrets Manager defaults to using the AWS account's default CMK (the one named `aws/secretsmanager`). If the default KMS CMK with that name doesn't yet exist, then AWS Secrets Manager creates it for you automatically the first time. * `policy` - (Optional) A valid JSON document representing a [resource policy](https://docs.aws.amazon.com/secretsmanager/latest/userguide/auth-and-access_resource-based-policies.html). For more information about building AWS IAM policy documents with Terraform, see the [AWS IAM Policy Document Guide](/docs/providers/aws/guides/iam-policy-documents.html). From 0a17b4dc5fda28fe4a89b633cd4f460b1e8ca78f Mon Sep 17 00:00:00 2001 From: saravanan30erd Date: Fri, 26 Oct 2018 22:47:42 +0400 Subject: [PATCH 3/3] add validation funcs in schema --- aws/resource_aws_secretsmanager_secret.go | 2 + aws/validators.go | 27 ++++++++++++ aws/validators_test.go | 54 ++++++++++++++++++++++- 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_secretsmanager_secret.go b/aws/resource_aws_secretsmanager_secret.go index 064402ad42d2..edb12687036d 100644 --- a/aws/resource_aws_secretsmanager_secret.go +++ b/aws/resource_aws_secretsmanager_secret.go @@ -42,6 +42,7 @@ func resourceAwsSecretsManagerSecret() *schema.Resource { Computed: true, ForceNew: true, ConflictsWith: []string{"name_prefix"}, + ValidateFunc: validateSecretManagerSecretName, }, "name_prefix": { Type: schema.TypeString, @@ -49,6 +50,7 @@ func resourceAwsSecretsManagerSecret() *schema.Resource { Computed: true, ForceNew: true, ConflictsWith: []string{"name"}, + ValidateFunc: validateSecretManagerSecretNamePrefix, }, "policy": { Type: schema.TypeString, diff --git a/aws/validators.go b/aws/validators.go index 7e8ec79e5153..4a4da445d70d 100644 --- a/aws/validators.go +++ b/aws/validators.go @@ -2014,6 +2014,19 @@ func validateLbTargetGroupName(v interface{}, k string) (ws []string, errors []e return } +func validateSecretManagerSecretName(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if !regexp.MustCompile(`^[0-9A-Za-z/_+=.@-]+$`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "only alphanumeric characters and /_+=.@- special characters are allowed in %q", k)) + } + if len(value) > 512 { + errors = append(errors, fmt.Errorf( + "%q cannot be greater than 512 characters", k)) + } + return +} + func validateLbTargetGroupNamePrefix(v interface{}, k string) (ws []string, errors []error) { value := v.(string) prefixMaxLength := 32 - resource.UniqueIDSuffixLength @@ -2031,3 +2044,17 @@ func validateLbTargetGroupNamePrefix(v interface{}, k string) (ws []string, erro } return } + +func validateSecretManagerSecretNamePrefix(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if !regexp.MustCompile(`^[0-9A-Za-z/_+=.@-]+$`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "only alphanumeric characters and /_+=.@- special characters are allowed in %q", k)) + } + prefixMaxLength := 512 - resource.UniqueIDSuffixLength + if len(value) > prefixMaxLength { + errors = append(errors, fmt.Errorf( + "%q cannot be greater than %d characters", k, prefixMaxLength)) + } + return +} diff --git a/aws/validators_test.go b/aws/validators_test.go index cafbef52bec9..fe0e73073e05 100644 --- a/aws/validators_test.go +++ b/aws/validators_test.go @@ -2890,7 +2890,6 @@ func TestValidateLbTargetGroupName(t *testing.T) { ErrCount: 1, }, } - for _, tc := range cases { _, errors := validateLbTargetGroupName(tc.Value, "aws_lb_target_group") if len(errors) != tc.ErrCount { @@ -2917,7 +2916,6 @@ func TestValidateLbTargetGroupNamePrefix(t *testing.T) { ErrCount: 1, }, } - for _, tc := range cases { _, errors := validateLbTargetGroupNamePrefix(tc.Value, "aws_lb_target_group") if len(errors) != tc.ErrCount { @@ -2925,3 +2923,55 @@ func TestValidateLbTargetGroupNamePrefix(t *testing.T) { } } } + +func TestValidateSecretManagerSecretName(t *testing.T) { + cases := []struct { + Value string + ErrCount int + }{ + { + Value: "testing123!", + ErrCount: 1, + }, + { + Value: "testing 123", + ErrCount: 1, + }, + { + Value: randomString(513), + ErrCount: 1, + }, + } + for _, tc := range cases { + _, errors := validateSecretManagerSecretName(tc.Value, "aws_secretsmanager_secret") + if len(errors) != tc.ErrCount { + t.Fatalf("Expected the AWS Secretsmanager Secret Name to not trigger a validation error for %q", tc.Value) + } + } +} + +func TestValidateSecretManagerSecretNamePrefix(t *testing.T) { + cases := []struct { + Value string + ErrCount int + }{ + { + Value: "testing123!", + ErrCount: 1, + }, + { + Value: "testing 123", + ErrCount: 1, + }, + { + Value: randomString(512), + ErrCount: 1, + }, + } + for _, tc := range cases { + _, errors := validateSecretManagerSecretNamePrefix(tc.Value, "aws_secretsmanager_secret") + if len(errors) != tc.ErrCount { + t.Fatalf("Expected the AWS Secretsmanager Secret Name to not trigger a validation error for %q", tc.Value) + } + } +}