From 784897a68cd932cac6bb5afd8c65db7a77a1ed2f Mon Sep 17 00:00:00 2001 From: Jean de Kernier Date: Mon, 29 Jul 2019 17:03:55 +0200 Subject: [PATCH 01/28] resource/aws_iot_policy: Delete oldest policy version when max number is reached --- aws/resource_aws_iot_policy.go | 57 ++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/aws/resource_aws_iot_policy.go b/aws/resource_aws_iot_policy.go index 724540e7404..6a720e866c9 100644 --- a/aws/resource_aws_iot_policy.go +++ b/aws/resource_aws_iot_policy.go @@ -1,6 +1,7 @@ package aws import ( + "fmt" "log" "github.com/aws/aws-sdk-go/aws" @@ -75,6 +76,10 @@ func resourceAwsIotPolicyRead(d *schema.ResourceData, meta interface{}) error { func resourceAwsIotPolicyUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).iotconn + if err := iotPolicyPruneVersions(d.Id(), conn); err != nil { + return err + } + if d.HasChange("policy") { _, err := conn.CreatePolicyVersion(&iot.CreatePolicyVersionInput{ PolicyName: aws.String(d.Id()), @@ -129,3 +134,55 @@ func resourceAwsIotPolicyDelete(d *schema.ResourceData, meta interface{}) error return nil } + +// iotPolicyPruneVersions deletes the oldest non-default version if the maximum +// number of versions (5) has been reached. +func iotPolicyPruneVersions(name string, iotconn *iot.IoT) error { + versions, err := iotPolicyListVersions(name, iotconn) + if err != nil { + return err + } + if len(versions) < 5 { + return nil + } + + var oldestVersion *iot.PolicyVersion + + for _, version := range versions { + if *version.IsDefaultVersion { + continue + } + if oldestVersion == nil || + version.CreateDate.Before(*oldestVersion.CreateDate) { + oldestVersion = version + } + } + + err = iotPolicyDeleteVersion(name, *oldestVersion.VersionId, iotconn) + return err +} + +func iotPolicyListVersions(name string, iotconn *iot.IoT) ([]*iot.PolicyVersion, error) { + request := &iot.ListPolicyVersionsInput{ + PolicyName: aws.String(name), + } + + response, err := iotconn.ListPolicyVersions(request) + if err != nil { + return nil, fmt.Errorf("Error listing versions for IoT policy %s: %s", name, err) + } + return response.PolicyVersions, nil +} + +func iotPolicyDeleteVersion(name, versionID string, iotconn *iot.IoT) error { + request := &iot.DeletePolicyVersionInput{ + PolicyName: aws.String(name), + PolicyVersionId: aws.String(versionID), + } + + _, err := iotconn.DeletePolicyVersion(request) + if err != nil { + return fmt.Errorf("Error deleting version %s from IoT policy %s: %s", versionID, name, err) + } + return nil +} From 9852ae0e6342dbcb2720de02be37faccfe4114be Mon Sep 17 00:00:00 2001 From: Jean de Kernier Date: Tue, 30 Jul 2019 14:32:52 +0200 Subject: [PATCH 02/28] resource/aws_iot_policy: Add an acceptance test for the resource update --- aws/resource_aws_iot_policy_test.go | 117 ++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/aws/resource_aws_iot_policy_test.go b/aws/resource_aws_iot_policy_test.go index ce0e6b53d7a..d03cf08dcf8 100644 --- a/aws/resource_aws_iot_policy_test.go +++ b/aws/resource_aws_iot_policy_test.go @@ -50,6 +50,59 @@ func TestAccAWSIoTPolicy_invalidJson(t *testing.T) { }) } +func TestAccAWSIoTPolicy_update(t *testing.T) { + rName := acctest.RandomWithPrefix("PubSubToAnyTopic-") + expectedVersions := []string{"1", "2", "3", "5", "6"} + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSIoTPolicyDestroy_basic, + Steps: []resource.TestStep{ + { + Config: testAccAWSIoTPolicyConfigInitialState(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("aws_iot_policy.pubsub", "name", rName), + resource.TestCheckResourceAttrSet("aws_iot_policy.pubsub", "arn"), + resource.TestCheckResourceAttr("aws_iot_policy.pubsub", "default_version_id", "1"), + resource.TestCheckResourceAttrSet("aws_iot_policy.pubsub", "policy"), + ), + }, + { + Config: testAccAWSIoTPolicyConfig_updatePolicy(rName, "topic2"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("aws_iot_policy.pubsub", "default_version_id", "2"), + ), + }, + { + Config: testAccAWSIoTPolicyConfig_updatePolicy(rName, "topic3"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("aws_iot_policy.pubsub", "default_version_id", "3"), + ), + }, + { + Config: testAccAWSIoTPolicyConfig_updatePolicy(rName, "topic4"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("aws_iot_policy.pubsub", "default_version_id", "4"), + ), + }, + { + Config: testAccAWSIoTPolicyConfig_updatePolicy(rName, "topic5"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("aws_iot_policy.pubsub", "default_version_id", "5"), + ), + }, + { + Config: testAccAWSIoTPolicyConfig_updatePolicy(rName, "topic6"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("aws_iot_policy.pubsub", "default_version_id", "6"), + testAccCheckAWSIoTPolicyVersions("aws_iot_policy.pubsub", expectedVersions), + ), + }, + }, + }) +} + func testAccCheckAWSIoTPolicyDestroy_basic(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).iotconn @@ -83,6 +136,50 @@ func testAccCheckAWSIoTPolicyDestroy_basic(s *terraform.State) error { return nil } +func testAccCheckAWSIoTPolicyVersions(rName string, expVersions []string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[rName] + if !ok { + return fmt.Errorf("Not found: %s", rName) + } + + conn := testAccProvider.Meta().(*AWSClient).iotconn + params := &iot.ListPolicyVersionsInput{ + PolicyName: aws.String(rs.Primary.Attributes["name"]), + } + + resp, err := conn.ListPolicyVersions(params) + if err != nil { + return err + } + + if len(expVersions) != len(resp.PolicyVersions) { + return fmt.Errorf("Expected %d versions, got %d", len(expVersions), len(resp.PolicyVersions)) + } + + var actVersions []string + for _, actVer := range resp.PolicyVersions { + actVersions = append(actVersions, *(actVer.VersionId)) + } + + matchedValue := false + for _, actVer := range actVersions { + matchedValue = false + for _, expVer := range expVersions { + if actVer == expVer { + matchedValue = true + break + } + } + if !matchedValue { + return fmt.Errorf("Expected: %v / Got: %v", expVersions, actVersions) + } + } + + return nil + } +} + func testAccAWSIoTPolicyConfigInitialState(rName string) string { return fmt.Sprintf(` resource "aws_iot_policy" "pubsub" { @@ -120,3 +217,23 @@ EOF } `, rName) } + +func testAccAWSIoTPolicyConfig_updatePolicy(rName string, topicName string) string { + return fmt.Sprintf(` +resource "aws_iot_policy" "pubsub" { + name = "%s" + + policy = < Date: Tue, 30 Jul 2019 14:49:05 +0200 Subject: [PATCH 03/28] resource/aws_iot_policy: Switch old version pruning to use VersionId instead of CreateDate --- aws/resource_aws_iot_policy.go | 13 +++++++++++-- aws/resource_aws_iot_policy_test.go | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/aws/resource_aws_iot_policy.go b/aws/resource_aws_iot_policy.go index 6a720e866c9..fbe5a0c0b0d 100644 --- a/aws/resource_aws_iot_policy.go +++ b/aws/resource_aws_iot_policy.go @@ -3,6 +3,7 @@ package aws import ( "fmt" "log" + "strconv" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/iot" @@ -147,14 +148,22 @@ func iotPolicyPruneVersions(name string, iotconn *iot.IoT) error { } var oldestVersion *iot.PolicyVersion + var oldestVersionId int64 for _, version := range versions { if *version.IsDefaultVersion { continue } - if oldestVersion == nil || - version.CreateDate.Before(*oldestVersion.CreateDate) { + + versionId, err := strconv.ParseInt(*version.VersionId, 10, 64) + if err != nil { + log.Printf("[ERROR] Unexpected version id cannot be parsed to an integer. %s", err) + return err + } + + if oldestVersion == nil || versionId < oldestVersionId { oldestVersion = version + oldestVersionId = versionId } } diff --git a/aws/resource_aws_iot_policy_test.go b/aws/resource_aws_iot_policy_test.go index d03cf08dcf8..2b0f96382f3 100644 --- a/aws/resource_aws_iot_policy_test.go +++ b/aws/resource_aws_iot_policy_test.go @@ -52,7 +52,7 @@ func TestAccAWSIoTPolicy_invalidJson(t *testing.T) { func TestAccAWSIoTPolicy_update(t *testing.T) { rName := acctest.RandomWithPrefix("PubSubToAnyTopic-") - expectedVersions := []string{"1", "2", "3", "5", "6"} + expectedVersions := []string{"2", "3", "4", "5", "6"} resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, From 4b776b95efa633c6a817ae3f7317a102603fe1a9 Mon Sep 17 00:00:00 2001 From: Octogonapus Date: Thu, 9 Nov 2023 16:15:20 -0500 Subject: [PATCH 04/28] fix: Add retry for eventual consistency bug in aws_iot_policy --- internal/service/iot/policy.go | 55 ++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/internal/service/iot/policy.go b/internal/service/iot/policy.go index e9df2ab5d0b..0b58772668b 100644 --- a/internal/service/iot/policy.go +++ b/internal/service/iot/policy.go @@ -6,19 +6,27 @@ package iot import ( "context" "log" + "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/iot" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "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/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" ) +const ( + // Maximum amount of time for a just-removed policy attachment to propagate. + policyAttachmentTimeout = 5 * time.Minute +) + // @SDKResource("aws_iot_policy") func ResourcePolicy() *schema.Resource { return &schema.Resource{ @@ -153,11 +161,30 @@ func resourcePolicyDelete(ctx context.Context, d *schema.ResourceData, meta inte // Delete all non-default versions of the policy for _, ver := range out.PolicyVersions { if !aws.BoolValue(ver.IsDefaultVersion) { - _, err = conn.DeletePolicyVersionWithContext(ctx, &iot.DeletePolicyVersionInput{ - PolicyName: aws.String(d.Id()), - PolicyVersionId: ver.VersionId, + err = retry.RetryContext(ctx, policyAttachmentTimeout, func() *retry.RetryError { + _, err := conn.DeletePolicyVersionWithContext(ctx, &iot.DeletePolicyVersionInput{ + PolicyName: aws.String(d.Id()), + PolicyVersionId: ver.VersionId, + }) + + if tfawserr.ErrCodeEquals(err, iot.ErrCodeDeleteConflictException) { + return retry.RetryableError(err) + } + + if err != nil { + return retry.NonRetryableError(err) + } + + return nil }) + if tfresource.TimedOut(err) { + _, err = conn.DeletePolicyVersionWithContext(ctx, &iot.DeletePolicyVersionInput{ + PolicyName: aws.String(d.Id()), + PolicyVersionId: ver.VersionId, + }) + } + if tfawserr.ErrCodeEquals(err, iot.ErrCodeResourceNotFoundException) { continue } @@ -169,10 +196,28 @@ func resourcePolicyDelete(ctx context.Context, d *schema.ResourceData, meta inte } //Delete default policy version - _, err = conn.DeletePolicyWithContext(ctx, &iot.DeletePolicyInput{ - PolicyName: aws.String(d.Id()), + err = retry.RetryContext(ctx, policyAttachmentTimeout, func() *retry.RetryError { + _, err := conn.DeletePolicyWithContext(ctx, &iot.DeletePolicyInput{ + PolicyName: aws.String(d.Id()), + }) + + if tfawserr.ErrCodeEquals(err, iot.ErrCodeDeleteConflictException) { + return retry.RetryableError(err) + } + + if err != nil { + return retry.NonRetryableError(err) + } + + return nil }) + if tfresource.TimedOut(err) { + _, err = conn.DeletePolicyWithContext(ctx, &iot.DeletePolicyInput{ + PolicyName: aws.String(d.Id()), + }) + } + if tfawserr.ErrCodeEquals(err, iot.ErrCodeResourceNotFoundException) { return diags } From 1d393725c27d3bda82cc186bd311c78f4ee53c20 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 10 Nov 2023 11:16:58 -0500 Subject: [PATCH 05/28] Revert "resource/aws_iot_policy: Switch old version pruning to use VersionId instead of CreateDate" This reverts commit 5d528f6a9e43f1ba010fb4fdad5ca229bbd68a2a. --- aws/resource_aws_iot_policy.go | 13 ++----------- aws/resource_aws_iot_policy_test.go | 2 +- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/aws/resource_aws_iot_policy.go b/aws/resource_aws_iot_policy.go index fbe5a0c0b0d..6a720e866c9 100644 --- a/aws/resource_aws_iot_policy.go +++ b/aws/resource_aws_iot_policy.go @@ -3,7 +3,6 @@ package aws import ( "fmt" "log" - "strconv" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/iot" @@ -148,22 +147,14 @@ func iotPolicyPruneVersions(name string, iotconn *iot.IoT) error { } var oldestVersion *iot.PolicyVersion - var oldestVersionId int64 for _, version := range versions { if *version.IsDefaultVersion { continue } - - versionId, err := strconv.ParseInt(*version.VersionId, 10, 64) - if err != nil { - log.Printf("[ERROR] Unexpected version id cannot be parsed to an integer. %s", err) - return err - } - - if oldestVersion == nil || versionId < oldestVersionId { + if oldestVersion == nil || + version.CreateDate.Before(*oldestVersion.CreateDate) { oldestVersion = version - oldestVersionId = versionId } } diff --git a/aws/resource_aws_iot_policy_test.go b/aws/resource_aws_iot_policy_test.go index 2b0f96382f3..d03cf08dcf8 100644 --- a/aws/resource_aws_iot_policy_test.go +++ b/aws/resource_aws_iot_policy_test.go @@ -52,7 +52,7 @@ func TestAccAWSIoTPolicy_invalidJson(t *testing.T) { func TestAccAWSIoTPolicy_update(t *testing.T) { rName := acctest.RandomWithPrefix("PubSubToAnyTopic-") - expectedVersions := []string{"2", "3", "4", "5", "6"} + expectedVersions := []string{"1", "2", "3", "5", "6"} resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, From 6932cf3c3be3a693ef542628116fd3dd499fbe08 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 10 Nov 2023 11:17:11 -0500 Subject: [PATCH 06/28] Revert "resource/aws_iot_policy: Add an acceptance test for the resource update" This reverts commit 9852ae0e6342dbcb2720de02be37faccfe4114be. --- aws/resource_aws_iot_policy_test.go | 117 ---------------------------- 1 file changed, 117 deletions(-) diff --git a/aws/resource_aws_iot_policy_test.go b/aws/resource_aws_iot_policy_test.go index d03cf08dcf8..ce0e6b53d7a 100644 --- a/aws/resource_aws_iot_policy_test.go +++ b/aws/resource_aws_iot_policy_test.go @@ -50,59 +50,6 @@ func TestAccAWSIoTPolicy_invalidJson(t *testing.T) { }) } -func TestAccAWSIoTPolicy_update(t *testing.T) { - rName := acctest.RandomWithPrefix("PubSubToAnyTopic-") - expectedVersions := []string{"1", "2", "3", "5", "6"} - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSIoTPolicyDestroy_basic, - Steps: []resource.TestStep{ - { - Config: testAccAWSIoTPolicyConfigInitialState(rName), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("aws_iot_policy.pubsub", "name", rName), - resource.TestCheckResourceAttrSet("aws_iot_policy.pubsub", "arn"), - resource.TestCheckResourceAttr("aws_iot_policy.pubsub", "default_version_id", "1"), - resource.TestCheckResourceAttrSet("aws_iot_policy.pubsub", "policy"), - ), - }, - { - Config: testAccAWSIoTPolicyConfig_updatePolicy(rName, "topic2"), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("aws_iot_policy.pubsub", "default_version_id", "2"), - ), - }, - { - Config: testAccAWSIoTPolicyConfig_updatePolicy(rName, "topic3"), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("aws_iot_policy.pubsub", "default_version_id", "3"), - ), - }, - { - Config: testAccAWSIoTPolicyConfig_updatePolicy(rName, "topic4"), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("aws_iot_policy.pubsub", "default_version_id", "4"), - ), - }, - { - Config: testAccAWSIoTPolicyConfig_updatePolicy(rName, "topic5"), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("aws_iot_policy.pubsub", "default_version_id", "5"), - ), - }, - { - Config: testAccAWSIoTPolicyConfig_updatePolicy(rName, "topic6"), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("aws_iot_policy.pubsub", "default_version_id", "6"), - testAccCheckAWSIoTPolicyVersions("aws_iot_policy.pubsub", expectedVersions), - ), - }, - }, - }) -} - func testAccCheckAWSIoTPolicyDestroy_basic(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).iotconn @@ -136,50 +83,6 @@ func testAccCheckAWSIoTPolicyDestroy_basic(s *terraform.State) error { return nil } -func testAccCheckAWSIoTPolicyVersions(rName string, expVersions []string) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[rName] - if !ok { - return fmt.Errorf("Not found: %s", rName) - } - - conn := testAccProvider.Meta().(*AWSClient).iotconn - params := &iot.ListPolicyVersionsInput{ - PolicyName: aws.String(rs.Primary.Attributes["name"]), - } - - resp, err := conn.ListPolicyVersions(params) - if err != nil { - return err - } - - if len(expVersions) != len(resp.PolicyVersions) { - return fmt.Errorf("Expected %d versions, got %d", len(expVersions), len(resp.PolicyVersions)) - } - - var actVersions []string - for _, actVer := range resp.PolicyVersions { - actVersions = append(actVersions, *(actVer.VersionId)) - } - - matchedValue := false - for _, actVer := range actVersions { - matchedValue = false - for _, expVer := range expVersions { - if actVer == expVer { - matchedValue = true - break - } - } - if !matchedValue { - return fmt.Errorf("Expected: %v / Got: %v", expVersions, actVersions) - } - } - - return nil - } -} - func testAccAWSIoTPolicyConfigInitialState(rName string) string { return fmt.Sprintf(` resource "aws_iot_policy" "pubsub" { @@ -217,23 +120,3 @@ EOF } `, rName) } - -func testAccAWSIoTPolicyConfig_updatePolicy(rName string, topicName string) string { - return fmt.Sprintf(` -resource "aws_iot_policy" "pubsub" { - name = "%s" - - policy = < Date: Fri, 10 Nov 2023 11:17:17 -0500 Subject: [PATCH 07/28] Revert "resource/aws_iot_policy: Delete oldest policy version when max number is reached" This reverts commit 784897a68cd932cac6bb5afd8c65db7a77a1ed2f. --- aws/resource_aws_iot_policy.go | 57 ---------------------------------- 1 file changed, 57 deletions(-) diff --git a/aws/resource_aws_iot_policy.go b/aws/resource_aws_iot_policy.go index 6a720e866c9..724540e7404 100644 --- a/aws/resource_aws_iot_policy.go +++ b/aws/resource_aws_iot_policy.go @@ -1,7 +1,6 @@ package aws import ( - "fmt" "log" "github.com/aws/aws-sdk-go/aws" @@ -76,10 +75,6 @@ func resourceAwsIotPolicyRead(d *schema.ResourceData, meta interface{}) error { func resourceAwsIotPolicyUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).iotconn - if err := iotPolicyPruneVersions(d.Id(), conn); err != nil { - return err - } - if d.HasChange("policy") { _, err := conn.CreatePolicyVersion(&iot.CreatePolicyVersionInput{ PolicyName: aws.String(d.Id()), @@ -134,55 +129,3 @@ func resourceAwsIotPolicyDelete(d *schema.ResourceData, meta interface{}) error return nil } - -// iotPolicyPruneVersions deletes the oldest non-default version if the maximum -// number of versions (5) has been reached. -func iotPolicyPruneVersions(name string, iotconn *iot.IoT) error { - versions, err := iotPolicyListVersions(name, iotconn) - if err != nil { - return err - } - if len(versions) < 5 { - return nil - } - - var oldestVersion *iot.PolicyVersion - - for _, version := range versions { - if *version.IsDefaultVersion { - continue - } - if oldestVersion == nil || - version.CreateDate.Before(*oldestVersion.CreateDate) { - oldestVersion = version - } - } - - err = iotPolicyDeleteVersion(name, *oldestVersion.VersionId, iotconn) - return err -} - -func iotPolicyListVersions(name string, iotconn *iot.IoT) ([]*iot.PolicyVersion, error) { - request := &iot.ListPolicyVersionsInput{ - PolicyName: aws.String(name), - } - - response, err := iotconn.ListPolicyVersions(request) - if err != nil { - return nil, fmt.Errorf("Error listing versions for IoT policy %s: %s", name, err) - } - return response.PolicyVersions, nil -} - -func iotPolicyDeleteVersion(name, versionID string, iotconn *iot.IoT) error { - request := &iot.DeletePolicyVersionInput{ - PolicyName: aws.String(name), - PolicyVersionId: aws.String(versionID), - } - - _, err := iotconn.DeletePolicyVersion(request) - if err != nil { - return fmt.Errorf("Error deleting version %s from IoT policy %s: %s", versionID, name, err) - } - return nil -} From 7a2b6c6ee281d56a83d3e20b157e2cbc124b4b93 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 10 Nov 2023 11:26:03 -0500 Subject: [PATCH 08/28] r/aws_iot_policy: Alphabetize attributes. --- internal/service/iot/policy.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/internal/service/iot/policy.go b/internal/service/iot/policy.go index 0b58772668b..cd97bca01ca 100644 --- a/internal/service/iot/policy.go +++ b/internal/service/iot/policy.go @@ -34,11 +34,20 @@ func ResourcePolicy() *schema.Resource { ReadWithoutTimeout: resourcePolicyRead, UpdateWithoutTimeout: resourcePolicyUpdate, DeleteWithoutTimeout: resourcePolicyDelete, + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "default_version_id": { + Type: schema.TypeString, + Computed: true, + }, "name": { Type: schema.TypeString, Required: true, @@ -55,14 +64,6 @@ func ResourcePolicy() *schema.Resource { return json }, }, - "arn": { - Type: schema.TypeString, - Computed: true, - }, - "default_version_id": { - Type: schema.TypeString, - Computed: true, - }, }, } } From b91588de9045a519980032601e583d463483f7ea Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 10 Nov 2023 11:31:28 -0500 Subject: [PATCH 09/28] r/aws_iot_policy: Tidy up Create. --- internal/service/iot/policy.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/internal/service/iot/policy.go b/internal/service/iot/policy.go index cd97bca01ca..e014223e375 100644 --- a/internal/service/iot/policy.go +++ b/internal/service/iot/policy.go @@ -77,16 +77,19 @@ func resourcePolicyCreate(ctx context.Context, d *schema.ResourceData, meta inte return sdkdiag.AppendErrorf(diags, "policy (%s) is invalid JSON: %s", policy, err) } - out, err := conn.CreatePolicyWithContext(ctx, &iot.CreatePolicyInput{ - PolicyName: aws.String(d.Get("name").(string)), + name := d.Get("name").(string) + input := &iot.CreatePolicyInput{ + PolicyName: aws.String(name), PolicyDocument: aws.String(policy), - }) + } + + output, err := conn.CreatePolicyWithContext(ctx, input) if err != nil { - return sdkdiag.AppendErrorf(diags, "creating IoT Policy: %s", err) + return sdkdiag.AppendErrorf(diags, "creating IoT Policy (%s): %s", name, err) } - d.SetId(aws.StringValue(out.PolicyName)) + d.SetId(aws.StringValue(output.PolicyName)) return append(diags, resourcePolicyRead(ctx, d, meta)...) } From 2b27c8a6f729c3a64d019caa9ec17549dd490647 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 10 Nov 2023 11:33:00 -0500 Subject: [PATCH 10/28] r/aws_iot_policy: Tidy up Update. --- internal/service/iot/policy.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/internal/service/iot/policy.go b/internal/service/iot/policy.go index e014223e375..7bd676b3cdd 100644 --- a/internal/service/iot/policy.go +++ b/internal/service/iot/policy.go @@ -79,8 +79,8 @@ func resourcePolicyCreate(ctx context.Context, d *schema.ResourceData, meta inte name := d.Get("name").(string) input := &iot.CreatePolicyInput{ - PolicyName: aws.String(name), PolicyDocument: aws.String(policy), + PolicyName: aws.String(name), } output, err := conn.CreatePolicyWithContext(ctx, input) @@ -130,21 +130,21 @@ func resourcePolicyUpdate(ctx context.Context, d *schema.ResourceData, meta inte var diags diag.Diagnostics conn := meta.(*conns.AWSClient).IoTConn(ctx) - if d.HasChange("policy") { - policy, err := structure.NormalizeJsonString(d.Get("policy").(string)) - if err != nil { - return sdkdiag.AppendErrorf(diags, "policy (%s) is invalid JSON: %s", policy, err) - } + policy, err := structure.NormalizeJsonString(d.Get("policy").(string)) + if err != nil { + return sdkdiag.AppendErrorf(diags, "policy (%s) is invalid JSON: %s", policy, err) + } - _, err = conn.CreatePolicyVersionWithContext(ctx, &iot.CreatePolicyVersionInput{ - PolicyName: aws.String(d.Id()), - PolicyDocument: aws.String(policy), - SetAsDefault: aws.Bool(true), - }) + input := &iot.CreatePolicyVersionInput{ + PolicyDocument: aws.String(policy), + PolicyName: aws.String(d.Id()), + SetAsDefault: aws.Bool(true), + } - if err != nil { - return sdkdiag.AppendErrorf(diags, "updating IoT Policy (%s): %s", d.Id(), err) - } + _, err = conn.CreatePolicyVersionWithContext(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "updating IoT Policy (%s): %s", d.Id(), err) } return append(diags, resourcePolicyRead(ctx, d, meta)...) From 9540f233173aaa3f8f66f0175e1f3fc40135b69c Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 10 Nov 2023 11:37:17 -0500 Subject: [PATCH 11/28] r/aws_iot_policy: Tidy up Read. --- internal/service/iot/policy.go | 41 ++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/internal/service/iot/policy.go b/internal/service/iot/policy.go index 7bd676b3cdd..02facb6f3a1 100644 --- a/internal/service/iot/policy.go +++ b/internal/service/iot/policy.go @@ -98,11 +98,9 @@ func resourcePolicyRead(ctx context.Context, d *schema.ResourceData, meta interf var diags diag.Diagnostics conn := meta.(*conns.AWSClient).IoTConn(ctx) - out, err := conn.GetPolicyWithContext(ctx, &iot.GetPolicyInput{ - PolicyName: aws.String(d.Id()), - }) + output, err := FindPolicyByName(ctx, conn, d.Id()) - if tfawserr.ErrCodeEquals(err, iot.ErrCodeResourceNotFoundException) { + if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] IoT Policy (%s) not found, removing from state", d.Id()) d.SetId("") return diags @@ -112,13 +110,13 @@ func resourcePolicyRead(ctx context.Context, d *schema.ResourceData, meta interf return sdkdiag.AppendErrorf(diags, "reading IoT Policy (%s): %s", d.Id(), err) } - d.Set("arn", out.PolicyArn) - d.Set("default_version_id", out.DefaultVersionId) - d.Set("name", out.PolicyName) + d.Set("arn", output.PolicyArn) + d.Set("default_version_id", output.DefaultVersionId) + d.Set("name", output.PolicyName) - policyToSet, err := verify.PolicyToSet(d.Get("policy").(string), aws.StringValue(out.PolicyDocument)) + policyToSet, err := verify.PolicyToSet(d.Get("policy").(string), aws.StringValue(output.PolicyDocument)) if err != nil { - return sdkdiag.AppendErrorf(diags, "reading IoT Policy (%s): %s", d.Id(), err) + return sdkdiag.AppendFromErr(diags, err) } d.Set("policy", policyToSet) @@ -232,3 +230,28 @@ func resourcePolicyDelete(ctx context.Context, d *schema.ResourceData, meta inte return diags } + +func FindPolicyByName(ctx context.Context, conn *iot.IoT, name string) (*iot.GetPolicyOutput, error) { + input := &iot.GetPolicyInput{ + PolicyName: aws.String(name), + } + + output, err := conn.GetPolicyWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, iot.ErrCodeResourceNotFoundException) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, nil +} From d2d01b8c0c3024899ad210a3c3afc0735f61c808 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 10 Nov 2023 11:52:40 -0500 Subject: [PATCH 12/28] r/aws_iot_policy: Tidy up Delete. --- internal/service/iot/policy.go | 145 ++++++++++++++++++--------------- 1 file changed, 81 insertions(+), 64 deletions(-) diff --git a/internal/service/iot/policy.go b/internal/service/iot/policy.go index 02facb6f3a1..24146fcd7ad 100644 --- a/internal/service/iot/policy.go +++ b/internal/service/iot/policy.go @@ -5,6 +5,7 @@ package iot import ( "context" + "fmt" "log" "time" @@ -152,80 +153,30 @@ func resourcePolicyDelete(ctx context.Context, d *schema.ResourceData, meta inte var diags diag.Diagnostics conn := meta.(*conns.AWSClient).IoTConn(ctx) - out, err := conn.ListPolicyVersionsWithContext(ctx, &iot.ListPolicyVersionsInput{ - PolicyName: aws.String(d.Id()), - }) + policyVersions, err := findPolicyVersionsByName(ctx, conn, d.Id()) - if err != nil { - return sdkdiag.AppendErrorf(diags, "listing IoT Policy (%s) versions: %s", d.Id(), err) + if tfresource.NotFound(err) { + return diags } - // Delete all non-default versions of the policy - for _, ver := range out.PolicyVersions { - if !aws.BoolValue(ver.IsDefaultVersion) { - err = retry.RetryContext(ctx, policyAttachmentTimeout, func() *retry.RetryError { - _, err := conn.DeletePolicyVersionWithContext(ctx, &iot.DeletePolicyVersionInput{ - PolicyName: aws.String(d.Id()), - PolicyVersionId: ver.VersionId, - }) - - if tfawserr.ErrCodeEquals(err, iot.ErrCodeDeleteConflictException) { - return retry.RetryableError(err) - } - - if err != nil { - return retry.NonRetryableError(err) - } - - return nil - }) - - if tfresource.TimedOut(err) { - _, err = conn.DeletePolicyVersionWithContext(ctx, &iot.DeletePolicyVersionInput{ - PolicyName: aws.String(d.Id()), - PolicyVersionId: ver.VersionId, - }) - } - - if tfawserr.ErrCodeEquals(err, iot.ErrCodeResourceNotFoundException) { - continue - } - - if err != nil { - return sdkdiag.AppendErrorf(diags, "deleting IoT Policy (%s) version (%s): %s", d.Id(), aws.StringValue(ver.VersionId), err) - } - } + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading IoT Policy (%s) versions: %s", d.Id(), err) } - //Delete default policy version - err = retry.RetryContext(ctx, policyAttachmentTimeout, func() *retry.RetryError { - _, err := conn.DeletePolicyWithContext(ctx, &iot.DeletePolicyInput{ - PolicyName: aws.String(d.Id()), - }) - - if tfawserr.ErrCodeEquals(err, iot.ErrCodeDeleteConflictException) { - return retry.RetryableError(err) + // Delete all non-default versions of the policy. + for _, v := range policyVersions { + if aws.BoolValue(v.IsDefaultVersion) { + continue } - if err != nil { - return retry.NonRetryableError(err) + if err := deletePolicyVersion(ctx, conn, d.Id(), aws.StringValue(v.VersionId)); err != nil { + return sdkdiag.AppendFromErr(diags, err) } - - return nil - }) - - if tfresource.TimedOut(err) { - _, err = conn.DeletePolicyWithContext(ctx, &iot.DeletePolicyInput{ - PolicyName: aws.String(d.Id()), - }) } - if tfawserr.ErrCodeEquals(err, iot.ErrCodeResourceNotFoundException) { - return diags - } - - if err != nil { - return sdkdiag.AppendErrorf(diags, "deleting IoT Policy (%s): %s", d.Id(), err) + // Delete default policy version. + if err := deletePolicy(ctx, conn, d.Id()); err != nil { + return sdkdiag.AppendFromErr(diags, err) } return diags @@ -255,3 +206,69 @@ func FindPolicyByName(ctx context.Context, conn *iot.IoT, name string) (*iot.Get return output, nil } + +func findPolicyVersionsByName(ctx context.Context, conn *iot.IoT, name string) ([]*iot.PolicyVersion, error) { + input := &iot.ListPolicyVersionsInput{ + PolicyName: aws.String(name), + } + + output, err := conn.ListPolicyVersionsWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, iot.ErrCodeResourceNotFoundException) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || len(output.PolicyVersions) == 0 { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.PolicyVersions, nil +} + +func deletePolicy(ctx context.Context, conn *iot.IoT, name string) error { + input := &iot.DeletePolicyInput{ + PolicyName: aws.String(name), + } + + _, err := tfresource.RetryWhenAWSErrCodeEquals(ctx, policyAttachmentTimeout, func() (interface{}, error) { + return conn.DeletePolicyWithContext(ctx, input) + }, iot.ErrCodeDeleteConflictException) + + if tfawserr.ErrCodeEquals(err, iot.ErrCodeResourceNotFoundException) { + return nil + } + + if err != nil { + return fmt.Errorf("deleting IoT Policy (%s): %w", name, err) + } + + return nil +} + +func deletePolicyVersion(ctx context.Context, conn *iot.IoT, name, versionID string) error { + input := &iot.DeletePolicyVersionInput{ + PolicyName: aws.String(name), + PolicyVersionId: aws.String(versionID), + } + + _, err := tfresource.RetryWhenAWSErrCodeEquals(ctx, policyAttachmentTimeout, func() (interface{}, error) { + return conn.DeletePolicyVersionWithContext(ctx, input) + }, iot.ErrCodeDeleteConflictException) + + if tfawserr.ErrCodeEquals(err, iot.ErrCodeResourceNotFoundException) { + return nil + } + + if err != nil { + return fmt.Errorf("deleting IoT Policy (%s) version (%s): %w", name, versionID, err) + } + + return nil +} From 692979cf4f3b0b1e67f8b559371cc859a6a6e48c Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 10 Nov 2023 11:56:58 -0500 Subject: [PATCH 13/28] r/aws_iot_policy: Tidy up acceptance tests. --- internal/service/iot/policy_test.go | 52 +++++++++++------------------ 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/internal/service/iot/policy_test.go b/internal/service/iot/policy_test.go index 531a0b52360..4c69e1c62cc 100644 --- a/internal/service/iot/policy_test.go +++ b/internal/service/iot/policy_test.go @@ -8,15 +8,14 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/iot" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfiot "github.com/hashicorp/terraform-provider-aws/internal/service/iot" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccIoTPolicy_basic(t *testing.T) { @@ -29,15 +28,15 @@ func TestAccIoTPolicy_basic(t *testing.T) { PreCheck: func() { acctest.PreCheck(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, iot.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckPolicyDestroy_basic(ctx), + CheckDestroy: testAccCheckPolicyDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccPolicyConfig_initialState(rName), - Check: resource.ComposeTestCheckFunc( + Config: testAccPolicyConfig_basic(rName), + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckPolicyExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "name", rName), acctest.CheckResourceAttrRegionalARN(resourceName, "arn", "iot", fmt.Sprintf("policy/%s", rName)), resource.TestCheckResourceAttrSet(resourceName, "default_version_id"), + resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttrSet(resourceName, "policy"), ), }, @@ -60,10 +59,10 @@ func TestAccIoTPolicy_disappears(t *testing.T) { PreCheck: func() { acctest.PreCheck(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, iot.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckPolicyDestroy_basic(ctx), + CheckDestroy: testAccCheckPolicyDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccPolicyConfig_initialState(rName), + Config: testAccPolicyConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckPolicyExists(ctx, resourceName, &v), acctest.CheckResourceDisappears(ctx, acctest.Provider, tfiot.ResourcePolicy(), resourceName), @@ -74,7 +73,7 @@ func TestAccIoTPolicy_disappears(t *testing.T) { }) } -func testAccCheckPolicyDestroy_basic(ctx context.Context) resource.TestCheckFunc { +func testAccCheckPolicyDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).IoTConn(ctx) @@ -83,24 +82,17 @@ func testAccCheckPolicyDestroy_basic(ctx context.Context) resource.TestCheckFunc continue } - // Try to find the Policy - GetPolicyOpts := &iot.GetPolicyInput{ - PolicyName: aws.String(rs.Primary.Attributes["name"]), - } - - resp, err := conn.GetPolicyWithContext(ctx, GetPolicyOpts) + _, err := tfiot.FindPolicyByName(ctx, conn, rs.Primary.ID) - if err == nil { - if resp.PolicyName != nil { - return fmt.Errorf("IoT Policy still exists") - } + if tfresource.NotFound(err) { + continue } if err != nil { - if !tfawserr.ErrCodeEquals(err, iot.ErrCodeResourceNotFoundException) { - return err - } + return err } + + return fmt.Errorf("IoT Policy %s still exists", rs.Primary.ID) } return nil @@ -114,29 +106,24 @@ func testAccCheckPolicyExists(ctx context.Context, n string, v *iot.GetPolicyOut return fmt.Errorf("Not found: %s", n) } - if rs.Primary.ID == "" { - return fmt.Errorf("No IoT Policy ID is set") - } - conn := acctest.Provider.Meta().(*conns.AWSClient).IoTConn(ctx) - resp, err := conn.GetPolicyWithContext(ctx, &iot.GetPolicyInput{ - PolicyName: aws.String(rs.Primary.ID), - }) + output, err := tfiot.FindPolicyByName(ctx, conn, rs.Primary.ID) + if err != nil { return err } - *v = *resp + *v = *output return nil } } -func testAccPolicyConfig_initialState(rName string) string { +func testAccPolicyConfig_basic(rName string) string { return fmt.Sprintf(` resource "aws_iot_policy" "test" { - name = "%s" + name = %[1]q policy = < Date: Fri, 10 Nov 2023 12:05:29 -0500 Subject: [PATCH 14/28] Add CHANGELOG entry. --- .changelog/34329.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/34329.txt diff --git a/.changelog/34329.txt b/.changelog/34329.txt new file mode 100644 index 00000000000..41fbf7005b0 --- /dev/null +++ b/.changelog/34329.txt @@ -0,0 +1,3 @@ +```release-note:bug +resource/aws_iot_policy: Retry `DeleteConflictException` errors on delete +``` \ No newline at end of file From a7edf477037174e4987153046fbfbf88dd811870 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 10 Nov 2023 12:09:34 -0500 Subject: [PATCH 15/28] r/aws_iot_policy: Add configurable timeouts. --- .changelog/34329.txt | 4 ++++ internal/service/iot/policy.go | 21 ++++++++++----------- website/docs/r/iot_policy.html.markdown | 6 ++++++ 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/.changelog/34329.txt b/.changelog/34329.txt index 41fbf7005b0..bf405e835e9 100644 --- a/.changelog/34329.txt +++ b/.changelog/34329.txt @@ -1,3 +1,7 @@ ```release-note:bug resource/aws_iot_policy: Retry `DeleteConflictException` errors on delete +``` + +```release-note:enhancement +resource/aws_iot_policy: Add configurable timeouts ``` \ No newline at end of file diff --git a/internal/service/iot/policy.go b/internal/service/iot/policy.go index 24146fcd7ad..de4b1c96cf5 100644 --- a/internal/service/iot/policy.go +++ b/internal/service/iot/policy.go @@ -23,11 +23,6 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/verify" ) -const ( - // Maximum amount of time for a just-removed policy attachment to propagate. - policyAttachmentTimeout = 5 * time.Minute -) - // @SDKResource("aws_iot_policy") func ResourcePolicy() *schema.Resource { return &schema.Resource{ @@ -40,6 +35,10 @@ func ResourcePolicy() *schema.Resource { StateContext: schema.ImportStatePassthroughContext, }, + Timeouts: &schema.ResourceTimeout{ + Delete: schema.DefaultTimeout(5 * time.Minute), + }, + Schema: map[string]*schema.Schema{ "arn": { Type: schema.TypeString, @@ -169,13 +168,13 @@ func resourcePolicyDelete(ctx context.Context, d *schema.ResourceData, meta inte continue } - if err := deletePolicyVersion(ctx, conn, d.Id(), aws.StringValue(v.VersionId)); err != nil { + if err := deletePolicyVersion(ctx, conn, d.Id(), aws.StringValue(v.VersionId), d.Timeout(schema.TimeoutDelete)); err != nil { return sdkdiag.AppendFromErr(diags, err) } } // Delete default policy version. - if err := deletePolicy(ctx, conn, d.Id()); err != nil { + if err := deletePolicy(ctx, conn, d.Id(), d.Timeout(schema.TimeoutDelete)); err != nil { return sdkdiag.AppendFromErr(diags, err) } @@ -232,12 +231,12 @@ func findPolicyVersionsByName(ctx context.Context, conn *iot.IoT, name string) ( return output.PolicyVersions, nil } -func deletePolicy(ctx context.Context, conn *iot.IoT, name string) error { +func deletePolicy(ctx context.Context, conn *iot.IoT, name string, timeout time.Duration) error { input := &iot.DeletePolicyInput{ PolicyName: aws.String(name), } - _, err := tfresource.RetryWhenAWSErrCodeEquals(ctx, policyAttachmentTimeout, func() (interface{}, error) { + _, err := tfresource.RetryWhenAWSErrCodeEquals(ctx, timeout, func() (interface{}, error) { return conn.DeletePolicyWithContext(ctx, input) }, iot.ErrCodeDeleteConflictException) @@ -252,13 +251,13 @@ func deletePolicy(ctx context.Context, conn *iot.IoT, name string) error { return nil } -func deletePolicyVersion(ctx context.Context, conn *iot.IoT, name, versionID string) error { +func deletePolicyVersion(ctx context.Context, conn *iot.IoT, name, versionID string, timeout time.Duration) error { input := &iot.DeletePolicyVersionInput{ PolicyName: aws.String(name), PolicyVersionId: aws.String(versionID), } - _, err := tfresource.RetryWhenAWSErrCodeEquals(ctx, policyAttachmentTimeout, func() (interface{}, error) { + _, err := tfresource.RetryWhenAWSErrCodeEquals(ctx, timeout, func() (interface{}, error) { return conn.DeletePolicyVersionWithContext(ctx, input) }, iot.ErrCodeDeleteConflictException) diff --git a/website/docs/r/iot_policy.html.markdown b/website/docs/r/iot_policy.html.markdown index 335d23407a5..1ffe27a48c9 100644 --- a/website/docs/r/iot_policy.html.markdown +++ b/website/docs/r/iot_policy.html.markdown @@ -49,6 +49,12 @@ This resource exports the following attributes in addition to the arguments abov * `default_version_id` - The default version of this policy. * `policy` - The policy document. +## Timeouts + +[Configuration options](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts): + +* `delete` - (Default `5m`) + ## Import In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import IoT policies using the `name`. For example: From c1560fae388c0885a0f8108ba0839e2c57fd1733 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 10 Nov 2023 12:13:15 -0500 Subject: [PATCH 16/28] Acceptance test output: % make testacc TESTARGS='-run=TestAccIoTPolicy_' PKG=iot ACCTEST_PARALLELISM=2 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/iot/... -v -count 1 -parallel 2 -run=TestAccIoTPolicy_ -timeout 360m === RUN TestAccIoTPolicy_basic === PAUSE TestAccIoTPolicy_basic === RUN TestAccIoTPolicy_disappears === PAUSE TestAccIoTPolicy_disappears === CONT TestAccIoTPolicy_basic === CONT TestAccIoTPolicy_disappears --- PASS: TestAccIoTPolicy_disappears (19.19s) --- PASS: TestAccIoTPolicy_basic (24.35s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/iot 29.768s From e4be94ab6f205dccbea1716c14b8450f0d5b68ae Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 10 Nov 2023 12:21:41 -0500 Subject: [PATCH 17/28] Add 'TestAccIoTPolicy_update'. --- internal/service/iot/policy_test.go | 62 ++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/internal/service/iot/policy_test.go b/internal/service/iot/policy_test.go index 4c69e1c62cc..52ce82c5a1f 100644 --- a/internal/service/iot/policy_test.go +++ b/internal/service/iot/policy_test.go @@ -35,7 +35,7 @@ func TestAccIoTPolicy_basic(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( testAccCheckPolicyExists(ctx, resourceName, &v), acctest.CheckResourceAttrRegionalARN(resourceName, "arn", "iot", fmt.Sprintf("policy/%s", rName)), - resource.TestCheckResourceAttrSet(resourceName, "default_version_id"), + resource.TestCheckResourceAttr(resourceName, "default_version_id", "1"), resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttrSet(resourceName, "policy"), ), @@ -73,6 +73,41 @@ func TestAccIoTPolicy_disappears(t *testing.T) { }) } +func TestAccIoTPolicy_update(t *testing.T) { + ctx := acctest.Context(t) + var v iot.GetPolicyOutput + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iot_policy.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iot.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckPolicyDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), + Check: resource.ComposeTestCheckFunc( + testAccCheckPolicyExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "default_version_id", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), + Check: resource.ComposeTestCheckFunc( + testAccCheckPolicyExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "default_version_id", "2"), + ), + }, + }, + }) +} + func testAccCheckPolicyDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).IoTConn(ctx) @@ -144,3 +179,28 @@ EOF } `, rName) } + +func testAccPolicyConfig_resourceName(rName, resourceName string) string { + return fmt.Sprintf(` +resource "aws_iot_policy" "test" { + name = %[1]q + + policy = < Date: Fri, 10 Nov 2023 12:27:21 -0500 Subject: [PATCH 18/28] Acceptance test output: % make testacc TESTARGS='-run=TestAccIoTPolicy_' PKG=iot ACCTEST_PARALLELISM=2 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/iot/... -v -count 1 -parallel 2 -run=TestAccIoTPolicy_ -timeout 360m === RUN TestAccIoTPolicy_basic === PAUSE TestAccIoTPolicy_basic === RUN TestAccIoTPolicy_disappears === PAUSE TestAccIoTPolicy_disappears === RUN TestAccIoTPolicy_update === PAUSE TestAccIoTPolicy_update === CONT TestAccIoTPolicy_basic === CONT TestAccIoTPolicy_update --- PASS: TestAccIoTPolicy_basic (32.82s) === CONT TestAccIoTPolicy_disappears --- PASS: TestAccIoTPolicy_update (48.98s) --- PASS: TestAccIoTPolicy_disappears (17.41s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/iot 57.992s From badbd6fb924a5991e0aa7c4cda72c6eac3de63e4 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 10 Nov 2023 13:52:40 -0500 Subject: [PATCH 19/28] Add 'TestAccIoTPolicy_prune'. --- internal/service/iot/policy.go | 4 +- internal/service/iot/policy_test.go | 105 ++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 2 deletions(-) diff --git a/internal/service/iot/policy.go b/internal/service/iot/policy.go index de4b1c96cf5..2801f92192d 100644 --- a/internal/service/iot/policy.go +++ b/internal/service/iot/policy.go @@ -152,7 +152,7 @@ func resourcePolicyDelete(ctx context.Context, d *schema.ResourceData, meta inte var diags diag.Diagnostics conn := meta.(*conns.AWSClient).IoTConn(ctx) - policyVersions, err := findPolicyVersionsByName(ctx, conn, d.Id()) + policyVersions, err := FindPolicyVersionsByName(ctx, conn, d.Id()) if tfresource.NotFound(err) { return diags @@ -206,7 +206,7 @@ func FindPolicyByName(ctx context.Context, conn *iot.IoT, name string) (*iot.Get return output, nil } -func findPolicyVersionsByName(ctx context.Context, conn *iot.IoT, name string) ([]*iot.PolicyVersion, error) { +func FindPolicyVersionsByName(ctx context.Context, conn *iot.IoT, name string) ([]*iot.PolicyVersion, error) { input := &iot.ListPolicyVersionsInput{ PolicyName: aws.String(name), } diff --git a/internal/service/iot/policy_test.go b/internal/service/iot/policy_test.go index 52ce82c5a1f..7ca34d1eecd 100644 --- a/internal/service/iot/policy_test.go +++ b/internal/service/iot/policy_test.go @@ -8,13 +8,17 @@ import ( "fmt" "testing" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/iot" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfiot "github.com/hashicorp/terraform-provider-aws/internal/service/iot" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) @@ -108,6 +112,78 @@ func TestAccIoTPolicy_update(t *testing.T) { }) } +func TestAccIoTPolicy_prune(t *testing.T) { + ctx := acctest.Context(t) + var v iot.GetPolicyOutput + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iot_policy.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iot.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckPolicyDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), + Check: resource.ComposeTestCheckFunc( + testAccCheckPolicyExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "default_version_id", "1"), + testAccCheckPolicyVersionIDs(ctx, resourceName, []string{"1"}), + ), + }, + { + Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), + Check: resource.ComposeTestCheckFunc( + testAccCheckPolicyExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "default_version_id", "2"), + testAccCheckPolicyVersionIDs(ctx, resourceName, []string{"1", "2"}), + ), + }, + { + Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), + Check: resource.ComposeTestCheckFunc( + testAccCheckPolicyExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "default_version_id", "3"), + testAccCheckPolicyVersionIDs(ctx, resourceName, []string{"1", "2", "3"}), + ), + }, + { + Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), + Check: resource.ComposeTestCheckFunc( + testAccCheckPolicyExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "default_version_id", "4"), + testAccCheckPolicyVersionIDs(ctx, resourceName, []string{"1", "2", "3", "4"}), + ), + }, + { + Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), + Check: resource.ComposeTestCheckFunc( + testAccCheckPolicyExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "default_version_id", "5"), + testAccCheckPolicyVersionIDs(ctx, resourceName, []string{"1", "2", "3", "4", "5"}), + ), + }, + { + Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), + Check: resource.ComposeTestCheckFunc( + testAccCheckPolicyExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "default_version_id", "6"), + testAccCheckPolicyVersionIDs(ctx, resourceName, []string{"2", "3", "4", "5", "6"}), + ), + }, + { + Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), + Check: resource.ComposeTestCheckFunc( + testAccCheckPolicyExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "default_version_id", "7"), + testAccCheckPolicyVersionIDs(ctx, resourceName, []string{"3", "4", "5", "6", "7"}), + ), + }, + }, + }) +} + func testAccCheckPolicyDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).IoTConn(ctx) @@ -155,6 +231,35 @@ func testAccCheckPolicyExists(ctx context.Context, n string, v *iot.GetPolicyOut } } +func testAccCheckPolicyVersionIDs(ctx context.Context, n string, want []string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).IoTConn(ctx) + + output, err := tfiot.FindPolicyVersionsByName(ctx, conn, rs.Primary.ID) + + if err != nil { + return err + } + + got := tfslices.ApplyToAll(output, func(v *iot.PolicyVersion) string { + return aws.StringValue(v.VersionId) + }) + + if !cmp.Equal(got, want, cmpopts.SortSlices(func(i, j string) bool { + return i < j + })) { + return fmt.Errorf("policy version IDs = %v, want = %v", got, want) + } + + return nil + } +} + func testAccPolicyConfig_basic(rName string) string { return fmt.Sprintf(` resource "aws_iot_policy" "test" { From c127f069f3198c133ed4e945a3c49a9d83bc5925 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 10 Nov 2023 15:38:03 -0500 Subject: [PATCH 20/28] r/aws_iot_policy: When updating the resource, delete the oldest non-default version of the policy if creating a new version would exceed the maximum number of versions (5). --- .changelog/34329.txt | 4 ++ internal/service/iot/policy.go | 49 +++++++++++++++++++++++-- website/docs/r/iot_policy.html.markdown | 3 ++ 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/.changelog/34329.txt b/.changelog/34329.txt index bf405e835e9..75e139100d7 100644 --- a/.changelog/34329.txt +++ b/.changelog/34329.txt @@ -4,4 +4,8 @@ resource/aws_iot_policy: Retry `DeleteConflictException` errors on delete ```release-note:enhancement resource/aws_iot_policy: Add configurable timeouts +``` + +```release-note:enhancement +resource/aws_iot_policy: When updating the resource, delete the oldest non-default version of the policy if creating a new version would exceed the maximum number of versions (5) ``` \ No newline at end of file diff --git a/internal/service/iot/policy.go b/internal/service/iot/policy.go index 2801f92192d..bd8b8569cf4 100644 --- a/internal/service/iot/policy.go +++ b/internal/service/iot/policy.go @@ -7,6 +7,7 @@ import ( "context" "fmt" "log" + "strconv" "time" "github.com/aws/aws-sdk-go/aws" @@ -21,6 +22,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" + "golang.org/x/exp/slices" ) // @SDKResource("aws_iot_policy") @@ -36,6 +38,7 @@ func ResourcePolicy() *schema.Resource { }, Timeouts: &schema.ResourceTimeout{ + Update: schema.DefaultTimeout(1 * time.Minute), Delete: schema.DefaultTimeout(5 * time.Minute), }, @@ -139,10 +142,50 @@ func resourcePolicyUpdate(ctx context.Context, d *schema.ResourceData, meta inte SetAsDefault: aws.Bool(true), } - _, err = conn.CreatePolicyVersionWithContext(ctx, input) + _, errCreate := conn.CreatePolicyVersionWithContext(ctx, input) - if err != nil { - return sdkdiag.AppendErrorf(diags, "updating IoT Policy (%s): %s", d.Id(), err) + // "VersionsLimitExceededException: The policy ... already has the maximum number of versions (5)" + if tfawserr.ErrCodeEquals(errCreate, iot.ErrCodeVersionsLimitExceededException) { + // Prune the lowest version and retry. + policyVersions, err := FindPolicyVersionsByName(ctx, conn, d.Id()) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading IoT Policy (%s) versions: %s", d.Id(), err) + } + + var versionIDs []int + + for _, v := range policyVersions { + if aws.BoolValue(v.IsDefaultVersion) { + continue + } + + if v, err := strconv.Atoi(aws.StringValue(v.VersionId)); err != nil { + continue + } else { + versionIDs = append(versionIDs, v) + } + } + + if len(versionIDs) > 0 { + // Sort ascending. + slices.Sort(versionIDs) + versionID := strconv.Itoa(versionIDs[0]) + + if err := deletePolicyVersion(ctx, conn, d.Id(), versionID, d.Timeout(schema.TimeoutUpdate)); err != nil { + return sdkdiag.AppendFromErr(diags, err) + } + + if err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for IoT Policy (%s) version (%s) delete: %s", d.Id(), versionID, err) + } + + _, errCreate = conn.CreatePolicyVersionWithContext(ctx, input) + } + } + + if errCreate != nil { + return sdkdiag.AppendErrorf(diags, "updating IoT Policy (%s): %s", d.Id(), errCreate) } return append(diags, resourcePolicyRead(ctx, d, meta)...) diff --git a/website/docs/r/iot_policy.html.markdown b/website/docs/r/iot_policy.html.markdown index 1ffe27a48c9..07506a3fdbc 100644 --- a/website/docs/r/iot_policy.html.markdown +++ b/website/docs/r/iot_policy.html.markdown @@ -10,6 +10,8 @@ description: |- Provides an IoT policy. +~> **NOTE on policy versions:** Updating this resource creates a new, default policy version. If updating the resource would exceed the maximum number of versions (5), the oldest non-default version of the policy is deleted before the new policy version is created. + ## Example Usage ```terraform @@ -53,6 +55,7 @@ This resource exports the following attributes in addition to the arguments abov [Configuration options](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts): +* `update` - (Default `1m`) * `delete` - (Default `5m`) ## Import From 384459dd7239316d5f5c044cc59c24eb815cd563 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 10 Nov 2023 15:44:59 -0500 Subject: [PATCH 21/28] r/aws_iot_policy_attachment: Correct order of CRUD handlers. --- internal/service/iot/policy_attachment.go | 81 ++++++++++++----------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/internal/service/iot/policy_attachment.go b/internal/service/iot/policy_attachment.go index 73211319818..2f810b11eff 100644 --- a/internal/service/iot/policy_attachment.go +++ b/internal/service/iot/policy_attachment.go @@ -23,6 +23,7 @@ func ResourcePolicyAttachment() *schema.Resource { CreateWithoutTimeout: resourcePolicyAttachmentCreate, ReadWithoutTimeout: resourcePolicyAttachmentRead, DeleteWithoutTimeout: resourcePolicyAttachmentDelete, + Schema: map[string]*schema.Schema{ "policy": { Type: schema.TypeString, @@ -58,46 +59,6 @@ func resourcePolicyAttachmentCreate(ctx context.Context, d *schema.ResourceData, return append(diags, resourcePolicyAttachmentRead(ctx, d, meta)...) } -func ListPolicyAttachmentPages(ctx context.Context, conn *iot.IoT, input *iot.ListAttachedPoliciesInput, - fn func(out *iot.ListAttachedPoliciesOutput, lastPage bool) bool) error { - for { - page, err := conn.ListAttachedPoliciesWithContext(ctx, input) - if err != nil { - return err - } - lastPage := page.NextMarker == nil - - shouldContinue := fn(page, lastPage) - if !shouldContinue || lastPage { - break - } - input.Marker = page.NextMarker - } - return nil -} - -func GetPolicyAttachment(ctx context.Context, conn *iot.IoT, target, policyName string) (*iot.Policy, error) { - var policy *iot.Policy - - input := &iot.ListAttachedPoliciesInput{ - PageSize: aws.Int64(250), - Recursive: aws.Bool(false), - Target: aws.String(target), - } - - err := ListPolicyAttachmentPages(ctx, conn, input, func(out *iot.ListAttachedPoliciesOutput, lastPage bool) bool { - for _, att := range out.Policies { - if policyName == aws.StringValue(att.PolicyName) { - policy = att - return false - } - } - return true - }) - - return policy, err -} - func resourcePolicyAttachmentRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).IoTConn(ctx) @@ -147,3 +108,43 @@ func resourcePolicyAttachmentDelete(ctx context.Context, d *schema.ResourceData, return diags } + +func ListPolicyAttachmentPages(ctx context.Context, conn *iot.IoT, input *iot.ListAttachedPoliciesInput, + fn func(out *iot.ListAttachedPoliciesOutput, lastPage bool) bool) error { + for { + page, err := conn.ListAttachedPoliciesWithContext(ctx, input) + if err != nil { + return err + } + lastPage := page.NextMarker == nil + + shouldContinue := fn(page, lastPage) + if !shouldContinue || lastPage { + break + } + input.Marker = page.NextMarker + } + return nil +} + +func GetPolicyAttachment(ctx context.Context, conn *iot.IoT, target, policyName string) (*iot.Policy, error) { + var policy *iot.Policy + + input := &iot.ListAttachedPoliciesInput{ + PageSize: aws.Int64(250), + Recursive: aws.Bool(false), + Target: aws.String(target), + } + + err := ListPolicyAttachmentPages(ctx, conn, input, func(out *iot.ListAttachedPoliciesOutput, lastPage bool) bool { + for _, att := range out.Policies { + if policyName == aws.StringValue(att.PolicyName) { + policy = att + return false + } + } + return true + }) + + return policy, err +} From 0f3043f809f5bbf561c4c3dbbe46f1307950297a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 10 Nov 2023 15:49:06 -0500 Subject: [PATCH 22/28] r/aws_iot_policy_attachment: Tidy up Create. --- internal/service/iot/policy_attachment.go | 33 +++++++++++++++++++---- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/internal/service/iot/policy_attachment.go b/internal/service/iot/policy_attachment.go index 2f810b11eff..5cc059ff985 100644 --- a/internal/service/iot/policy_attachment.go +++ b/internal/service/iot/policy_attachment.go @@ -7,6 +7,7 @@ import ( "context" "fmt" "log" + "strings" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/iot" @@ -45,17 +46,20 @@ func resourcePolicyAttachmentCreate(ctx context.Context, d *schema.ResourceData, policyName := d.Get("policy").(string) target := d.Get("target").(string) - - _, err := conn.AttachPolicyWithContext(ctx, &iot.AttachPolicyInput{ + id := policyAttachmentCreateResourceID(policyName, target) + input := &iot.AttachPolicyInput{ PolicyName: aws.String(policyName), Target: aws.String(target), - }) + } + + _, err := conn.AttachPolicyWithContext(ctx, input) if err != nil { - return sdkdiag.AppendErrorf(diags, "attaching policy %s to target %s: %s", policyName, target, err) + return sdkdiag.AppendErrorf(diags, "creating IoT Policy Attachment (%s): %s", id, err) } - d.SetId(fmt.Sprintf("%s|%s", policyName, target)) + d.SetId(id) + return append(diags, resourcePolicyAttachmentRead(ctx, d, meta)...) } @@ -148,3 +152,22 @@ func GetPolicyAttachment(ctx context.Context, conn *iot.IoT, target, policyName return policy, err } + +const policyAttachmentResourceIDSeparator = "|" + +func policyAttachmentCreateResourceID(policyName, target string) string { + parts := []string{policyName, target} + id := strings.Join(parts, policyAttachmentResourceIDSeparator) + + return id +} + +func policyAttachmentParseResourceID(id string) (string, string, error) { + parts := strings.Split(id, policyAttachmentResourceIDSeparator) + + if len(parts) == 2 && parts[0] != "" && parts[1] != "" { + return parts[0], parts[1], nil + } + + return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected policy-name%[2]starget", id, policyAttachmentResourceIDSeparator) +} From fbb6f52e14b993e68095440baeee62536eadc099 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 10 Nov 2023 16:12:40 -0500 Subject: [PATCH 23/28] r/aws_iot_policy_attachment: Tidy up Delete. --- internal/service/iot/policy_attachment.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/internal/service/iot/policy_attachment.go b/internal/service/iot/policy_attachment.go index 5cc059ff985..f9a08018249 100644 --- a/internal/service/iot/policy_attachment.go +++ b/internal/service/iot/policy_attachment.go @@ -91,10 +91,13 @@ func resourcePolicyAttachmentDelete(ctx context.Context, d *schema.ResourceData, var diags diag.Diagnostics conn := meta.(*conns.AWSClient).IoTConn(ctx) - policyName := d.Get("policy").(string) - target := d.Get("target").(string) + policyName, target, err := policyAttachmentParseResourceID(d.Id()) + if err != nil { + return sdkdiag.AppendFromErr(diags, err) + } - _, err := conn.DetachPolicyWithContext(ctx, &iot.DetachPolicyInput{ + log.Printf("[DEBUG] Deleting IoT Policy Attachment: %s", d.Id()) + _, err = conn.DetachPolicyWithContext(ctx, &iot.DetachPolicyInput{ PolicyName: aws.String(policyName), Target: aws.String(target), }) @@ -102,12 +105,11 @@ func resourcePolicyAttachmentDelete(ctx context.Context, d *schema.ResourceData, // DetachPolicy doesn't return an error if the policy doesn't exist, // but it returns an error if the Target is not found. if tfawserr.ErrMessageContains(err, iot.ErrCodeInvalidRequestException, "Invalid Target") { - log.Printf("[WARN] IOT target (%s) not found, removing attachment to policy (%s) from state", target, policyName) return diags } if err != nil { - return sdkdiag.AppendErrorf(diags, "detaching policy %s from target %s: %s", policyName, target, err) + return sdkdiag.AppendErrorf(diags, "deleting IoT Policy Attachment (%s): %s", d.Id(), err) } return diags From 10999feb253612ca9fd83e4c8943b2e363363ac4 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 10 Nov 2023 16:17:17 -0500 Subject: [PATCH 24/28] Fix providerlint 'AWSAT005'. --- internal/service/iot/policy_test.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/service/iot/policy_test.go b/internal/service/iot/policy_test.go index 7ca34d1eecd..5a257c15204 100644 --- a/internal/service/iot/policy_test.go +++ b/internal/service/iot/policy_test.go @@ -125,7 +125,7 @@ func TestAccIoTPolicy_prune(t *testing.T) { CheckDestroy: testAccCheckPolicyDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), + Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), // lintignore:AWSAT005 Check: resource.ComposeTestCheckFunc( testAccCheckPolicyExists(ctx, resourceName, &v), resource.TestCheckResourceAttr(resourceName, "default_version_id", "1"), @@ -133,7 +133,7 @@ func TestAccIoTPolicy_prune(t *testing.T) { ), }, { - Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), + Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), // lintignore:AWSAT005 Check: resource.ComposeTestCheckFunc( testAccCheckPolicyExists(ctx, resourceName, &v), resource.TestCheckResourceAttr(resourceName, "default_version_id", "2"), @@ -141,7 +141,7 @@ func TestAccIoTPolicy_prune(t *testing.T) { ), }, { - Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), + Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), // lintignore:AWSAT005 Check: resource.ComposeTestCheckFunc( testAccCheckPolicyExists(ctx, resourceName, &v), resource.TestCheckResourceAttr(resourceName, "default_version_id", "3"), @@ -149,7 +149,7 @@ func TestAccIoTPolicy_prune(t *testing.T) { ), }, { - Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), + Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), // lintignore:AWSAT005 Check: resource.ComposeTestCheckFunc( testAccCheckPolicyExists(ctx, resourceName, &v), resource.TestCheckResourceAttr(resourceName, "default_version_id", "4"), @@ -157,7 +157,7 @@ func TestAccIoTPolicy_prune(t *testing.T) { ), }, { - Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), + Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), // lintignore:AWSAT005 Check: resource.ComposeTestCheckFunc( testAccCheckPolicyExists(ctx, resourceName, &v), resource.TestCheckResourceAttr(resourceName, "default_version_id", "5"), @@ -165,7 +165,7 @@ func TestAccIoTPolicy_prune(t *testing.T) { ), }, { - Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), + Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), // lintignore:AWSAT005 Check: resource.ComposeTestCheckFunc( testAccCheckPolicyExists(ctx, resourceName, &v), resource.TestCheckResourceAttr(resourceName, "default_version_id", "6"), @@ -173,7 +173,7 @@ func TestAccIoTPolicy_prune(t *testing.T) { ), }, { - Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), + Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), // lintignore:AWSAT005 Check: resource.ComposeTestCheckFunc( testAccCheckPolicyExists(ctx, resourceName, &v), resource.TestCheckResourceAttr(resourceName, "default_version_id", "7"), From c78f1da114a346e846e747c8f5d968a0aea0d2de Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 10 Nov 2023 16:28:34 -0500 Subject: [PATCH 25/28] r/aws_iot_policy_attachment: Tidy up Read. --- internal/service/iot/policy_attachment.go | 94 +++++++++++++---------- 1 file changed, 55 insertions(+), 39 deletions(-) diff --git a/internal/service/iot/policy_attachment.go b/internal/service/iot/policy_attachment.go index f9a08018249..24f1b8d3949 100644 --- a/internal/service/iot/policy_attachment.go +++ b/internal/service/iot/policy_attachment.go @@ -13,9 +13,12 @@ import ( "github.com/aws/aws-sdk-go/service/iot" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) // @SDKResource("aws_iot_policy_attachment") @@ -67,21 +70,21 @@ func resourcePolicyAttachmentRead(ctx context.Context, d *schema.ResourceData, m var diags diag.Diagnostics conn := meta.(*conns.AWSClient).IoTConn(ctx) - policyName := d.Get("policy").(string) - target := d.Get("target").(string) - - var policy *iot.Policy - - policy, err := GetPolicyAttachment(ctx, conn, target, policyName) - + policyName, target, err := policyAttachmentParseResourceID(d.Id()) if err != nil { - return sdkdiag.AppendErrorf(diags, "listing policy attachments for target %s: %s", target, err) + return sdkdiag.AppendFromErr(diags, err) } - if policy == nil { - log.Printf("[WARN] IOT Policy Attachment (%s) not found, removing from state", d.Id()) + _, err = FindAttachedPolicyByTwoPartKey(ctx, conn, policyName, target) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] IoT Policy Attachment (%s) not found, removing from state", d.Id()) d.SetId("") - return diags + return nil + } + + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading IoT Policy Attachment (%s): %s", d.Id(), err) } return diags @@ -115,44 +118,57 @@ func resourcePolicyAttachmentDelete(ctx context.Context, d *schema.ResourceData, return diags } -func ListPolicyAttachmentPages(ctx context.Context, conn *iot.IoT, input *iot.ListAttachedPoliciesInput, - fn func(out *iot.ListAttachedPoliciesOutput, lastPage bool) bool) error { - for { - page, err := conn.ListAttachedPoliciesWithContext(ctx, input) - if err != nil { - return err - } - lastPage := page.NextMarker == nil - - shouldContinue := fn(page, lastPage) - if !shouldContinue || lastPage { - break - } - input.Marker = page.NextMarker - } - return nil -} - -func GetPolicyAttachment(ctx context.Context, conn *iot.IoT, target, policyName string) (*iot.Policy, error) { - var policy *iot.Policy - +func FindAttachedPolicyByTwoPartKey(ctx context.Context, conn *iot.IoT, policyName, target string) (*iot.Policy, error) { input := &iot.ListAttachedPoliciesInput{ PageSize: aws.Int64(250), Recursive: aws.Bool(false), Target: aws.String(target), } - err := ListPolicyAttachmentPages(ctx, conn, input, func(out *iot.ListAttachedPoliciesOutput, lastPage bool) bool { - for _, att := range out.Policies { - if policyName == aws.StringValue(att.PolicyName) { - policy = att - return false + return findAttachedPolicy(ctx, conn, input, func(v *iot.Policy) bool { + return aws.StringValue(v.PolicyName) == policyName + }) +} + +func findAttachedPolicy(ctx context.Context, conn *iot.IoT, input *iot.ListAttachedPoliciesInput, filter tfslices.Predicate[*iot.Policy]) (*iot.Policy, error) { + output, err := findAttachedPolicies(ctx, conn, input, filter) + + if err != nil { + return nil, err + } + + return tfresource.AssertSinglePtrResult(output) +} + +func findAttachedPolicies(ctx context.Context, conn *iot.IoT, input *iot.ListAttachedPoliciesInput, filter tfslices.Predicate[*iot.Policy]) ([]*iot.Policy, error) { + var output []*iot.Policy + + err := conn.ListAttachedPoliciesPagesWithContext(ctx, input, func(page *iot.ListAttachedPoliciesOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, v := range page.Policies { + if v != nil && filter(v) { + output = append(output, v) } } - return true + + return !lastPage }) - return policy, err + if tfawserr.ErrCodeEquals(err, iot.ErrCodeResourceNotFoundException) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + return output, nil } const policyAttachmentResourceIDSeparator = "|" From d41c2da9205a3f078e1aaa1a4cf4dcdd307d5f11 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 10 Nov 2023 16:39:36 -0500 Subject: [PATCH 26/28] r/aws_iot_policy_attachment: Tidy up acceptance tests. --- .../service/iot/policy_attachment_test.go | 206 +++++------------- 1 file changed, 59 insertions(+), 147 deletions(-) diff --git a/internal/service/iot/policy_attachment_test.go b/internal/service/iot/policy_attachment_test.go index 8c8713074a5..92c1df7ea9e 100644 --- a/internal/service/iot/policy_attachment_test.go +++ b/internal/service/iot/policy_attachment_test.go @@ -8,21 +8,23 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/iot" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfiot "github.com/hashicorp/terraform-provider-aws/internal/service/iot" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccIoTPolicyAttachment_basic(t *testing.T) { ctx := acctest.Context(t) - policyName := sdkacctest.RandomWithPrefix("PolicyName-") - policyName2 := sdkacctest.RandomWithPrefix("PolicyName2-") + policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + policyName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resource1Name := "aws_iot_policy_attachment.test1" + resource2Name := "aws_iot_policy_attachment.test2" + resource3Name := "aws_iot_policy_attachment.test3" resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -31,34 +33,29 @@ func TestAccIoTPolicyAttachment_basic(t *testing.T) { CheckDestroy: testAccCheckPolicyAttchmentDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccPolicyAttachmentConfig_basic(policyName), + Config: testAccPolicyAttachmentConfig_basic(policyName1), Check: resource.ComposeTestCheckFunc( - testAccCheckPolicyAttachmentExists(ctx, "aws_iot_policy_attachment.att"), - testAccCheckPolicyAttachmentCertStatus(ctx, "aws_iot_certificate.cert", []string{policyName}), + testAccCheckPolicyAttachmentExists(ctx, resource1Name), ), }, { - Config: testAccPolicyAttachmentConfig_update1(policyName, policyName2), + Config: testAccPolicyAttachmentConfig_update1(policyName1, policyName2), Check: resource.ComposeTestCheckFunc( - testAccCheckPolicyAttachmentExists(ctx, "aws_iot_policy_attachment.att"), - testAccCheckPolicyAttachmentExists(ctx, "aws_iot_policy_attachment.att2"), - testAccCheckPolicyAttachmentCertStatus(ctx, "aws_iot_certificate.cert", []string{policyName, policyName2}), + testAccCheckPolicyAttachmentExists(ctx, resource1Name), + testAccCheckPolicyAttachmentExists(ctx, resource2Name), ), }, { Config: testAccPolicyAttachmentConfig_update2(policyName2), Check: resource.ComposeTestCheckFunc( - testAccCheckPolicyAttachmentExists(ctx, "aws_iot_policy_attachment.att2"), - testAccCheckPolicyAttachmentCertStatus(ctx, "aws_iot_certificate.cert", []string{policyName2}), + testAccCheckPolicyAttachmentExists(ctx, resource2Name), ), }, { Config: testAccPolicyAttachmentConfig_update3(policyName2), Check: resource.ComposeTestCheckFunc( - testAccCheckPolicyAttachmentExists(ctx, "aws_iot_policy_attachment.att2"), - testAccCheckPolicyAttachmentExists(ctx, "aws_iot_policy_attachment.att3"), - testAccCheckPolicyAttachmentCertStatus(ctx, "aws_iot_certificate.cert", []string{policyName2}), - testAccCheckPolicyAttachmentCertStatus(ctx, "aws_iot_certificate.cert2", []string{policyName2}), + testAccCheckPolicyAttachmentExists(ctx, resource2Name), + testAccCheckPolicyAttachmentExists(ctx, resource3Name), ), }, }, @@ -73,38 +70,19 @@ func testAccCheckPolicyAttchmentDestroy(ctx context.Context) resource.TestCheckF continue } - target := rs.Primary.Attributes["target"] - policyName := rs.Primary.Attributes["policy"] + _, err := tfiot.FindAttachedPolicyByTwoPartKey(ctx, conn, rs.Primary.Attributes["policy"], rs.Primary.Attributes["target"]) - input := &iot.ListAttachedPoliciesInput{ - PageSize: aws.Int64(250), - Recursive: aws.Bool(false), - Target: aws.String(target), - } - - var policy *iot.Policy - err := tfiot.ListPolicyAttachmentPages(ctx, conn, input, func(out *iot.ListAttachedPoliciesOutput, lastPage bool) bool { - for _, att := range out.Policies { - if policyName == aws.StringValue(att.PolicyName) { - policy = att - return false - } - } - return true - }) - - if tfawserr.ErrMessageContains(err, iot.ErrCodeResourceNotFoundException, "The certificate given in the principal does not exist.") { + if tfresource.NotFound(err) { continue - } else if err != nil { - return err } - if policy == nil { - continue + if err != nil { + return err } - return fmt.Errorf("IOT Policy Attachment (%s) still exists", rs.Primary.Attributes["id"]) + return fmt.Errorf("IoT Policy Attachment %s still exists", rs.Primary.ID) } + return nil } } @@ -116,86 +94,23 @@ func testAccCheckPolicyAttachmentExists(ctx context.Context, n string) resource. return fmt.Errorf("Not found: %s", n) } - if rs.Primary.ID == "" { - return fmt.Errorf("No policy name is set") - } - conn := acctest.Provider.Meta().(*conns.AWSClient).IoTConn(ctx) - target := rs.Primary.Attributes["target"] - policyName := rs.Primary.Attributes["policy"] - policy, err := tfiot.GetPolicyAttachment(ctx, conn, target, policyName) + _, err := tfiot.FindAttachedPolicyByTwoPartKey(ctx, conn, rs.Primary.Attributes["policy"], rs.Primary.Attributes["target"]) - if err != nil { - return fmt.Errorf("Error: Failed to get attached policies for target %s (%s): %s", target, n, err) - } - - if policy == nil { - return fmt.Errorf("Error: Policy %s is not attached to target (%s)", policyName, target) - } - - return nil + return err } } -func testAccCheckPolicyAttachmentCertStatus(ctx context.Context, n string, policies []string) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).IoTConn(ctx) - - rs, ok := s.RootModule().Resources[n] - - if !ok { - return fmt.Errorf("Not found: %s", n) - } - - if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") - } - - certARN := rs.Primary.Attributes["arn"] - - out, err := conn.ListAttachedPoliciesWithContext(ctx, &iot.ListAttachedPoliciesInput{ - Target: aws.String(certARN), - PageSize: aws.Int64(250), - }) - - if err != nil { - return fmt.Errorf("Error: Cannot list attached policies for target %s: %s", certARN, err) - } - - if len(out.Policies) != len(policies) { - return fmt.Errorf("Error: Invalid attached policies count for target %s, expected %d, got %d", - certARN, - len(policies), - len(out.Policies)) - } - - for _, p1 := range policies { - found := false - for _, p2 := range out.Policies { - if p1 == aws.StringValue(p2.PolicyName) { - found = true - break - } - } - if !found { - return fmt.Errorf("Error: Policy %s is not attached to target %s", p1, certARN) - } - } - - return nil - } -} - -func testAccPolicyAttachmentConfig_basic(policyName string) string { +func testAccPolicyAttachmentConfig_basic(rName string) string { return fmt.Sprintf(` -resource "aws_iot_certificate" "cert" { +resource "aws_iot_certificate" "test1" { csr = file("test-fixtures/iot-csr.pem") active = true } -resource "aws_iot_policy" "policy" { - name = "%s" +resource "aws_iot_policy" "test1" { + name = %[1]q policy = < Date: Fri, 10 Nov 2023 17:14:35 -0500 Subject: [PATCH 27/28] Fix providerlint 'AWSAT005'. --- internal/service/iot/policy_test.go | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/internal/service/iot/policy_test.go b/internal/service/iot/policy_test.go index 5a257c15204..73fd00adc7d 100644 --- a/internal/service/iot/policy_test.go +++ b/internal/service/iot/policy_test.go @@ -90,6 +90,7 @@ func TestAccIoTPolicy_update(t *testing.T) { CheckDestroy: testAccCheckPolicyDestroy(ctx), Steps: []resource.TestStep{ { + // lintignore:AWSAT005 Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), Check: resource.ComposeTestCheckFunc( testAccCheckPolicyExists(ctx, resourceName, &v), @@ -102,6 +103,7 @@ func TestAccIoTPolicy_update(t *testing.T) { ImportStateVerify: true, }, { + // lintignore:AWSAT005 Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), Check: resource.ComposeTestCheckFunc( testAccCheckPolicyExists(ctx, resourceName, &v), @@ -125,7 +127,8 @@ func TestAccIoTPolicy_prune(t *testing.T) { CheckDestroy: testAccCheckPolicyDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), // lintignore:AWSAT005 + // lintignore:AWSAT005 + Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), Check: resource.ComposeTestCheckFunc( testAccCheckPolicyExists(ctx, resourceName, &v), resource.TestCheckResourceAttr(resourceName, "default_version_id", "1"), @@ -133,7 +136,8 @@ func TestAccIoTPolicy_prune(t *testing.T) { ), }, { - Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), // lintignore:AWSAT005 + // lintignore:AWSAT005 + Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), Check: resource.ComposeTestCheckFunc( testAccCheckPolicyExists(ctx, resourceName, &v), resource.TestCheckResourceAttr(resourceName, "default_version_id", "2"), @@ -141,7 +145,8 @@ func TestAccIoTPolicy_prune(t *testing.T) { ), }, { - Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), // lintignore:AWSAT005 + // lintignore:AWSAT005 + Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), Check: resource.ComposeTestCheckFunc( testAccCheckPolicyExists(ctx, resourceName, &v), resource.TestCheckResourceAttr(resourceName, "default_version_id", "3"), @@ -149,7 +154,8 @@ func TestAccIoTPolicy_prune(t *testing.T) { ), }, { - Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), // lintignore:AWSAT005 + // lintignore:AWSAT005 + Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), Check: resource.ComposeTestCheckFunc( testAccCheckPolicyExists(ctx, resourceName, &v), resource.TestCheckResourceAttr(resourceName, "default_version_id", "4"), @@ -157,7 +163,8 @@ func TestAccIoTPolicy_prune(t *testing.T) { ), }, { - Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), // lintignore:AWSAT005 + // lintignore:AWSAT005 + Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), Check: resource.ComposeTestCheckFunc( testAccCheckPolicyExists(ctx, resourceName, &v), resource.TestCheckResourceAttr(resourceName, "default_version_id", "5"), @@ -165,7 +172,8 @@ func TestAccIoTPolicy_prune(t *testing.T) { ), }, { - Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), // lintignore:AWSAT005 + // lintignore:AWSAT005 + Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), Check: resource.ComposeTestCheckFunc( testAccCheckPolicyExists(ctx, resourceName, &v), resource.TestCheckResourceAttr(resourceName, "default_version_id", "6"), @@ -173,7 +181,8 @@ func TestAccIoTPolicy_prune(t *testing.T) { ), }, { - Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), // lintignore:AWSAT005 + // lintignore:AWSAT005 + Config: testAccPolicyConfig_resourceName(rName, fmt.Sprintf("arn:aws:iot:*:*:topic/%s", sdkacctest.RandomWithPrefix(acctest.ResourcePrefix))), Check: resource.ComposeTestCheckFunc( testAccCheckPolicyExists(ctx, resourceName, &v), resource.TestCheckResourceAttr(resourceName, "default_version_id", "7"), From cd4ab9a3782c30d8c85b23acd6d8f8a8c8d2ff88 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sat, 11 Nov 2023 05:52:19 -0500 Subject: [PATCH 28/28] Fix golangci-lint 'revive/superfluous-else'. --- internal/service/iot/policy.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/internal/service/iot/policy.go b/internal/service/iot/policy.go index bd8b8569cf4..a2d6a991189 100644 --- a/internal/service/iot/policy.go +++ b/internal/service/iot/policy.go @@ -160,11 +160,13 @@ func resourcePolicyUpdate(ctx context.Context, d *schema.ResourceData, meta inte continue } - if v, err := strconv.Atoi(aws.StringValue(v.VersionId)); err != nil { + v, err := strconv.Atoi(aws.StringValue(v.VersionId)) + + if err != nil { continue - } else { - versionIDs = append(versionIDs, v) } + + versionIDs = append(versionIDs, v) } if len(versionIDs) > 0 {