Skip to content

Commit

Permalink
Merge remote-tracking branch 'obounaim/f-aws_secretsmanager_rotate' i…
Browse files Browse the repository at this point in the history
…nto HEAD

# Conflicts:
#	internal/service/secretsmanager/secret_rotation.go
  • Loading branch information
ewbankkit committed Jan 12, 2024
2 parents 18e734b + 913ec6b commit 75e2768
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 30 deletions.
3 changes: 3 additions & 0 deletions .changelog/35105.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_secretsmanager_secret_rotation: Add `rotate_immediately` argument
```
39 changes: 24 additions & 15 deletions internal/service/secretsmanager/secret_rotation.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ func resourceSecretRotation() *schema.Resource {
},

Schema: map[string]*schema.Schema{
"rotate_immediately": {
Type: schema.TypeBool,
Optional: true,
Default: true,
},
"rotation_enabled": {
Type: schema.TypeBool,
Computed: true,
Expand Down Expand Up @@ -88,6 +93,7 @@ func resourceSecretRotationCreate(ctx context.Context, d *schema.ResourceData, m
secretID := d.Get("secret_id").(string)
input := &secretsmanager.RotateSecretInput{
ClientRequestToken: aws.String(id.UniqueId()), // Needed because we're handling our own retries
RotateImmediately: aws.Bool(d.Get("rotate_immediately").(bool)),
RotationRules: expandRotationRules(d.Get("rotation_rules").([]interface{})),
SecretId: aws.String(secretID),
}
Expand Down Expand Up @@ -146,24 +152,27 @@ func resourceSecretRotationUpdate(ctx context.Context, d *schema.ResourceData, m
var diags diag.Diagnostics
conn := meta.(*conns.AWSClient).SecretsManagerClient(ctx)

secretID := d.Get("secret_id").(string)
input := &secretsmanager.RotateSecretInput{
ClientRequestToken: aws.String(id.UniqueId()), // Needed because we're handling our own retries
RotationRules: expandRotationRules(d.Get("rotation_rules").([]interface{})),
SecretId: aws.String(secretID),
}
if d.HasChanges("rotation_lambda_arn", "rotation_rules") {
secretID := d.Get("secret_id").(string)
input := &secretsmanager.RotateSecretInput{
ClientRequestToken: aws.String(id.UniqueId()), // Needed because we're handling our own retries
RotateImmediately: aws.Bool(d.Get("rotate_immediately").(bool)),
RotationRules: expandRotationRules(d.Get("rotation_rules").([]interface{})),
SecretId: aws.String(secretID),
}

if v, ok := d.GetOk("rotation_lambda_arn"); ok {
input.RotationLambdaARN = aws.String(v.(string))
}
if v, ok := d.GetOk("rotation_lambda_arn"); ok {
input.RotationLambdaARN = aws.String(v.(string))
}

// AccessDeniedException: Secrets Manager cannot invoke the specified Lambda function.
_, err := tfresource.RetryWhenAWSErrCodeEquals(ctx, 1*time.Minute, func() (interface{}, error) {
return conn.RotateSecret(ctx, input)
}, "AccessDeniedException")
// AccessDeniedException: Secrets Manager cannot invoke the specified Lambda function.
_, err := tfresource.RetryWhenAWSErrCodeEquals(ctx, 1*time.Minute, func() (interface{}, error) {
return conn.RotateSecret(ctx, input)
}, "AccessDeniedException")

if err != nil {
return sdkdiag.AppendErrorf(diags, "updating Secrets Manager Secret Rotation (%s): %s", d.Id(), err)
if err != nil {
return sdkdiag.AppendErrorf(diags, "updating Secrets Manager Secret Rotation (%s): %s", d.Id(), err)
}
}

return append(diags, resourceSecretRotationRead(ctx, d, meta)...)
Expand Down
103 changes: 88 additions & 15 deletions internal/service/secretsmanager/secret_rotation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,57 @@ func TestAccSecretsManagerSecretRotation_basic(t *testing.T) {
testAccCheckSecretRotationExists(ctx, resourceName, &secret),
resource.TestCheckResourceAttr(resourceName, "rotation_enabled", "true"),
resource.TestCheckResourceAttrPair(resourceName, "rotation_lambda_arn", lambdaFunctionResourceName, "arn"),
resource.TestCheckResourceAttr(resourceName, "rotate_immediately", "true"),
resource.TestCheckResourceAttr(resourceName, "rotation_rules.#", "1"),
resource.TestCheckResourceAttr(resourceName, "rotation_rules.0.automatically_after_days", strconv.Itoa(days)),
resource.TestCheckResourceAttr(resourceName, "rotation_rules.0.duration", ""),
resource.TestCheckResourceAttr(resourceName, "rotation_rules.0.schedule_expression", ""),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"rotate_immediately"},
},
},
})
}

func TestAccSecretsManagerSecretRotation_rotateImmediately(t *testing.T) {
ctx := acctest.Context(t)
var secret secretsmanager.DescribeSecretOutput
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
const (
resourceName = "aws_secretsmanager_secret_rotation.test"
lambdaFunctionResourceName = "aws_lambda_function.test"
days = 7
)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t); testAccPreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.SecretsManagerEndpointID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckSecretRotationDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccSecretRotationConfig_rotateImmediately(rName, days),
Check: resource.ComposeTestCheckFunc(
testAccCheckSecretRotationExists(ctx, resourceName, &secret),
resource.TestCheckResourceAttr(resourceName, "rotation_enabled", "true"),
resource.TestCheckResourceAttrPair(resourceName, "rotation_lambda_arn", lambdaFunctionResourceName, "arn"),
resource.TestCheckResourceAttr(resourceName, "rotate_immediately", "false"),
resource.TestCheckResourceAttr(resourceName, "rotation_rules.#", "1"),
resource.TestCheckResourceAttr(resourceName, "rotation_rules.0.automatically_after_days", strconv.Itoa(days)),
resource.TestCheckResourceAttr(resourceName, "rotation_rules.0.duration", ""),
resource.TestCheckResourceAttr(resourceName, "rotation_rules.0.schedule_expression", ""),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"rotate_immediately"},
},
},
})
Expand Down Expand Up @@ -151,9 +192,10 @@ func TestAccSecretsManagerSecretRotation_scheduleExpression(t *testing.T) {
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"rotate_immediately"},
},
},
})
Expand Down Expand Up @@ -199,9 +241,10 @@ func TestAccSecretsManagerSecretRotation_scheduleExpressionToDays(t *testing.T)
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"rotate_immediately"},
},
},
})
Expand Down Expand Up @@ -246,9 +289,10 @@ func TestAccSecretsManagerSecretRotation_scheduleExpressionHours(t *testing.T) {
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"rotate_immediately"},
},
},
})
Expand Down Expand Up @@ -283,9 +327,10 @@ func TestAccSecretsManagerSecretRotation_duration(t *testing.T) {
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"rotate_immediately"},
},
},
})
Expand Down Expand Up @@ -426,6 +471,34 @@ resource "aws_secretsmanager_secret_rotation" "test" {
`, rName, automaticallyAfterDays))
}

