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

service/secretsmanager: Handle read-after-create eventual consistency in resources #18462

Merged
merged 1 commit into from
Apr 2, 2021
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
15 changes: 15 additions & 0 deletions .changelog/18462.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
```release-note:bug
resource/aws_secretsmanager_secret: Handle read-after-create eventual consistency
```

```release-note:bug
resource/aws_secretsmanager_secret_policy: Handle read-after-create eventual consistency
```

```release-note:bug
resource/aws_secretsmanager_secret_rotation: Handle read-after-create eventual consistency
```

```release-note:bug
resource/aws_secretsmanager_secret_version: Handle read-after-create eventual consistency
```
4 changes: 2 additions & 2 deletions aws/internal/service/secretsmanager/waiter/waiter.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ import (
)

const (
// Maximum amount of time to wait for Secrets Manager deletions to propagate
DeletionPropagationTimeout = 2 * time.Minute
// Maximum amount of time to wait for Secrets Manager changes to propagate
PropagationTimeout = 2 * time.Minute
)
45 changes: 36 additions & 9 deletions aws/resource_aws_secretsmanager_secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import (

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/secretsmanager"
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags"
iamwaiter "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/iam/waiter"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/secretsmanager/waiter"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource"
)

func resourceAwsSecretsManagerSecret() *schema.Resource {
Expand Down Expand Up @@ -131,7 +133,7 @@ func resourceAwsSecretsManagerSecretCreate(d *schema.ResourceData, meta interfac

// Retry for secret recreation after deletion
var output *secretsmanager.CreateSecretOutput
err := resource.Retry(waiter.DeletionPropagationTimeout, func() *resource.RetryError {
err := resource.Retry(waiter.PropagationTimeout, func() *resource.RetryError {
var err error
output, err = conn.CreateSecret(input)
// Temporarily retry on these errors to support immediate secret recreation:
Expand Down Expand Up @@ -218,15 +220,40 @@ func resourceAwsSecretsManagerSecretRead(d *schema.ResourceData, meta interface{
SecretId: aws.String(d.Id()),
}

log.Printf("[DEBUG] Reading Secrets Manager Secret: %s", input)
output, err := conn.DescribeSecret(input)
if err != nil {
if isAWSErr(err, secretsmanager.ErrCodeResourceNotFoundException, "") {
log.Printf("[WARN] Secrets Manager Secret %q not found - removing from state", d.Id())
d.SetId("")
return nil
var output *secretsmanager.DescribeSecretOutput

err := resource.Retry(waiter.PropagationTimeout, func() *resource.RetryError {
var err error

output, err = conn.DescribeSecret(input)

if d.IsNewResource() && tfawserr.ErrCodeEquals(err, secretsmanager.ErrCodeResourceNotFoundException) {
return resource.RetryableError(err)
}

if err != nil {
return resource.NonRetryableError(err)
}
return fmt.Errorf("error reading Secrets Manager Secret: %w", err)

return nil
})

if tfresource.TimedOut(err) {
output, err = conn.DescribeSecret(input)
}

if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, secretsmanager.ErrCodeResourceNotFoundException) {
log.Printf("[WARN] Secrets Manager Secret (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

if err != nil {
return fmt.Errorf("error reading Secrets Manager Secret (%s): %w", d.Id(), err)
}

if output == nil {
return fmt.Errorf("error reading Secrets Manager Secret (%s): empty response", d.Id())
}

d.Set("arn", output.ARN)
Expand Down
45 changes: 37 additions & 8 deletions aws/resource_aws_secretsmanager_secret_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ import (

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/secretsmanager"
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
iamwaiter "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/iam/waiter"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/secretsmanager/waiter"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource"
)

func resourceAwsSecretsManagerSecretPolicy() *schema.Resource {
Expand Down Expand Up @@ -89,15 +92,41 @@ func resourceAwsSecretsManagerSecretPolicyRead(d *schema.ResourceData, meta inte
input := &secretsmanager.GetResourcePolicyInput{
SecretId: aws.String(d.Id()),
}
log.Printf("[DEBUG] Reading Secrets Manager Secret Policy: %#v", input)
res, err := conn.GetResourcePolicy(input)
if err != nil {
if isAWSErr(err, secretsmanager.ErrCodeResourceNotFoundException, "") {
log.Printf("[WARN] SecretsManager Secret Policy (%s) not found, removing from state", d.Id())
d.SetId("")
return nil

var res *secretsmanager.GetResourcePolicyOutput

err := resource.Retry(waiter.PropagationTimeout, func() *resource.RetryError {
var err error

res, err = conn.GetResourcePolicy(input)

if d.IsNewResource() && tfawserr.ErrCodeEquals(err, secretsmanager.ErrCodeResourceNotFoundException) {
return resource.RetryableError(err)
}

if err != nil {
return resource.NonRetryableError(err)
}
return fmt.Errorf("error reading Secrets Manager Secret policy: %w", err)

return nil
})

if tfresource.TimedOut(err) {
res, err = conn.GetResourcePolicy(input)
}

if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, secretsmanager.ErrCodeResourceNotFoundException) {
log.Printf("[WARN] Secrets Manager Secret Policy (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

if err != nil {
return fmt.Errorf("error reading Secrets Manager Secret Policy (%s): %w", d.Id(), err)
}

if res == nil {
return fmt.Errorf("error reading Secrets Manager Secret Policy (%s): empty response", d.Id())
}

if res.ResourcePolicy != nil {
Expand Down
2 changes: 1 addition & 1 deletion aws/resource_aws_secretsmanager_secret_policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ func testAccCheckAwsSecretsManagerSecretPolicyDestroy(s *terraform.State) error

var output *secretsmanager.DescribeSecretOutput

err := resource.Retry(waiter.DeletionPropagationTimeout, func() *resource.RetryError {
err := resource.Retry(waiter.PropagationTimeout, func() *resource.RetryError {
var err error
output, err = conn.DescribeSecret(secretInput)

Expand Down
44 changes: 36 additions & 8 deletions aws/resource_aws_secretsmanager_secret_rotation.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import (

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/secretsmanager"
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/secretsmanager/waiter"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource"
)

func resourceAwsSecretsManagerSecretRotation() *schema.Resource {
Expand Down Expand Up @@ -99,15 +102,40 @@ func resourceAwsSecretsManagerSecretRotationRead(d *schema.ResourceData, meta in
SecretId: aws.String(d.Id()),
}

log.Printf("[DEBUG] Reading Secrets Manager Secret Rotation: %s", input)
output, err := conn.DescribeSecret(input)
if err != nil {
if isAWSErr(err, secretsmanager.ErrCodeResourceNotFoundException, "") {
log.Printf("[WARN] Secrets Manager Secret Rotation %q not found - removing from state", d.Id())
d.SetId("")
return nil
var output *secretsmanager.DescribeSecretOutput

err := resource.Retry(waiter.PropagationTimeout, func() *resource.RetryError {
var err error

output, err = conn.DescribeSecret(input)

if d.IsNewResource() && tfawserr.ErrCodeEquals(err, secretsmanager.ErrCodeResourceNotFoundException) {
return resource.RetryableError(err)
}
return fmt.Errorf("error reading Secrets Manager Secret Rotation: %s", err)

if err != nil {
return resource.NonRetryableError(err)
}

return nil
})

if tfresource.TimedOut(err) {
output, err = conn.DescribeSecret(input)
}

if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, secretsmanager.ErrCodeResourceNotFoundException) {
log.Printf("[WARN] Secrets Manager Secret Rotation (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

if err != nil {
return fmt.Errorf("error reading Secrets Manager Secret Rotation (%s): %w", d.Id(), err)
}

if output == nil {
return fmt.Errorf("error reading Secrets Manager Secret Rotation (%s): empty response", d.Id())
}

d.Set("secret_id", d.Id())
Expand Down
2 changes: 1 addition & 1 deletion aws/resource_aws_secretsmanager_secret_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ func testAccCheckAwsSecretsManagerSecretDestroy(s *terraform.State) error {

var output *secretsmanager.DescribeSecretOutput

err := resource.Retry(waiter.DeletionPropagationTimeout, func() *resource.RetryError {
err := resource.Retry(waiter.PropagationTimeout, func() *resource.RetryError {
var err error
output, err = conn.DescribeSecret(input)

Expand Down
58 changes: 46 additions & 12 deletions aws/resource_aws_secretsmanager_secret_version.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import (

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/secretsmanager"
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/secretsmanager/waiter"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource"
)

func resourceAwsSecretsManagerSecretVersion() *schema.Resource {
Expand Down Expand Up @@ -114,20 +118,50 @@ func resourceAwsSecretsManagerSecretVersionRead(d *schema.ResourceData, meta int
VersionId: aws.String(versionID),
}

log.Printf("[DEBUG] Reading Secrets Manager Secret Version: %s", input)
output, err := conn.GetSecretValue(input)
if err != nil {
if isAWSErr(err, secretsmanager.ErrCodeResourceNotFoundException, "") {
log.Printf("[WARN] Secrets Manager Secret Version %q not found - removing from state", d.Id())
d.SetId("")
return nil
var output *secretsmanager.GetSecretValueOutput

err = resource.Retry(waiter.PropagationTimeout, func() *resource.RetryError {
var err error

output, err = conn.GetSecretValue(input)

if d.IsNewResource() && tfawserr.ErrCodeEquals(err, secretsmanager.ErrCodeResourceNotFoundException) {
return resource.RetryableError(err)
}
if isAWSErr(err, secretsmanager.ErrCodeInvalidRequestException, "You can’t perform this operation on the secret because it was deleted") {
log.Printf("[WARN] Secrets Manager Secret Version %q not found - removing from state", d.Id())
d.SetId("")
return nil

if d.IsNewResource() && tfawserr.ErrMessageContains(err, secretsmanager.ErrCodeInvalidRequestException, "You can’t perform this operation on the secret because it was deleted") {
return resource.RetryableError(err)
}
return fmt.Errorf("error reading Secrets Manager Secret Version: %s", err)

if err != nil {
return resource.NonRetryableError(err)
}

return nil
})

if tfresource.TimedOut(err) {
output, err = conn.GetSecretValue(input)
}

if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, secretsmanager.ErrCodeResourceNotFoundException) {
log.Printf("[WARN] Secrets Manager Secret Version (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

if !d.IsNewResource() && tfawserr.ErrMessageContains(err, secretsmanager.ErrCodeInvalidRequestException, "You can’t perform this operation on the secret because it was deleted") {
log.Printf("[WARN] Secrets Manager Secret Version (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

if err != nil {
return fmt.Errorf("error reading Secrets Manager Secret Version (%s): %w", d.Id(), err)
}

if output == nil {
return fmt.Errorf("error reading Secrets Manager Secret Version (%s): empty response", d.Id())
}

d.Set("secret_id", secretID)
Expand Down