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

resource/aws_secretsmanager_secret: Support ForceDeleteWithoutRecovery and recreation after immediate deletion #5583

Merged
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
45 changes: 37 additions & 8 deletions aws/resource_aws_secretsmanager_secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/structure"
"github.com/hashicorp/terraform/helper/validation"
)

func resourceAwsSecretsManagerSecret() *schema.Resource {
Expand Down Expand Up @@ -48,10 +47,20 @@ func resourceAwsSecretsManagerSecret() *schema.Resource {
DiffSuppressFunc: suppressEquivalentAwsPolicyDiffs,
},
"recovery_window_in_days": {
Type: schema.TypeInt,
Optional: true,
Default: 30,
ValidateFunc: validation.IntBetween(7, 30),
Type: schema.TypeInt,
Optional: true,
Default: 30,
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
value := v.(int)
if value == 0 {
return
}
if value >= 7 && value <= 30 {
return
}
errors = append(errors, fmt.Errorf("%q must be 0 or between 7 and 30", k))
return
},
},
"rotation_enabled": {
Type: schema.TypeBool,
Expand Down Expand Up @@ -92,7 +101,21 @@ func resourceAwsSecretsManagerSecretCreate(d *schema.ResourceData, meta interfac
}

log.Printf("[DEBUG] Creating Secrets Manager Secret: %s", input)
output, err := conn.CreateSecret(input)

// Retry for secret recreation after deletion
var output *secretsmanager.CreateSecretOutput
err := resource.Retry(2*time.Minute, func() *resource.RetryError {
var err error
output, err = conn.CreateSecret(input)
// InvalidRequestException: You can’t perform this operation on the secret because it was deleted.
if isAWSErr(err, secretsmanager.ErrCodeInvalidRequestException, "You can’t perform this operation on the secret because it was deleted") {
return resource.RetryableError(err)
}
if err != nil {
return resource.NonRetryableError(err)
}
return nil
})
if err != nil {
return fmt.Errorf("error creating Secrets Manager Secret: %s", err)
}
Expand Down Expand Up @@ -336,8 +359,14 @@ func resourceAwsSecretsManagerSecretDelete(d *schema.ResourceData, meta interfac
conn := meta.(*AWSClient).secretsmanagerconn

input := &secretsmanager.DeleteSecretInput{
RecoveryWindowInDays: aws.Int64(int64(d.Get("recovery_window_in_days").(int))),
SecretId: aws.String(d.Id()),
SecretId: aws.String(d.Id()),
}

recoveryWindowInDays := d.Get("recovery_window_in_days").(int)
if recoveryWindowInDays == 0 {
input.ForceDeleteWithoutRecovery = aws.Bool(true)
} else {
input.RecoveryWindowInDays = aws.Int64(int64(recoveryWindowInDays))
}

log.Printf("[DEBUG] Deleting Secrets Manager Secret: %s", input)
Expand Down
48 changes: 46 additions & 2 deletions aws/resource_aws_secretsmanager_secret_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ func testSweepSecretsManagerSecrets(region string) error {
}
log.Printf("[INFO] Deleting Secrets Manager Secret: %s", name)
input := &secretsmanager.DeleteSecretInput{
RecoveryWindowInDays: aws.Int64(7),
SecretId: aws.String(name),
ForceDeleteWithoutRecovery: aws.Bool(true),
SecretId: aws.String(name),
}

_, err := conn.DeleteSecret(input)
Expand Down Expand Up @@ -171,6 +171,41 @@ func TestAccAwsSecretsManagerSecret_KmsKeyID(t *testing.T) {
})
}

func TestAccAwsSecretsManagerSecret_RecoveryWindowInDays_Recreate(t *testing.T) {
var secret secretsmanager.DescribeSecretOutput
rName := acctest.RandomWithPrefix("tf-acc-test")
resourceName := "aws_secretsmanager_secret.test"

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAwsSecretsManagerSecretDestroy,
Steps: []resource.TestStep{
{
Config: testAccAwsSecretsManagerSecretConfig_RecoveryWindowInDays(rName, 0),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsSecretsManagerSecretExists(resourceName, &secret),
resource.TestCheckResourceAttr(resourceName, "recovery_window_in_days", "0"),
),
},
{
Config: testAccAwsSecretsManagerSecretConfig_RecoveryWindowInDays(rName, 0),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsSecretsManagerSecretExists(resourceName, &secret),
resource.TestCheckResourceAttr(resourceName, "recovery_window_in_days", "0"),
),
Taint: []string{resourceName},
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"recovery_window_in_days"},
},
},
})
}

func TestAccAwsSecretsManagerSecret_RotationLambdaARN(t *testing.T) {
var secret secretsmanager.DescribeSecretOutput
rName := acctest.RandomWithPrefix("tf-acc-test")
Expand Down Expand Up @@ -494,6 +529,15 @@ resource "aws_secretsmanager_secret" "test" {
`, rName)
}

func testAccAwsSecretsManagerSecretConfig_RecoveryWindowInDays(rName string, recoveryWindowInDays int) string {
return fmt.Sprintf(`
resource "aws_secretsmanager_secret" "test" {
name = %q
recovery_window_in_days = %d
}
`, rName, recoveryWindowInDays)
}

func testAccAwsSecretsManagerSecretConfig_RotationLambdaARN(rName string) string {
return baseAccAWSLambdaConfig(rName, rName, rName) + fmt.Sprintf(`
# Not a real rotation function
Expand Down
2 changes: 1 addition & 1 deletion website/docs/r/secretsmanager_secret.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ The following arguments are supported:
* `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).
* `recovery_window_in_days` - (Optional) Specifies the number of days that AWS Secrets Manager waits before it can delete the secret. This value can range from 7 to 30 days. The default value is 30.
* `recovery_window_in_days` - (Optional) Specifies the number of days that AWS Secrets Manager waits before it can delete the secret. This value can be `0` to force deletion without recovery or range from `7` to `30` days. The default value is `30`.
* `rotation_lambda_arn` - (Optional) Specifies the ARN of the Lambda function that can rotate the secret.
* `rotation_rules` - (Optional) A structure that defines the rotation configuration for this secret. Defined below.
* `tags` - (Optional) Specifies a key-value map of user-defined tags that are attached to the secret.
Expand Down