func testAccSecretRotationConfig_rotateImmediately(rName string, automaticallyAfterDays int) string {
return acctest.ConfigCompose(
acctest.ConfigLambdaBase(rName, rName, rName),
testAccSecretRotationConfig_base(rName),
fmt.Sprintf(`
resource "aws_secretsmanager_secret" "test" {
name = %[1]q
}
resource "aws_secretsmanager_secret_version" "test" {
secret_id = aws_secretsmanager_secret.test.id
secret_string = "test-string"
}
resource "aws_secretsmanager_secret_rotation" "test" {
secret_id = aws_secretsmanager_secret.test.id
rotation_lambda_arn = aws_lambda_function.test.arn
rotate_immediately = false
rotation_rules {
automatically_after_days = %[2]d
}
depends_on = [aws_lambda_permission.test]
}
`, rName, automaticallyAfterDays))
}

func testAccSecretRotationConfig_scheduleExpression(rName string, scheduleExpression string) string {
return acctest.ConfigCompose(
acctest.ConfigLambdaBase(rName, rName, rName),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ To enable automatic secret rotation, the Secrets Manager service requires usage
This resource supports the following arguments:

* `secret_id` - (Required) Specifies the secret to which you want to add a new version. You can specify either the Amazon Resource Name (ARN) or the friendly name of the secret. The secret must already exist.
* `rotate_immediately` - (Optional) Specifies whether to rotate the secret immediately or wait until the next scheduled rotation window. For secrets that use a Lambda rotation function to rotate, if you don't immediately rotate the secret, Secrets Manager tests the rotation configuration by running the testSecret step (https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotate-secrets_how.html) of the Lambda rotation function. The test creates an AWSPENDING version of the secret and then removes it. Defaults to `false`.
* `rotation_lambda_arn` - (Optional) Specifies the ARN of the Lambda function that can rotate the secret. Must be supplied if the secret is not managed by AWS.
* `rotation_rules` - (Required) A structure that defines the rotation configuration for this secret. Defined below.

Expand Down

0 comments on commit 75e2768

Please sign in to comment.