diff --git a/.changelog/20467.txt b/.changelog/20467.txt new file mode 100644 index 000000000000..3e0809028a9a --- /dev/null +++ b/.changelog/20467.txt @@ -0,0 +1,3 @@ +```release-note:bug +resource/aws_iot_topic_rule: Enhance handling of IAM eventual consistency errors during create +``` \ No newline at end of file diff --git a/aws/resource_aws_iot_topic_rule.go b/aws/resource_aws_iot_topic_rule.go index 043811a32877..3e7e9998730a 100644 --- a/aws/resource_aws_iot_topic_rule.go +++ b/aws/resource_aws_iot_topic_rule.go @@ -5,9 +5,13 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/iot" + "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/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/tfresource" ) func resourceAwsIotTopicRule() *schema.Resource { @@ -1094,7 +1098,24 @@ func resourceAwsIotTopicRuleCreate(d *schema.ResourceData, meta interface{}) err TopicRulePayload: expandIotTopicRulePayload(d), } - _, err := conn.CreateTopicRule(input) + err := resource.Retry(iamwaiter.PropagationTimeout, func() *resource.RetryError { + var err error + _, err = conn.CreateTopicRule(input) + + if tfawserr.ErrMessageContains(err, iot.ErrCodeInvalidRequestException, "unable to perform: sts:AssumeRole on resource") { + return resource.RetryableError(err) + } + + if err != nil { + return resource.NonRetryableError(err) + } + + return nil + }) + + if tfresource.TimedOut(err) { + _, err = conn.CreateTopicRule(input) + } if err != nil { return fmt.Errorf("error creating IoT Topic Rule (%s): %w", ruleName, err) diff --git a/aws/resource_aws_iot_topic_rule_test.go b/aws/resource_aws_iot_topic_rule_test.go index 787b08b5bad6..18326b045ab8 100644 --- a/aws/resource_aws_iot_topic_rule_test.go +++ b/aws/resource_aws_iot_topic_rule_test.go @@ -674,7 +674,8 @@ func testAccCheckAWSIoTTopicRuleExists(name string) resource.TestCheckFunc { } } -const testAccAWSIoTTopicRuleRole = ` +func testAccAWSIoTTopicRuleRole(rName string) string { + return fmt.Sprintf(` data "aws_partition" "current" {} resource "aws_iam_role" "iot_role" { @@ -722,10 +723,11 @@ resource "aws_iam_policy_attachment" "attach_policy" { roles = [aws_iam_role.iot_role.name] policy_arn = aws_iam_policy.policy.arn } -` +`, rName) +} func testAccAWSIoTTopicRule_basic(rName string) string { - return fmt.Sprintf(testAccAWSIoTTopicRuleRole+` + return fmt.Sprintf(` resource "aws_iot_topic_rule" "rule" { name = "test_rule_%[1]s" description = "Example rule" @@ -737,7 +739,9 @@ resource "aws_iot_topic_rule" "rule" { } func testAccAWSIoTTopicRule_cloudwatchalarm(rName string) string { - return fmt.Sprintf(testAccAWSIoTTopicRuleRole+` + return composeConfig( + testAccAWSIoTTopicRuleRole(rName), + fmt.Sprintf(` resource "aws_iot_topic_rule" "rule" { name = "test_rule_%[1]s" description = "Example rule" @@ -752,11 +756,13 @@ resource "aws_iot_topic_rule" "rule" { state_value = "OK" } } -`, rName) +`, rName)) } func testAccAWSIoTTopicRule_cloudwatchmetric(rName string) string { - return fmt.Sprintf(testAccAWSIoTTopicRuleRole+` + return composeConfig( + testAccAWSIoTTopicRuleRole(rName), + fmt.Sprintf(` resource "aws_iot_topic_rule" "rule" { name = "test_rule_%[1]s" description = "Example rule" @@ -772,11 +778,13 @@ resource "aws_iot_topic_rule" "rule" { role_arn = aws_iam_role.iot_role.arn } } -`, rName) +`, rName)) } func testAccAWSIoTTopicRule_dynamoDbv2(rName string) string { - return fmt.Sprintf(testAccAWSIoTTopicRuleRole+` + return composeConfig( + testAccAWSIoTTopicRuleRole(rName), + fmt.Sprintf(` resource "aws_iot_topic_rule" "rule" { name = "test_rule_%[1]s" description = "Example rule" @@ -792,11 +800,13 @@ resource "aws_iot_topic_rule" "rule" { role_arn = aws_iam_role.iot_role.arn } } -`, rName) +`, rName)) } func testAccAWSIoTTopicRule_dynamodb(rName string) string { - return fmt.Sprintf(testAccAWSIoTTopicRuleRole+` + return composeConfig( + testAccAWSIoTTopicRuleRole(rName), + fmt.Sprintf(` resource "aws_iot_topic_rule" "rule" { name = "test_rule_%[1]s" description = "Example rule" @@ -812,11 +822,13 @@ resource "aws_iot_topic_rule" "rule" { table_name = "table_name" } } -`, rName) +`, rName)) } func testAccAWSIoTTopicRule_dynamodb_rangekeys(rName string) string { - return fmt.Sprintf(testAccAWSIoTTopicRuleRole+` + return composeConfig( + testAccAWSIoTTopicRuleRole(rName), + fmt.Sprintf(` resource "aws_iot_topic_rule" "rule" { name = "test_rule_%[1]s" description = "Example rule" @@ -836,11 +848,13 @@ resource "aws_iot_topic_rule" "rule" { operation = "INSERT" } } -`, rName) +`, rName)) } func testAccAWSIoTTopicRule_elasticsearch(rName string) string { - return fmt.Sprintf(testAccAWSIoTTopicRuleRole+` + return composeConfig( + testAccAWSIoTTopicRuleRole(rName), + fmt.Sprintf(` data "aws_region" "current" {} resource "aws_iot_topic_rule" "rule" { @@ -858,11 +872,13 @@ resource "aws_iot_topic_rule" "rule" { role_arn = aws_iam_role.iot_role.arn } } -`, rName) +`, rName)) } func testAccAWSIoTTopicRule_firehose(rName string) string { - return fmt.Sprintf(testAccAWSIoTTopicRuleRole+` + return composeConfig( + testAccAWSIoTTopicRuleRole(rName), + fmt.Sprintf(` resource "aws_iot_topic_rule" "rule" { name = "test_rule_%[1]s" description = "Example rule" @@ -875,11 +891,13 @@ resource "aws_iot_topic_rule" "rule" { role_arn = aws_iam_role.iot_role.arn } } -`, rName) +`, rName)) } func testAccAWSIoTTopicRule_firehose_separator(rName, separator string) string { - return fmt.Sprintf(testAccAWSIoTTopicRuleRole+` + return composeConfig( + testAccAWSIoTTopicRuleRole(rName), + fmt.Sprintf(` resource "aws_iot_topic_rule" "rule" { name = "test_rule_%[1]s" description = "Example rule" @@ -893,11 +911,13 @@ resource "aws_iot_topic_rule" "rule" { separator = %q } } -`, rName, separator) +`, rName, separator)) } func testAccAWSIoTTopicRule_kinesis(rName string) string { - return fmt.Sprintf(testAccAWSIoTTopicRuleRole+` + return composeConfig( + testAccAWSIoTTopicRuleRole(rName), + fmt.Sprintf(` resource "aws_iot_topic_rule" "rule" { name = "test_rule_%[1]s" description = "Example rule" @@ -910,13 +930,15 @@ resource "aws_iot_topic_rule" "rule" { role_arn = aws_iam_role.iot_role.arn } } -`, rName) +`, rName)) } func testAccAWSIoTTopicRule_lambda(rName string) string { - return fmt.Sprintf(testAccAWSIoTTopicRuleRole+` + return fmt.Sprintf(` data "aws_region" "current" {} +data "aws_partition" "current" {} + resource "aws_iot_topic_rule" "rule" { name = "test_rule_%[1]s" description = "Example rule" @@ -932,7 +954,9 @@ resource "aws_iot_topic_rule" "rule" { } func testAccAWSIoTTopicRule_republish(rName string) string { - return fmt.Sprintf(testAccAWSIoTTopicRuleRole+` + return composeConfig( + testAccAWSIoTTopicRuleRole(rName), + fmt.Sprintf(` resource "aws_iot_topic_rule" "rule" { name = "test_rule_%[1]s" description = "Example rule" @@ -945,11 +969,13 @@ resource "aws_iot_topic_rule" "rule" { topic = "mytopic" } } -`, rName) +`, rName)) } func testAccAWSIoTTopicRule_republish_with_qos(rName string) string { - return fmt.Sprintf(testAccAWSIoTTopicRuleRole+` + return composeConfig( + testAccAWSIoTTopicRuleRole(rName), + fmt.Sprintf(` resource "aws_iot_topic_rule" "rule" { name = "test_rule_%[1]s" description = "Example rule" @@ -963,11 +989,13 @@ resource "aws_iot_topic_rule" "rule" { qos = 1 } } -`, rName) +`, rName)) } func testAccAWSIoTTopicRule_s3(rName string) string { - return fmt.Sprintf(testAccAWSIoTTopicRuleRole+` + return composeConfig( + testAccAWSIoTTopicRuleRole(rName), + fmt.Sprintf(` resource "aws_iot_topic_rule" "rule" { name = "test_rule_%[1]s" description = "Example rule" @@ -981,11 +1009,13 @@ resource "aws_iot_topic_rule" "rule" { role_arn = aws_iam_role.iot_role.arn } } -`, rName) +`, rName)) } func testAccAWSIoTTopicRule_sns(rName string) string { - return fmt.Sprintf(testAccAWSIoTTopicRuleRole+` + return composeConfig( + testAccAWSIoTTopicRuleRole(rName), + fmt.Sprintf(` data "aws_region" "current" {} resource "aws_iot_topic_rule" "rule" { @@ -1000,11 +1030,13 @@ resource "aws_iot_topic_rule" "rule" { target_arn = "arn:${data.aws_partition.current.partition}:sns:${data.aws_region.current.name}:123456789012:my_corporate_topic" } } -`, rName) +`, rName)) } func testAccAWSIoTTopicRule_sqs(rName string) string { - return fmt.Sprintf(testAccAWSIoTTopicRuleRole+` + return composeConfig( + testAccAWSIoTTopicRuleRole(rName), + fmt.Sprintf(` resource "aws_iot_topic_rule" "rule" { name = "test_rule_%[1]s" description = "Example rule" @@ -1018,11 +1050,13 @@ resource "aws_iot_topic_rule" "rule" { use_base64 = false } } -`, rName) +`, rName)) } func testAccAWSIoTTopicRule_step_functions(rName string) string { - return fmt.Sprintf(testAccAWSIoTTopicRuleRole+` + return composeConfig( + testAccAWSIoTTopicRuleRole(rName), + fmt.Sprintf(` resource "aws_iot_topic_rule" "rule" { name = "test_rule_%[1]s" description = "Example rule" @@ -1036,11 +1070,13 @@ resource "aws_iot_topic_rule" "rule" { role_arn = aws_iam_role.iot_role.arn } } -`, rName) +`, rName)) } func testAccAWSIoTTopicRule_iot_analytics(rName string) string { - return fmt.Sprintf(testAccAWSIoTTopicRuleRole+` + return composeConfig( + testAccAWSIoTTopicRuleRole(rName), + fmt.Sprintf(` resource "aws_iot_topic_rule" "rule" { name = "test_rule_%[1]s" description = "Example rule" @@ -1053,11 +1089,13 @@ resource "aws_iot_topic_rule" "rule" { role_arn = aws_iam_role.iot_role.arn } } -`, rName) +`, rName)) } func testAccAWSIoTTopicRule_iot_events(rName string) string { - return fmt.Sprintf(testAccAWSIoTTopicRuleRole+` + return composeConfig( + testAccAWSIoTTopicRuleRole(rName), + fmt.Sprintf(` resource "aws_iot_topic_rule" "rule" { name = "test_rule_%[1]s" description = "Example rule" @@ -1071,11 +1109,11 @@ resource "aws_iot_topic_rule" "rule" { message_id = "fake_message_id" } } -`, rName) +`, rName)) } func testAccAWSIoTTopicRuleTags1(rName, tagKey1, tagValue1 string) string { - return fmt.Sprintf(testAccAWSIoTTopicRuleRole+` + return fmt.Sprintf(` resource "aws_iot_topic_rule" "test" { name = "test_rule_%[1]s" enabled = true @@ -1090,7 +1128,7 @@ resource "aws_iot_topic_rule" "test" { } func testAccAWSIoTTopicRuleTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { - return fmt.Sprintf(testAccAWSIoTTopicRuleRole+` + return fmt.Sprintf(` resource "aws_iot_topic_rule" "test" { name = "test_rule_%[1]s" enabled = true @@ -1106,7 +1144,9 @@ resource "aws_iot_topic_rule" "test" { } func testAccAWSIoTTopicRule_errorAction(rName string) string { - return fmt.Sprintf(testAccAWSIoTTopicRuleRole+` + return composeConfig( + testAccAWSIoTTopicRuleRole(rName), + fmt.Sprintf(` resource "aws_iot_topic_rule" "rule" { name = "test_rule_%[1]s" description = "Example rule" @@ -1126,5 +1166,5 @@ resource "aws_iot_topic_rule" "rule" { } } } -`, rName) +`, rName)) }