From 8204801d424a347dce9df80ad7c1436fdf780d5e Mon Sep 17 00:00:00 2001 From: Isaiah-Turner <42742035+Isaiah-Turner@users.noreply.github.com> Date: Wed, 10 Mar 2021 15:15:43 -0500 Subject: [PATCH 001/398] Added AppConfig to keyvalue generators --- aws/config.go | 3 +++ aws/internal/keyvaluetags/generators/listtags/main.go | 1 + aws/internal/keyvaluetags/generators/servicetags/main.go | 1 + aws/internal/keyvaluetags/generators/updatetags/main.go | 1 + aws/internal/keyvaluetags/service_generation_customizations.go | 3 +++ 5 files changed, 9 insertions(+) diff --git a/aws/config.go b/aws/config.go index 0493b8d71a0..d0db99f78a1 100644 --- a/aws/config.go +++ b/aws/config.go @@ -14,6 +14,7 @@ import ( "github.com/aws/aws-sdk-go/service/amplify" "github.com/aws/aws-sdk-go/service/apigateway" "github.com/aws/aws-sdk-go/service/apigatewayv2" + "github.com/aws/aws-sdk-go/service/appconfig" "github.com/aws/aws-sdk-go/service/applicationautoscaling" "github.com/aws/aws-sdk-go/service/applicationinsights" "github.com/aws/aws-sdk-go/service/appmesh" @@ -214,6 +215,7 @@ type AWSClient struct { apigatewayconn *apigateway.APIGateway apigatewayv2conn *apigatewayv2.ApiGatewayV2 appautoscalingconn *applicationautoscaling.ApplicationAutoScaling + appconfigconn *appconfig.AppConfig applicationinsightsconn *applicationinsights.ApplicationInsights appmeshconn *appmesh.AppMesh appstreamconn *appstream.AppStream @@ -457,6 +459,7 @@ func (c *Config) Client() (interface{}, error) { apigatewayconn: apigateway.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["apigateway"])})), apigatewayv2conn: apigatewayv2.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["apigateway"])})), appautoscalingconn: applicationautoscaling.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["applicationautoscaling"])})), + appconfigconn: appconfig.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["appconfig"])})), applicationinsightsconn: applicationinsights.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["applicationinsights"])})), appmeshconn: appmesh.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["appmesh"])})), appstreamconn: appstream.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["appstream"])})), diff --git a/aws/internal/keyvaluetags/generators/listtags/main.go b/aws/internal/keyvaluetags/generators/listtags/main.go index 4ffe44d98ec..5c50c7e6d1b 100644 --- a/aws/internal/keyvaluetags/generators/listtags/main.go +++ b/aws/internal/keyvaluetags/generators/listtags/main.go @@ -22,6 +22,7 @@ var serviceNames = []string{ "acmpca", "amplify", "apigatewayv2", + "appconfig", "appmesh", "appstream", "appsync", diff --git a/aws/internal/keyvaluetags/generators/servicetags/main.go b/aws/internal/keyvaluetags/generators/servicetags/main.go index 05762715592..24354c0660c 100644 --- a/aws/internal/keyvaluetags/generators/servicetags/main.go +++ b/aws/internal/keyvaluetags/generators/servicetags/main.go @@ -20,6 +20,7 @@ const filename = `service_tags_gen.go` var sliceServiceNames = []string{ "acm", "acmpca", + "appconfig", "appmesh", "athena", "autoscaling", diff --git a/aws/internal/keyvaluetags/generators/updatetags/main.go b/aws/internal/keyvaluetags/generators/updatetags/main.go index 5d9b9cf73eb..f2ce5f14b6b 100644 --- a/aws/internal/keyvaluetags/generators/updatetags/main.go +++ b/aws/internal/keyvaluetags/generators/updatetags/main.go @@ -23,6 +23,7 @@ var serviceNames = []string{ "amplify", "apigateway", "apigatewayv2", + "appconfig", "appmesh", "appstream", "appsync", diff --git a/aws/internal/keyvaluetags/service_generation_customizations.go b/aws/internal/keyvaluetags/service_generation_customizations.go index ba3797f086a..903f94c506f 100644 --- a/aws/internal/keyvaluetags/service_generation_customizations.go +++ b/aws/internal/keyvaluetags/service_generation_customizations.go @@ -12,6 +12,7 @@ import ( "github.com/aws/aws-sdk-go/service/amplify" "github.com/aws/aws-sdk-go/service/apigateway" "github.com/aws/aws-sdk-go/service/apigatewayv2" + "github.com/aws/aws-sdk-go/service/appconfig" "github.com/aws/aws-sdk-go/service/appmesh" "github.com/aws/aws-sdk-go/service/appstream" "github.com/aws/aws-sdk-go/service/appsync" @@ -141,6 +142,8 @@ func ServiceClientType(serviceName string) string { funcType = reflect.TypeOf(apigateway.New) case "apigatewayv2": funcType = reflect.TypeOf(apigatewayv2.New) + case "appconfig": + funcType = reflect.TypeOf(appconfig.New) case "appmesh": funcType = reflect.TypeOf(appmesh.New) case "appstream": From 88142803f204c3377ceee55ba968aed0afacb2a1 Mon Sep 17 00:00:00 2001 From: Isaiah-Turner <42742035+Isaiah-Turner@users.noreply.github.com> Date: Wed, 10 Mar 2021 15:22:29 -0500 Subject: [PATCH 002/398] Generated all tags --- .../generators/servicetags/main.go | 2 +- aws/internal/keyvaluetags/list_tags_gen.go | 18 +++++++++ aws/internal/keyvaluetags/service_tags_gen.go | 10 +++++ aws/internal/keyvaluetags/update_tags_gen.go | 37 +++++++++++++++++++ 4 files changed, 66 insertions(+), 1 deletion(-) diff --git a/aws/internal/keyvaluetags/generators/servicetags/main.go b/aws/internal/keyvaluetags/generators/servicetags/main.go index 24354c0660c..918b01622f2 100644 --- a/aws/internal/keyvaluetags/generators/servicetags/main.go +++ b/aws/internal/keyvaluetags/generators/servicetags/main.go @@ -20,7 +20,6 @@ const filename = `service_tags_gen.go` var sliceServiceNames = []string{ "acm", "acmpca", - "appconfig", "appmesh", "athena", "autoscaling", @@ -110,6 +109,7 @@ var mapServiceNames = []string{ "amplify", "apigateway", "apigatewayv2", + "appconfig", "appstream", "appsync", "backup", diff --git a/aws/internal/keyvaluetags/list_tags_gen.go b/aws/internal/keyvaluetags/list_tags_gen.go index 5cbec75b054..0fb0e72e04b 100644 --- a/aws/internal/keyvaluetags/list_tags_gen.go +++ b/aws/internal/keyvaluetags/list_tags_gen.go @@ -9,6 +9,7 @@ import ( "github.com/aws/aws-sdk-go/service/acmpca" "github.com/aws/aws-sdk-go/service/amplify" "github.com/aws/aws-sdk-go/service/apigatewayv2" + "github.com/aws/aws-sdk-go/service/appconfig" "github.com/aws/aws-sdk-go/service/appmesh" "github.com/aws/aws-sdk-go/service/appstream" "github.com/aws/aws-sdk-go/service/appsync" @@ -196,6 +197,23 @@ func Apigatewayv2ListTags(conn *apigatewayv2.ApiGatewayV2, identifier string) (K return Apigatewayv2KeyValueTags(output.Tags), nil } +// AppconfigListTags lists appconfig service tags. +// The identifier is typically the Amazon Resource Name (ARN), although +// it may also be a different identifier depending on the service. +func AppconfigListTags(conn *appconfig.AppConfig, identifier string) (KeyValueTags, error) { + input := &appconfig.ListTagsForResourceInput{ + ResourceArn: aws.String(identifier), + } + + output, err := conn.ListTagsForResource(input) + + if err != nil { + return New(nil), err + } + + return AppconfigKeyValueTags(output.Tags), nil +} + // AppmeshListTags lists appmesh service tags. // The identifier is typically the Amazon Resource Name (ARN), although // it may also be a different identifier depending on the service. diff --git a/aws/internal/keyvaluetags/service_tags_gen.go b/aws/internal/keyvaluetags/service_tags_gen.go index 8e1d2f68f6d..c2bb82cf9b0 100644 --- a/aws/internal/keyvaluetags/service_tags_gen.go +++ b/aws/internal/keyvaluetags/service_tags_gen.go @@ -134,6 +134,16 @@ func Apigatewayv2KeyValueTags(tags map[string]*string) KeyValueTags { return New(tags) } +// AppconfigTags returns appconfig service tags. +func (tags KeyValueTags) AppconfigTags() map[string]*string { + return aws.StringMap(tags.Map()) +} + +// AppconfigKeyValueTags creates KeyValueTags from appconfig service tags. +func AppconfigKeyValueTags(tags map[string]*string) KeyValueTags { + return New(tags) +} + // AppstreamTags returns appstream service tags. func (tags KeyValueTags) AppstreamTags() map[string]*string { return aws.StringMap(tags.Map()) diff --git a/aws/internal/keyvaluetags/update_tags_gen.go b/aws/internal/keyvaluetags/update_tags_gen.go index 62ee4ea9fae..48395f0c7a4 100644 --- a/aws/internal/keyvaluetags/update_tags_gen.go +++ b/aws/internal/keyvaluetags/update_tags_gen.go @@ -12,6 +12,7 @@ import ( "github.com/aws/aws-sdk-go/service/amplify" "github.com/aws/aws-sdk-go/service/apigateway" "github.com/aws/aws-sdk-go/service/apigatewayv2" + "github.com/aws/aws-sdk-go/service/appconfig" "github.com/aws/aws-sdk-go/service/appmesh" "github.com/aws/aws-sdk-go/service/appstream" "github.com/aws/aws-sdk-go/service/appsync" @@ -336,6 +337,42 @@ func Apigatewayv2UpdateTags(conn *apigatewayv2.ApiGatewayV2, identifier string, return nil } +// AppconfigUpdateTags updates appconfig service tags. +// The identifier is typically the Amazon Resource Name (ARN), although +// it may also be a different identifier depending on the service. +func AppconfigUpdateTags(conn *appconfig.AppConfig, identifier string, oldTagsMap interface{}, newTagsMap interface{}) error { + oldTags := New(oldTagsMap) + newTags := New(newTagsMap) + + if removedTags := oldTags.Removed(newTags); len(removedTags) > 0 { + input := &appconfig.UntagResourceInput{ + ResourceArn: aws.String(identifier), + TagKeys: aws.StringSlice(removedTags.IgnoreAws().Keys()), + } + + _, err := conn.UntagResource(input) + + if err != nil { + return fmt.Errorf("error untagging resource (%s): %w", identifier, err) + } + } + + if updatedTags := oldTags.Updated(newTags); len(updatedTags) > 0 { + input := &appconfig.TagResourceInput{ + ResourceArn: aws.String(identifier), + Tags: updatedTags.IgnoreAws().AppconfigTags(), + } + + _, err := conn.TagResource(input) + + if err != nil { + return fmt.Errorf("error tagging resource (%s): %w", identifier, err) + } + } + + return nil +} + // AppmeshUpdateTags updates appmesh service tags. // The identifier is typically the Amazon Resource Name (ARN), although // it may also be a different identifier depending on the service. From d7bd4eb4e261eb1ae0a592c34f16a663aae0c1e5 Mon Sep 17 00:00:00 2001 From: Isaiah-Turner <42742035+Isaiah-Turner@users.noreply.github.com> Date: Wed, 10 Mar 2021 15:16:06 -0500 Subject: [PATCH 003/398] Added AppConfig Application Resource --- aws/provider.go | 2 + aws/resource_aws_appconfig_application.go | 177 ++++++++++++++ ...resource_aws_appconfig_application_test.go | 223 ++++++++++++++++++ 3 files changed, 402 insertions(+) create mode 100644 aws/resource_aws_appconfig_application.go create mode 100644 aws/resource_aws_appconfig_application_test.go diff --git a/aws/provider.go b/aws/provider.go index 5de81c4636c..7d7c14c9309 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -456,6 +456,7 @@ func Provider() *schema.Provider { "aws_appautoscaling_target": resourceAwsAppautoscalingTarget(), "aws_appautoscaling_policy": resourceAwsAppautoscalingPolicy(), "aws_appautoscaling_scheduled_action": resourceAwsAppautoscalingScheduledAction(), + "aws_appconfig_application": resourceAwsAppconfigApplication(), "aws_appmesh_gateway_route": resourceAwsAppmeshGatewayRoute(), "aws_appmesh_mesh": resourceAwsAppmeshMesh(), "aws_appmesh_route": resourceAwsAppmeshRoute(), @@ -1180,6 +1181,7 @@ func init() { "acmpca", "amplify", "apigateway", + "appconfig", "applicationautoscaling", "applicationinsights", "appmesh", diff --git a/aws/resource_aws_appconfig_application.go b/aws/resource_aws_appconfig_application.go new file mode 100644 index 00000000000..743b609df61 --- /dev/null +++ b/aws/resource_aws_appconfig_application.go @@ -0,0 +1,177 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" + "github.com/aws/aws-sdk-go/service/appconfig" + "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" +) + +func resourceAwsAppconfigApplication() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsAppconfigApplicationCreate, + Read: resourceAwsAppconfigApplicationRead, + Update: resourceAwsAppconfigApplicationUpdate, + Delete: resourceAwsAppconfigApplicationDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 64), + ), + }, + "description": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.All( + validation.StringLenBetween(0, 1024), + ), + }, + "tags": tagsSchema(), + "id": { + Type: schema.TypeString, + Computed: true, + }, + "arn": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceAwsAppconfigApplicationCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).appconfigconn + applicationName := d.Get("name").(string) + applicationDescription := d.Get("description").(string) + + input := &appconfig.CreateApplicationInput{ + Name: aws.String(applicationName), + Description: aws.String(applicationDescription), + Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().AppconfigTags(), + } + + app, err := conn.CreateApplication(input) + if err != nil { + return fmt.Errorf("Error creating AppConfig application: %s", err) + } + + d.SetId(aws.StringValue(app.Id)) + + return resourceAwsAppconfigApplicationRead(d, meta) +} + +func resourceAwsAppconfigApplicationRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).appconfigconn + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + + input := &appconfig.GetApplicationInput{ + ApplicationId: aws.String(d.Id()), + } + + output, err := conn.GetApplication(input) + + if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + log.Printf("[WARN] Appconfig Application (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error getting AppConfig Application (%s): %s", d.Id(), err) + } + + if output == nil { + return fmt.Errorf("error getting AppConfig Application (%s): empty response", d.Id()) + } + + appARN := arn.ARN{ + AccountID: meta.(*AWSClient).accountid, + Partition: meta.(*AWSClient).partition, + Region: meta.(*AWSClient).region, + Resource: fmt.Sprintf("application/%s", aws.StringValue(output.Id)), + Service: "appconfig", + }.String() + + d.Set("arn", appARN) + d.Set("name", output.Name) + d.Set("description", output.Description) + + tags, err := keyvaluetags.AppconfigListTags(conn, appARN) + if err != nil { + return fmt.Errorf("error getting tags for AppConfig Application (%s): %s", d.Id(), err) + } + + if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting tags: %s", err) + } + + return nil +} + +func resourceAwsAppconfigApplicationUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).appconfigconn + + if d.HasChange("tags") { + o, n := d.GetChange("tags") + if err := keyvaluetags.AppconfigUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return fmt.Errorf("error updating AppConfig (%s) tags: %s", d.Id(), err) + } + } + + appDesc := d.Get("description").(string) + appName := d.Get("name").(string) + + updateInput := &appconfig.UpdateApplicationInput{ + ApplicationId: aws.String(d.Id()), + Description: aws.String(appDesc), + Name: aws.String(appName), + } + + if d.HasChange("description") { + _, n := d.GetChange("description") + updateInput.Description = aws.String(n.(string)) + } + + if d.HasChange("name") { + _, n := d.GetChange("name") + updateInput.Name = aws.String(n.(string)) + } + + _, err := conn.UpdateApplication(updateInput) + if err != nil { + return fmt.Errorf("error updating AppConfig Application(%s): %s", d.Id(), err) + } + + return resourceAwsAppconfigApplicationRead(d, meta) +} + +func resourceAwsAppconfigApplicationDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).appconfigconn + + input := &appconfig.DeleteApplicationInput{ + ApplicationId: aws.String(d.Id()), + } + + _, err := conn.DeleteApplication(input) + + if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + return nil + } + + if err != nil { + return fmt.Errorf("error deleting Appconfig Application (%s): %s", d.Id(), err) + } + + return nil +} diff --git a/aws/resource_aws_appconfig_application_test.go b/aws/resource_aws_appconfig_application_test.go new file mode 100644 index 00000000000..a405fdd57ee --- /dev/null +++ b/aws/resource_aws_appconfig_application_test.go @@ -0,0 +1,223 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/appconfig" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccAWSAppConfigApplication_basic(t *testing.T) { + var application appconfig.GetApplicationOutput + rName := acctest.RandomWithPrefix("tf-acc-test") + rDesc := acctest.RandomWithPrefix("desc") + resourceName := "aws_appconfig_application.test" + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigApplicationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigApplicationName(rName, rDesc), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigApplicationExists(resourceName, &application), + resource.TestCheckResourceAttr(resourceName, "name", rName), + testAccCheckAWSAppConfigApplicationARN(resourceName, &application), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "description", rDesc), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSAppConfigApplication_disappears(t *testing.T) { + var application appconfig.GetApplicationOutput + + rName := acctest.RandomWithPrefix("tf-acc-test") + rDesc := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_appconfig_application.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigApplicationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigApplicationName(rName, rDesc), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigApplicationExists(resourceName, &application), + testAccCheckAWSAppConfigApplicationDisappears(&application), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAWSAppConfigApplication_Tags(t *testing.T) { + var application appconfig.GetApplicationOutput + + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_appconfig_application.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigApplicationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigApplicationTags1(rName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigApplicationExists(resourceName, &application), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSAppConfigApplicationTags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigApplicationExists(resourceName, &application), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccAWSAppConfigApplicationTags1(rName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigApplicationExists(resourceName, &application), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + }, + }) +} + +func testAccCheckAppConfigApplicationDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).appconfigconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_appconfig_application" { + continue + } + + input := &appconfig.GetApplicationInput{ + ApplicationId: aws.String(rs.Primary.ID), + } + + output, err := conn.GetApplication(input) + + if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + continue + } + + if err != nil { + return err + } + + if output != nil { + return fmt.Errorf("AppConfig Application (%s) still exists", rs.Primary.ID) + } + } + + return nil + +} + +func testAccCheckAWSAppConfigApplicationDisappears(application *appconfig.GetApplicationOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).appconfigconn + + input := &appconfig.DeleteApplicationInput{ + ApplicationId: aws.String(*application.Id), + } + + _, err := conn.DeleteApplication(input) + + return err + } +} + +func testAccCheckAWSAppConfigApplicationExists(resourceName string, application *appconfig.GetApplicationOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Resource not found: %s", resourceName) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("Resource (%s) ID not set", resourceName) + } + + conn := testAccProvider.Meta().(*AWSClient).appconfigconn + + input := &appconfig.GetApplicationInput{ + ApplicationId: aws.String(rs.Primary.ID), + } + + output, err := conn.GetApplication(input) + if err != nil { + return err + } + + *application = *output + + return nil + } +} + +func testAccCheckAWSAppConfigApplicationARN(resourceName string, application *appconfig.GetApplicationOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + return testAccCheckResourceAttrRegionalARN(resourceName, "arn", "appconfig", fmt.Sprintf("application/%s", aws.StringValue(application.Id)))(s) + } +} + +func testAccAWSAppConfigApplicationName(rName, rDesc string) string { + return fmt.Sprintf(` +resource "aws_appconfig_application" "test" { + name = %[1]q + description = %[2]q +} +`, rName, rDesc) +} + +func testAccAWSAppConfigApplicationTags1(rName, tagKey1, tagValue1 string) string { + return fmt.Sprintf(` +resource "aws_appconfig_application" "test" { + name = %[1]q + + tags = { + %[2]q = %[3]q + } +} +`, rName, tagKey1, tagValue1) +} + +func testAccAWSAppConfigApplicationTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return fmt.Sprintf(` +resource "aws_appconfig_application" "test" { + name = %[1]q + + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } +} +`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +} From b434d7aa84c9717f008a072e0edb27386dea8957 Mon Sep 17 00:00:00 2001 From: Isaiah-Turner <42742035+Isaiah-Turner@users.noreply.github.com> Date: Wed, 10 Mar 2021 15:16:18 -0500 Subject: [PATCH 004/398] Added web documentation --- .../r/appconfig_application.html.markdown | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 website/docs/r/appconfig_application.html.markdown diff --git a/website/docs/r/appconfig_application.html.markdown b/website/docs/r/appconfig_application.html.markdown new file mode 100644 index 00000000000..d2850e984c9 --- /dev/null +++ b/website/docs/r/appconfig_application.html.markdown @@ -0,0 +1,48 @@ +--- +subcategory: "AppConfig" +layout: "aws" +page_title: "AWS: aws_appconfig_application" +description: |- + Provides an AppConfig Application resource. +--- + +# Resource: aws_appconfig_application + +Provides an AppConfig Application resource. + +## Example Usage + +### AppConfig Application + +```hcl +resource "aws_appconfig_application" "test" { + name = "test-application-tf" + description = "Test AppConfig Application" + tags = { + Type = "AppConfig Application" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +- `name` - (Required) The name to use for the application. Must be between 1 and 64 characters in length. +- `description` - (Optional) The description of the application. Can be at most 1024 characters. +- `tags` - (Optional) A map of tags to assign to the resource. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +- `arn` - The Amazon Resource Name (ARN) of the AppConfig Application. +- `id` - The AppConfig Application ID + +## Import + +Applications can be imported using their ID, e.g. + +``` +$ terraform import aws_appconfig_application.bar 71rxuzt +``` From bdf3a4bb3b99d8ecf8fdc9cddb1a570bafe50220 Mon Sep 17 00:00:00 2001 From: Isaiah-Turner <42742035+Isaiah-Turner@users.noreply.github.com> Date: Wed, 10 Mar 2021 16:04:56 -0500 Subject: [PATCH 005/398] Added changelog entry --- .changelog/18032.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/18032.txt diff --git a/.changelog/18032.txt b/.changelog/18032.txt new file mode 100644 index 00000000000..bfbca0898be --- /dev/null +++ b/.changelog/18032.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_appconfig_application +``` \ No newline at end of file From bc89ad3e1bb65b6e4de4ef0c636b117712901629 Mon Sep 17 00:00:00 2001 From: Isaiah-Turner <42742035+Isaiah-Turner@users.noreply.github.com> Date: Wed, 10 Mar 2021 16:12:10 -0500 Subject: [PATCH 006/398] Fixed terraform formatting --- aws/resource_aws_appconfig_application_test.go | 4 ++-- website/docs/r/appconfig_application.html.markdown | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/aws/resource_aws_appconfig_application_test.go b/aws/resource_aws_appconfig_application_test.go index a405fdd57ee..e5702d0f112 100644 --- a/aws/resource_aws_appconfig_application_test.go +++ b/aws/resource_aws_appconfig_application_test.go @@ -191,8 +191,8 @@ func testAccCheckAWSAppConfigApplicationARN(resourceName string, application *ap func testAccAWSAppConfigApplicationName(rName, rDesc string) string { return fmt.Sprintf(` resource "aws_appconfig_application" "test" { - name = %[1]q - description = %[2]q + name = %[1]q + description = %[2]q } `, rName, rDesc) } diff --git a/website/docs/r/appconfig_application.html.markdown b/website/docs/r/appconfig_application.html.markdown index d2850e984c9..c90809be06a 100644 --- a/website/docs/r/appconfig_application.html.markdown +++ b/website/docs/r/appconfig_application.html.markdown @@ -16,7 +16,7 @@ Provides an AppConfig Application resource. ```hcl resource "aws_appconfig_application" "test" { - name = "test-application-tf" + name = "test-application-tf" description = "Test AppConfig Application" tags = { Type = "AppConfig Application" From be0d7b488808ffbdb8489f06d24ddf47ca258c2b Mon Sep 17 00:00:00 2001 From: Isaiah-Turner <42742035+Isaiah-Turner@users.noreply.github.com> Date: Wed, 10 Mar 2021 18:42:35 -0500 Subject: [PATCH 007/398] Fixed spacing in acceptance test --- aws/resource_aws_appconfig_application_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_appconfig_application_test.go b/aws/resource_aws_appconfig_application_test.go index e5702d0f112..1721363742a 100644 --- a/aws/resource_aws_appconfig_application_test.go +++ b/aws/resource_aws_appconfig_application_test.go @@ -191,8 +191,8 @@ func testAccCheckAWSAppConfigApplicationARN(resourceName string, application *ap func testAccAWSAppConfigApplicationName(rName, rDesc string) string { return fmt.Sprintf(` resource "aws_appconfig_application" "test" { - name = %[1]q - description = %[2]q + name = %[1]q + description = %[2]q } `, rName, rDesc) } From d46cc0fcc5c036a3f6432bbc0e94f9b6e5687369 Mon Sep 17 00:00:00 2001 From: Chris Trawick Date: Tue, 26 Jan 2021 19:41:35 -0500 Subject: [PATCH 008/398] Added AWS Config Organization Conformance Pack resource --- aws/configservice.go | 80 +++ aws/provider.go | 1 + aws/provider_test.go | 32 ++ ...ws_config_organization_conformance_pack.go | 278 +++++++++++ ...nfig_organization_conformance_pack_test.go | 469 ++++++++++++++++++ deleteme/main.tf | 0 6 files changed, 860 insertions(+) create mode 100644 aws/resource_aws_config_organization_conformance_pack.go create mode 100644 aws/resource_aws_config_organization_conformance_pack_test.go create mode 100644 deleteme/main.tf diff --git a/aws/configservice.go b/aws/configservice.go index 712f43cf189..7c835d3f653 100644 --- a/aws/configservice.go +++ b/aws/configservice.go @@ -301,3 +301,83 @@ func configWaitForOrganizationRuleStatusUpdateSuccessful(conn *configservice.Con return err } + +func configDescribeOrganizationConformancePack(conn *configservice.ConfigService, name string) (*configservice.OrganizationConformancePack, error) { + input := &configservice.DescribeOrganizationConformancePacksInput{ + OrganizationConformancePackNames: []*string{aws.String(name)}, + } + + for { + output, err := conn.DescribeOrganizationConformancePacks(input) + + if err != nil { + return nil, err + } + + for _, pack := range output.OrganizationConformancePacks { + if aws.StringValue(pack.OrganizationConformancePackName) == name { + return pack, nil + } + } + + if aws.StringValue(output.NextToken) == "" { + break + } + + input.NextToken = output.NextToken + } + + return nil, nil +} + +func configDescribeOrganizationConformancePackStatus(conn *configservice.ConfigService, name string) (*configservice.OrganizationConformancePackStatus, error) { + input := &configservice.DescribeOrganizationConformancePackStatusesInput{ + OrganizationConformancePackNames: []*string{aws.String(name)}, + } + + for { + output, err := conn.DescribeOrganizationConformancePackStatuses(input) + + if err != nil { + return nil, err + } + + for _, status := range output.OrganizationConformancePackStatuses { + if aws.StringValue(status.OrganizationConformancePackName) == name { + return status, nil + } + } + + if aws.StringValue(output.NextToken) == "" { + break + } + + input.NextToken = output.NextToken + } + + return nil, nil +} + +func configDescribeOrganizationConformancePackDetailedStatus(conn *configservice.ConfigService, name string) ([]*configservice.OrganizationConformancePackDetailedStatus, error) { + input := &configservice.GetOrganizationConformancePackDetailedStatusInput{ + OrganizationConformancePackName: aws.String(name), + } + var ret []*configservice.OrganizationConformancePackDetailedStatus + for { + output, err := conn.GetOrganizationConformancePackDetailedStatus(input) + + if err != nil { + return nil, err + } + + ret = append(ret, output.OrganizationConformancePackDetailedStatuses...) + + if aws.StringValue(output.NextToken) == "" { + break + } + + input.NextToken = output.NextToken + } + + return ret, nil +} diff --git a/aws/provider.go b/aws/provider.go index 64ee387878e..d92023439d5 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -541,6 +541,7 @@ func Provider() *schema.Provider { "aws_config_configuration_recorder_status": resourceAwsConfigConfigurationRecorderStatus(), "aws_config_conformance_pack": resourceAwsConfigConformancePack(), "aws_config_delivery_channel": resourceAwsConfigDeliveryChannel(), + "aws_config_organization_conformance_pack": resourceAwsConfigOrganizationConformancePack(), "aws_config_organization_custom_rule": resourceAwsConfigOrganizationCustomRule(), "aws_config_organization_managed_rule": resourceAwsConfigOrganizationManagedRule(), "aws_config_remediation_configuration": resourceAwsConfigRemediationConfiguration(), diff --git a/aws/provider_test.go b/aws/provider_test.go index 5a4e7a8cfa5..5acdb1bb13c 100644 --- a/aws/provider_test.go +++ b/aws/provider_test.go @@ -792,6 +792,38 @@ func testAccOrganizationsEnabledPreCheck(t *testing.T) { } } +func testAccOrganizationsMasterPreCheck(t *testing.T) { + conn := testAccProvider.Meta().(*AWSClient).organizationsconn + input := &organizations.DescribeOrganizationInput{} + out, err := conn.DescribeOrganization(input) + if isAWSErr(err, organizations.ErrCodeAWSOrganizationsNotInUseException, "") { + t.Skip("this AWS account must be an existing member of an AWS Organization") + } + if err != nil { + t.Fatalf("error describing AWS Organization: %s", err) + } + masterAccountId := *out.Organization.MasterAccountId + thisAccountId := testAccProvider.Meta().(*AWSClient).accountid + if masterAccountId != thisAccountId { + t.Skipf("this AWS account must be master of its AWS Organization ( %q != %q )", masterAccountId, thisAccountId) + } +} + +func testAccOrganizationsMinAccountsPreCheck(t *testing.T, minAccounts int) { + conn := testAccProvider.Meta().(*AWSClient).organizationsconn + input := &organizations.ListAccountsInput{} + out, err := conn.ListAccounts(input) + if isAWSErr(err, organizations.ErrCodeAWSOrganizationsNotInUseException, "") { + t.Skip("this AWS account must be an existing member of an AWS Organization") + } + if err != nil { + t.Fatalf("error listing accounts in AWS Organization: %s", err) + } + if len(out.Accounts) < minAccounts { + t.Skipf("this AWS account must have at least %d accounts in its organization", minAccounts) + } +} + func testAccPreCheckIamServiceLinkedRole(t *testing.T, pathPrefix string) { conn := testAccProvider.Meta().(*AWSClient).iamconn diff --git a/aws/resource_aws_config_organization_conformance_pack.go b/aws/resource_aws_config_organization_conformance_pack.go new file mode 100644 index 00000000000..7474d3693bb --- /dev/null +++ b/aws/resource_aws_config_organization_conformance_pack.go @@ -0,0 +1,278 @@ +package aws + +import ( + "fmt" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/configservice" + "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" + "log" + "regexp" + "time" +) + +func resourceAwsConfigOrganizationConformancePack() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsConfigOrganizationConformancePackPut, + Read: resourceAwsConfigOrganizationConformancePackRead, + Update: resourceAwsConfigOrganizationConformancePackPut, + Delete: resourceAwsConfigOrganizationConformancePackDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 51200), + validation.StringMatch(regexp.MustCompile(`^[a-zA-Z][-a-zA-Z0-9]*$`), "must be a valid conformance pack name"), + ), + }, + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "template_s3_uri": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 256), + }, + "template_body": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 51200), + validateStringIsJsonOrYaml), + StateFunc: func(v interface{}) string { + template, _ := normalizeJsonOrYamlString(v) + return template + }, + }, + "delivery_s3_bucket": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 63), + validation.StringMatch(regexp.MustCompile("awsconfigconforms.+"), "must start with 'awsconfigconforms'"), + ), + }, + "delivery_s3_key_prefix": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 1024), + }, + "input_parameters": { + Type: schema.TypeMap, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + }, + "excluded_accounts": { + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringMatch(regexp.MustCompile(`^[0-9]{12}$`), "must be a valid AWS account ID"), + }, + Optional: true, + }, + }, + } +} + +func resourceAwsConfigOrganizationConformancePackPut(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).configconn + + name := d.Get("name").(string) + input := configservice.PutOrganizationConformancePackInput{ + OrganizationConformancePackName: aws.String(name), + } + + if v, ok := d.GetOk("delivery_s3_bucket"); ok { + input.DeliveryS3Bucket = aws.String(v.(string)) + } + if v, ok := d.GetOk("delivery_s3_key_prefix"); ok { + input.DeliveryS3KeyPrefix = aws.String(v.(string)) + } + if v, ok := d.GetOk("excluded_accounts"); ok { + input.ExcludedAccounts = expandConfigConformancePackExcludedAccounts(v.([]interface{})) + } + if v, ok := d.GetOk("input_parameters"); ok { + input.ConformancePackInputParameters = expandConfigConformancePackParameters(v.(map[string]interface{})) + } + if v, ok := d.GetOk("template_body"); ok { + input.TemplateBody = aws.String(v.(string)) + } + if v, ok := d.GetOk("template_s3_uri"); ok { + input.TemplateS3Uri = aws.String(v.(string)) + } + + _, err := conn.PutOrganizationConformancePack(&input) + if err != nil { + return fmt.Errorf("failed to put AWSConfig organization conformance pack %q: %s", name, err) + } + + d.SetId(name) + conf := resource.StateChangeConf{ + Pending: []string{ + configservice.OrganizationResourceDetailedStatusCreateInProgress, + configservice.OrganizationResourceDetailedStatusUpdateInProgress, + }, + Target: []string{ + configservice.OrganizationResourceDetailedStatusCreateSuccessful, + configservice.OrganizationResourceDetailedStatusUpdateSuccessful, + }, + Timeout: 30 * time.Minute, + Refresh: refreshOrganizationConformancePackStatus(d, conn), + } + if _, err := conf.WaitForState(); err != nil { + return err + } + return resourceAwsConfigOrganizationConformancePackRead(d, meta) +} + +func expandConfigConformancePackExcludedAccounts(i []interface{}) (ret []*string) { + for _, v := range i { + ret = append(ret, aws.String(v.(string))) + } + return +} + +func expandConfigConformancePackParameters(m map[string]interface{}) (params []*configservice.ConformancePackInputParameter) { + for k, v := range m { + params = append(params, &configservice.ConformancePackInputParameter{ + ParameterName: aws.String(k), + ParameterValue: aws.String(v.(string)), + }) + } + return +} + +func refreshOrganizationConformancePackStatus(d *schema.ResourceData, conn *configservice.ConfigService) func() (interface{}, string, error) { + return func() (interface{}, string, error) { + out, err := conn.DescribeOrganizationConformancePackStatuses(&configservice.DescribeOrganizationConformancePackStatusesInput{ + OrganizationConformancePackNames: []*string{aws.String(d.Id())}, + }) + if err != nil { + if awsErr, ok := err.(awserr.Error); ok && isAWSErr(awsErr, configservice.ErrCodeNoSuchOrganizationConformancePackException, "") { + return 42, "", nil + } + return 42, "", fmt.Errorf("failed to describe organization conformance pack %q: %s", d.Id(), err) + } + if len(out.OrganizationConformancePackStatuses) < 1 { + return 42, "", nil + } + status := out.OrganizationConformancePackStatuses[0] + return out, *status.Status, nil + } +} + +func resourceAwsConfigOrganizationConformancePackRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).configconn + + out, err := conn.DescribeOrganizationConformancePacks(&configservice.DescribeOrganizationConformancePacksInput{ + OrganizationConformancePackNames: []*string{aws.String(d.Id())}, + }) + if err != nil { + if awsErr, ok := err.(awserr.Error); ok && isAWSErr(err, configservice.ErrCodeNoSuchOrganizationConformancePackException, "") { + log.Printf("[WARN] Organization Conformance Pack %q is gone (%s)", d.Id(), awsErr.Code()) + d.SetId("") + return nil + } + return err + } + + numberOfPacks := len(out.OrganizationConformancePacks) + if numberOfPacks < 1 { + log.Printf("[WARN] Organization Conformance Pack %q is gone (no packs found)", d.Id()) + d.SetId("") + return nil + } + + if numberOfPacks > 1 { + return fmt.Errorf("expected exactly 1 organization conformance pack, received %d: %#v", + numberOfPacks, out.OrganizationConformancePacks) + } + + log.Printf("[DEBUG] AWS Config organization conformance packs received: %s", out) + + pack := out.OrganizationConformancePacks[0] + if err = d.Set("arn", pack.OrganizationConformancePackArn); err != nil { + return err + } + if err = d.Set("name", pack.OrganizationConformancePackName); err != nil { + return err + } + if err = d.Set("delivery_s3_bucket", pack.DeliveryS3Bucket); err != nil { + return err + } + if err = d.Set("delivery_s3_key_prefix", pack.DeliveryS3KeyPrefix); err != nil { + return err + } + if err = d.Set("excluded_accounts", pack.ExcludedAccounts); err != nil { + return err + } + + if pack.ConformancePackInputParameters != nil { + if err = d.Set("input_parameters", flattenConformancePackInputParameters(pack.ConformancePackInputParameters)); err != nil { + return err + } + } + + return nil +} + +func flattenConformancePackInputParameters(parameters []*configservice.ConformancePackInputParameter) (m map[string]string) { + m = make(map[string]string) + for _, p := range parameters { + m[*p.ParameterName] = *p.ParameterValue + } + return +} + +func resourceAwsConfigOrganizationConformancePackDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).configconn + + name := d.Get("name").(string) + + log.Printf("[DEBUG] Deleting AWS Config organization conformance pack %q", name) + input := &configservice.DeleteOrganizationConformancePackInput{ + OrganizationConformancePackName: aws.String(name), + } + err := resource.Retry(30*time.Minute, func() *resource.RetryError { + _, err := conn.DeleteOrganizationConformancePack(input) + if err != nil { + if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "ResourceInUseException" { + return resource.RetryableError(err) + } + return resource.NonRetryableError(err) + } + return nil + }) + if isResourceTimeoutError(err) { + _, err = conn.DeleteOrganizationConformancePack(input) + } + if err != nil { + return fmt.Errorf("deleting organization conformance pack failed: %s", err) + } + + conf := resource.StateChangeConf{ + Pending: []string{ + configservice.OrganizationResourceDetailedStatusDeleteInProgress, + }, + Target: []string{""}, + Timeout: 30 * time.Minute, + Refresh: refreshOrganizationConformancePackStatus(d, conn), + } + _, err = conf.WaitForState() + if err != nil { + return err + } + + log.Printf("[DEBUG] AWS organization conformance pack %q deleted", name) + + return nil +} diff --git a/aws/resource_aws_config_organization_conformance_pack_test.go b/aws/resource_aws_config_organization_conformance_pack_test.go new file mode 100644 index 00000000000..e8aaf17f5cd --- /dev/null +++ b/aws/resource_aws_config_organization_conformance_pack_test.go @@ -0,0 +1,469 @@ +package aws + +import ( + "fmt" + "github.com/aws/aws-sdk-go/service/configservice" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "regexp" + "testing" +) + +func TestAccConfigOrganizationConformancePack_basic(t *testing.T) { + var pack configservice.OrganizationConformancePack + rName := acctest.RandomWithPrefix("tf-acc-test") + rId := "IAM_PASSWORD_POLICY" + resourceName := "aws_config_organization_conformance_pack.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccOrganizationsMasterPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, + Steps: []resource.TestStep{ + { + Config: testAccConfigOrganizationConformancePackConfigRuleIdentifier(rName, rId), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigOrganizationConformancePackExists(resourceName, &pack), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "config", regexp.MustCompile(fmt.Sprintf("organization-conformance-pack/%s-.+", rName))), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "delivery_s3_bucket", ""), + resource.TestCheckResourceAttr(resourceName, "delivery_s3_key_prefix", ""), + resource.TestCheckNoResourceAttr(resourceName, "input_parameters"), + resource.TestCheckNoResourceAttr(resourceName, "excluded_accounts"), + testAccCheckConfigOrganizationConformancePackSuccessful(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"template_body"}, + }, + }, + }) +} + +func TestAccConfigOrganizationConformancePack_disappears(t *testing.T) { + var pack configservice.OrganizationConformancePack + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_config_organization_conformance_pack.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccOrganizationsMasterPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, + Steps: []resource.TestStep{ + { + Config: testAccConfigOrganizationConformancePackConfigRuleIdentifier(rName, "IAM_PASSWORD_POLICY"), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigOrganizationConformancePackExists(resourceName, &pack), + testAccCheckResourceDisappears(testAccProvider, resourceAwsConfigOrganizationConformancePack(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccConfigOrganizationConformancePack_inputParameters(t *testing.T) { + var pack configservice.OrganizationConformancePack + rName := acctest.RandomWithPrefix("tf-acc-test") + rId := "IAM_PASSWORD_POLICY" + pKey := "ParamKey" + pValue := "ParamValue" + resourceName := "aws_config_organization_conformance_pack.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccOrganizationsMasterPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, + Steps: []resource.TestStep{ + { + Config: testAccConfigOrganizationConformancePackConfigRuleIdentifierParameter(rName, rId, pKey, pValue), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigOrganizationConformancePackExists(resourceName, &pack), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "config", regexp.MustCompile(fmt.Sprintf("organization-conformance-pack/%s-.+", rName))), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "delivery_s3_bucket", ""), + resource.TestCheckResourceAttr(resourceName, "delivery_s3_key_prefix", ""), + resource.TestCheckResourceAttr(resourceName, "input_parameters."+pKey, pValue), + resource.TestCheckNoResourceAttr(resourceName, "excluded_accounts"), + testAccCheckConfigOrganizationConformancePackSuccessful(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"template_body"}, + }, + }, + }) +} + +func TestAccConfigOrganizationConformancePack_s3Delivery(t *testing.T) { + var pack configservice.OrganizationConformancePack + rName := acctest.RandomWithPrefix("tf-acc-test") + bName := "awsconfigconforms" + rName + rId := "IAM_PASSWORD_POLICY" + resourceName := "aws_config_organization_conformance_pack.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccOrganizationsMasterPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, + Steps: []resource.TestStep{ + { + Config: testAccConfigOrganizationConformancePackConfigRuleIdentifierS3Delivery(rName, rId, bName), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigOrganizationConformancePackExists(resourceName, &pack), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "config", regexp.MustCompile(fmt.Sprintf("organization-conformance-pack/%s-.+", rName))), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "delivery_s3_bucket", bName), + resource.TestCheckResourceAttr(resourceName, "delivery_s3_key_prefix", rId), + resource.TestCheckNoResourceAttr(resourceName, "input_parameters"), + resource.TestCheckNoResourceAttr(resourceName, "excluded_accounts"), + testAccCheckConfigOrganizationConformancePackSuccessful(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"template_body"}, + }, + }, + }) +} + +func TestAccConfigOrganizationConformancePack_s3Template(t *testing.T) { + var pack configservice.OrganizationConformancePack + rName := acctest.RandomWithPrefix("tf-acc-test") + bName := rName + kName := rName + ".yaml" + rId := "IAM_PASSWORD_POLICY" + resourceName := "aws_config_organization_conformance_pack.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccOrganizationsMasterPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, + Steps: []resource.TestStep{ + { + Config: testAccConfigOrganizationConformancePackConfigRuleIdentifierS3Template(rName, rId, bName, kName), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigOrganizationConformancePackExists(resourceName, &pack), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "config", regexp.MustCompile(fmt.Sprintf("organization-conformance-pack/%s-.+", rName))), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "delivery_s3_bucket", ""), + resource.TestCheckResourceAttr(resourceName, "delivery_s3_key_prefix", ""), + resource.TestCheckNoResourceAttr(resourceName, "input_parameters"), + resource.TestCheckNoResourceAttr(resourceName, "excluded_accounts"), + testAccCheckConfigOrganizationConformancePackSuccessful(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"template_s3_uri"}, + }, + }, + }) +} + +func TestAccConfigOrganizationConformancePack_excludedAccounts(t *testing.T) { + var pack configservice.OrganizationConformancePack + rName := acctest.RandomWithPrefix("tf-acc-test") + rId := "IAM_PASSWORD_POLICY" + resourceName := "aws_config_organization_conformance_pack.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccOrganizationsMasterPreCheck(t) + testAccOrganizationsMinAccountsPreCheck(t, 2) + // TODO: All accounts in the organization must also have configuration recorders in the current region, + // which is a little complicated for a precheck. If you get an 'unexpected state' error with this + // test, try enabling configuration recorders across the org. + }, + Providers: testAccProviders, + CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, + Steps: []resource.TestStep{ + { + Config: testAccConfigOrganizationConformancePackConfigRuleIdentifierExcludedAccounts(rName, rId), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigOrganizationConformancePackExists(resourceName, &pack), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "config", regexp.MustCompile(fmt.Sprintf("organization-conformance-pack/%s-.+", rName))), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "delivery_s3_bucket", ""), + resource.TestCheckResourceAttr(resourceName, "delivery_s3_key_prefix", ""), + resource.TestCheckNoResourceAttr(resourceName, "input_parameters"), + resource.TestCheckResourceAttr(resourceName, "excluded_accounts.#", "1"), + testAccCheckConfigOrganizationConformancePackSuccessful(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"template_body"}, + }, + }, + }) +} + +func testAccCheckConfigOrganizationConformancePackDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).configconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_config_organization_conformance_pack" { + continue + } + + rule, err := configDescribeOrganizationConformancePack(conn, rs.Primary.ID) + + if isAWSErr(err, configservice.ErrCodeNoSuchOrganizationConformancePackException, "") { + continue + } + + if err != nil { + return fmt.Errorf("error describing Config Organization Managed Rule (%s): %s", rs.Primary.ID, err) + } + + if rule != nil { + return fmt.Errorf("Config Organization Managed Rule (%s) still exists", rs.Primary.ID) + } + } + + return nil +} + +func testAccCheckConfigOrganizationConformancePackExists(resourceName string, ocr *configservice.OrganizationConformancePack) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not Found: %s", resourceName) + } + + conn := testAccProvider.Meta().(*AWSClient).configconn + + pack, err := configDescribeOrganizationConformancePack(conn, rs.Primary.ID) + + if err != nil { + return fmt.Errorf("error describing organization conformance pack (%s): %s", rs.Primary.ID, err) + } + + if pack == nil { + return fmt.Errorf("organization conformance pack (%s) not found", rs.Primary.ID) + } + + *ocr = *pack + + return nil + } +} + +func testAccCheckConfigOrganizationConformancePackSuccessful(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not Found: %s", resourceName) + } + + conn := testAccProvider.Meta().(*AWSClient).configconn + + packStatus, err := configDescribeOrganizationConformancePackStatus(conn, rs.Primary.ID) + if err != nil { + return fmt.Errorf("error describing organization conformance pack status (%s): %s", rs.Primary.ID, err) + } + if packStatus == nil { + return fmt.Errorf("organization conformance pack status (%s) not found", rs.Primary.ID) + } + if *packStatus.Status != configservice.OrganizationResourceStatusCreateSuccessful { + return fmt.Errorf("organization conformance pack (%s) returned %s status (%s): %s", rs.Primary.ID, *packStatus.Status, *packStatus.ErrorCode, *packStatus.ErrorMessage) + } + + detailedStatus, err := configDescribeOrganizationConformancePackDetailedStatus(conn, rs.Primary.ID) + if err != nil { + return fmt.Errorf("error describing organization conformance pack detailed status (%s): %s", rs.Primary.ID, err) + } + if detailedStatus == nil { + return fmt.Errorf("organization conformance pack detailed status (%s) not found", rs.Primary.ID) + } + for _, s := range detailedStatus { + if *s.Status != configservice.OrganizationResourceDetailedStatusCreateSuccessful { + return fmt.Errorf("organization conformance pack (%s) on account %s returned %s status (%s): %s", rs.Primary.ID, *s.Status, *s.AccountId, *s.ErrorCode, *s.ErrorMessage) + } + } + + return nil + } +} + +/*func testAccConfigOrganizationConformancePackBase(rName string) string { + return fmt.Sprintf(` + +data "aws_partition" "current" { +} + +resource "aws_config_configuration_recorder" "test" { + depends_on = [aws_iam_role_policy_attachment.test] + + name = %[1]q + role_arn = aws_iam_role.test.arn +} + +resource "aws_iam_role" "test" { + name = %[1]q + + assume_role_policy = < Date: Tue, 26 Jan 2021 20:09:25 -0500 Subject: [PATCH 009/398] Added whitespace to strings --- ...nfig_organization_conformance_pack_test.go | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/aws/resource_aws_config_organization_conformance_pack_test.go b/aws/resource_aws_config_organization_conformance_pack_test.go index e8aaf17f5cd..4d446cfeb45 100644 --- a/aws/resource_aws_config_organization_conformance_pack_test.go +++ b/aws/resource_aws_config_organization_conformance_pack_test.go @@ -310,14 +310,12 @@ data "aws_partition" "current" { resource "aws_config_configuration_recorder" "test" { depends_on = [aws_iam_role_policy_attachment.test] - - name = %[1]q - role_arn = aws_iam_role.test.arn + name = %[1]q + role_arn = aws_iam_role.test.arn } resource "aws_iam_role" "test" { - name = %[1]q - + name = %[1]q assume_role_policy = < Date: Tue, 26 Jan 2021 20:15:34 -0500 Subject: [PATCH 010/398] More whitespace "fixes" in strings. Seems the linter is as confused as I am. --- ...esource_aws_config_organization_conformance_pack_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aws/resource_aws_config_organization_conformance_pack_test.go b/aws/resource_aws_config_organization_conformance_pack_test.go index 4d446cfeb45..dfe07b1220b 100644 --- a/aws/resource_aws_config_organization_conformance_pack_test.go +++ b/aws/resource_aws_config_organization_conformance_pack_test.go @@ -363,11 +363,11 @@ EOT func testAccConfigOrganizationConformancePackConfigRuleIdentifierParameter(rName, ruleIdentifier, pKey, pValue string) string { return fmt.Sprintf(` resource "aws_config_organization_conformance_pack" "test" { - name = %[1]q + name = %[1]q input_parameters = { %[3]s = %[4]q } - template_body = < Date: Tue, 26 Jan 2021 20:27:15 -0500 Subject: [PATCH 011/398] so many lints, so little time --- aws/resource_aws_config_organization_conformance_pack.go | 7 ++++--- ...source_aws_config_organization_conformance_pack_test.go | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/aws/resource_aws_config_organization_conformance_pack.go b/aws/resource_aws_config_organization_conformance_pack.go index 7474d3693bb..1a1c024c2b9 100644 --- a/aws/resource_aws_config_organization_conformance_pack.go +++ b/aws/resource_aws_config_organization_conformance_pack.go @@ -2,15 +2,16 @@ package aws import ( "fmt" + "log" + "regexp" + "time" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/configservice" "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" - "log" - "regexp" - "time" ) func resourceAwsConfigOrganizationConformancePack() *schema.Resource { diff --git a/aws/resource_aws_config_organization_conformance_pack_test.go b/aws/resource_aws_config_organization_conformance_pack_test.go index dfe07b1220b..3c4509001d1 100644 --- a/aws/resource_aws_config_organization_conformance_pack_test.go +++ b/aws/resource_aws_config_organization_conformance_pack_test.go @@ -2,12 +2,13 @@ package aws import ( "fmt" + "regexp" + "testing" + "github.com/aws/aws-sdk-go/service/configservice" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" - "regexp" - "testing" ) func TestAccConfigOrganizationConformancePack_basic(t *testing.T) { From 0bbc5e7f88fdeed16ab6b77385cafa158f481f93 Mon Sep 17 00:00:00 2001 From: Chris Trawick Date: Tue, 26 Jan 2021 21:46:21 -0500 Subject: [PATCH 012/398] Added documentation. --- ...rganization_conformance_pack.html.markdown | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 website/docs/r/config_organization_conformance_pack.html.markdown diff --git a/website/docs/r/config_organization_conformance_pack.html.markdown b/website/docs/r/config_organization_conformance_pack.html.markdown new file mode 100644 index 00000000000..8d1e26205bf --- /dev/null +++ b/website/docs/r/config_organization_conformance_pack.html.markdown @@ -0,0 +1,60 @@ +--- +subcategory: "Config" +layout: "aws" +page_title: "AWS: aws_config_organization_conformance_pack" +description: |- + Manages a Config Organization Conformance Pack +--- + +# Resource: aws_config_organization_conformance_pack + +Manages a Config Organization Conformance Pack. More information about these rules can be found in the [Managing Conformance Packs Across all Accounts in Your Organization](https://docs.aws.amazon.com/config/latest/developerguide/conformance-pack-organization-apis.html) and [AWS Config Managed Rules](https://docs.aws.amazon.com/config/latest/developerguide/evaluate-config_use-managed-rules.html) documentation. Example conformance pack templates may be found in the [AWS Config Rules Repository](https://github.com/awslabs/aws-config-rules/tree/master/aws-config-conformance-packs). + +~> **NOTE:** This resource must be created in the Organization master account or a delegate administrator account and rules will include the master account unless its ID is added to the `excluded_accounts` argument. + +~> **NOTE:** Every Organization account except those configured in the `excluded_accounts` argument must have a Configuration Recorder with proper IAM permissions before the rule will successfully create or update. See also the [`aws_config_configuration_recorder` resource](/docs/providers/aws/r/config_configuration_recorder.html). + +## Example Usage + +```hcl +resource "aws_config_organization_conformance_pack" "test" { + name = "example" + template_body = < Date: Tue, 26 Jan 2021 23:46:13 -0500 Subject: [PATCH 013/398] Linked tests to top level --- ..._aws_config_organization_conformance_pack_test.go | 12 ++++++------ aws/resource_aws_config_test.go | 8 ++++++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/aws/resource_aws_config_organization_conformance_pack_test.go b/aws/resource_aws_config_organization_conformance_pack_test.go index 3c4509001d1..18670a2fa85 100644 --- a/aws/resource_aws_config_organization_conformance_pack_test.go +++ b/aws/resource_aws_config_organization_conformance_pack_test.go @@ -11,7 +11,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) -func TestAccConfigOrganizationConformancePack_basic(t *testing.T) { +func testAccConfigOrganizationConformancePack_basic(t *testing.T) { var pack configservice.OrganizationConformancePack rName := acctest.RandomWithPrefix("tf-acc-test") rId := "IAM_PASSWORD_POLICY" @@ -45,7 +45,7 @@ func TestAccConfigOrganizationConformancePack_basic(t *testing.T) { }) } -func TestAccConfigOrganizationConformancePack_disappears(t *testing.T) { +func testAccConfigOrganizationConformancePack_disappears(t *testing.T) { var pack configservice.OrganizationConformancePack rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_config_organization_conformance_pack.test" @@ -67,7 +67,7 @@ func TestAccConfigOrganizationConformancePack_disappears(t *testing.T) { }) } -func TestAccConfigOrganizationConformancePack_inputParameters(t *testing.T) { +func testAccConfigOrganizationConformancePack_InputParameters(t *testing.T) { var pack configservice.OrganizationConformancePack rName := acctest.RandomWithPrefix("tf-acc-test") rId := "IAM_PASSWORD_POLICY" @@ -103,7 +103,7 @@ func TestAccConfigOrganizationConformancePack_inputParameters(t *testing.T) { }) } -func TestAccConfigOrganizationConformancePack_s3Delivery(t *testing.T) { +func testAccConfigOrganizationConformancePack_S3Delivery(t *testing.T) { var pack configservice.OrganizationConformancePack rName := acctest.RandomWithPrefix("tf-acc-test") bName := "awsconfigconforms" + rName @@ -138,7 +138,7 @@ func TestAccConfigOrganizationConformancePack_s3Delivery(t *testing.T) { }) } -func TestAccConfigOrganizationConformancePack_s3Template(t *testing.T) { +func testAccConfigOrganizationConformancePack_S3Template(t *testing.T) { var pack configservice.OrganizationConformancePack rName := acctest.RandomWithPrefix("tf-acc-test") bName := rName @@ -174,7 +174,7 @@ func TestAccConfigOrganizationConformancePack_s3Template(t *testing.T) { }) } -func TestAccConfigOrganizationConformancePack_excludedAccounts(t *testing.T) { +func testAccConfigOrganizationConformancePack_ExcludedAccounts(t *testing.T) { var pack configservice.OrganizationConformancePack rName := acctest.RandomWithPrefix("tf-acc-test") rId := "IAM_PASSWORD_POLICY" diff --git a/aws/resource_aws_config_test.go b/aws/resource_aws_config_test.go index ecaaeecd53e..a97c7ba88a1 100644 --- a/aws/resource_aws_config_test.go +++ b/aws/resource_aws_config_test.go @@ -74,6 +74,14 @@ func TestAccAWSConfig_serial(t *testing.T) { "TagKeyScope": testAccConfigOrganizationManagedRule_TagKeyScope, "TagValueScope": testAccConfigOrganizationManagedRule_TagValueScope, }, + "OrganizationConformancePack": { + "basic": testAccConfigOrganizationConformancePack_basic, + "disappears": testAccConfigOrganizationConformancePack_disappears, + "InputParameters": testAccConfigOrganizationConformancePack_InputParameters, + "S3Delivery": testAccConfigOrganizationConformancePack_S3Delivery, + "S3Template": testAccConfigOrganizationConformancePack_S3Template, + "ExcludedAccounts": testAccConfigOrganizationConformancePack_ExcludedAccounts, + }, "RemediationConfiguration": { "basic": testAccConfigRemediationConfiguration_basic, "disappears": testAccConfigRemediationConfiguration_disappears, From 07cb43a1d9dad27efc24dc3a5d2e22f0ec109abd Mon Sep 17 00:00:00 2001 From: Chris Trawick Date: Wed, 27 Jan 2021 07:13:32 -0500 Subject: [PATCH 014/398] Added changelog --- .changelog/17298.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/17298.txt diff --git a/.changelog/17298.txt b/.changelog/17298.txt new file mode 100644 index 00000000000..dc491d05c22 --- /dev/null +++ b/.changelog/17298.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_config_organization_conformance_pack +``` \ No newline at end of file From b6b4730f5712ae5694bbe5ba6dc3ee615e21b5f7 Mon Sep 17 00:00:00 2001 From: Chris Trawick Date: Wed, 27 Jan 2021 07:15:27 -0500 Subject: [PATCH 015/398] Cleanup --- deleteme/main.tf | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 deleteme/main.tf diff --git a/deleteme/main.tf b/deleteme/main.tf deleted file mode 100644 index e69de29bb2d..00000000000 From d2d14309ad4fbf34274bd988e81097f6b4b36eb8 Mon Sep 17 00:00:00 2001 From: Chris Trawick Date: Wed, 27 Jan 2021 11:17:17 -0500 Subject: [PATCH 016/398] Serialized tests for consistency with other config tests --- ...nfig_organization_conformance_pack_test.go | 42 ++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/aws/resource_aws_config_organization_conformance_pack_test.go b/aws/resource_aws_config_organization_conformance_pack_test.go index 18670a2fa85..988926dfc58 100644 --- a/aws/resource_aws_config_organization_conformance_pack_test.go +++ b/aws/resource_aws_config_organization_conformance_pack_test.go @@ -17,7 +17,7 @@ func testAccConfigOrganizationConformancePack_basic(t *testing.T) { rId := "IAM_PASSWORD_POLICY" resourceName := "aws_config_organization_conformance_pack.test" - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccOrganizationsMasterPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, @@ -50,7 +50,7 @@ func testAccConfigOrganizationConformancePack_disappears(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_config_organization_conformance_pack.test" - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccOrganizationsMasterPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, @@ -75,7 +75,7 @@ func testAccConfigOrganizationConformancePack_InputParameters(t *testing.T) { pValue := "ParamValue" resourceName := "aws_config_organization_conformance_pack.test" - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccOrganizationsMasterPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, @@ -110,7 +110,7 @@ func testAccConfigOrganizationConformancePack_S3Delivery(t *testing.T) { rId := "IAM_PASSWORD_POLICY" resourceName := "aws_config_organization_conformance_pack.test" - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccOrganizationsMasterPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, @@ -146,7 +146,7 @@ func testAccConfigOrganizationConformancePack_S3Template(t *testing.T) { rId := "IAM_PASSWORD_POLICY" resourceName := "aws_config_organization_conformance_pack.test" - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccOrganizationsMasterPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, @@ -180,7 +180,7 @@ func testAccConfigOrganizationConformancePack_ExcludedAccounts(t *testing.T) { rId := "IAM_PASSWORD_POLICY" resourceName := "aws_config_organization_conformance_pack.test" - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testAccOrganizationsMasterPreCheck(t) @@ -303,7 +303,7 @@ func testAccCheckConfigOrganizationConformancePackSuccessful(resourceName string } } -/*func testAccConfigOrganizationConformancePackBase(rName string) string { +func testAccConfigOrganizationConformancePackBase(rName string) string { return fmt.Sprintf(` data "aws_partition" "current" { @@ -341,10 +341,13 @@ resource "aws_iam_role_policy_attachment" "test" { `, rName) } -*/ + func testAccConfigOrganizationConformancePackConfigRuleIdentifier(rName, ruleIdentifier string) string { return fmt.Sprintf(` +%[3]s + resource "aws_config_organization_conformance_pack" "test" { + depends_on = [aws_config_configuration_recorder.test] name = %[1]q template_body = < Date: Wed, 27 Jan 2021 11:37:05 -0500 Subject: [PATCH 017/398] removed whitespace from a string, wishing the linter would make up its mind --- aws/resource_aws_config_organization_conformance_pack_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_config_organization_conformance_pack_test.go b/aws/resource_aws_config_organization_conformance_pack_test.go index 988926dfc58..b26a42d7025 100644 --- a/aws/resource_aws_config_organization_conformance_pack_test.go +++ b/aws/resource_aws_config_organization_conformance_pack_test.go @@ -369,8 +369,8 @@ func testAccConfigOrganizationConformancePackConfigRuleIdentifierParameter(rName %[5]s resource "aws_config_organization_conformance_pack" "test" { - depends_on = [aws_config_configuration_recorder.test] - name = %[1]q + depends_on = [aws_config_configuration_recorder.test] + name = %[1]q input_parameters = { %[3]s = %[4]q } From 30e33b81efeeb13701b0eef5d437226d4002be6d Mon Sep 17 00:00:00 2001 From: Chris Trawick Date: Thu, 28 Jan 2021 18:28:25 -0500 Subject: [PATCH 018/398] fixed a potential conflict --- aws/configservice.go | 18 ++++++++++++++++++ ...aws_config_organization_conformance_pack.go | 18 ------------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/aws/configservice.go b/aws/configservice.go index 7c835d3f653..a395eb3b30f 100644 --- a/aws/configservice.go +++ b/aws/configservice.go @@ -381,3 +381,21 @@ func configDescribeOrganizationConformancePackDetailedStatus(conn *configservice return ret, nil } + +func expandConfigConformancePackParameters(m map[string]interface{}) (params []*configservice.ConformancePackInputParameter) { + for k, v := range m { + params = append(params, &configservice.ConformancePackInputParameter{ + ParameterName: aws.String(k), + ParameterValue: aws.String(v.(string)), + }) + } + return +} + +func flattenConformancePackInputParameters(parameters []*configservice.ConformancePackInputParameter) (m map[string]string) { + m = make(map[string]string) + for _, p := range parameters { + m[*p.ParameterName] = *p.ParameterValue + } + return +} diff --git a/aws/resource_aws_config_organization_conformance_pack.go b/aws/resource_aws_config_organization_conformance_pack.go index 1a1c024c2b9..9954c47a4b6 100644 --- a/aws/resource_aws_config_organization_conformance_pack.go +++ b/aws/resource_aws_config_organization_conformance_pack.go @@ -142,16 +142,6 @@ func expandConfigConformancePackExcludedAccounts(i []interface{}) (ret []*string return } -func expandConfigConformancePackParameters(m map[string]interface{}) (params []*configservice.ConformancePackInputParameter) { - for k, v := range m { - params = append(params, &configservice.ConformancePackInputParameter{ - ParameterName: aws.String(k), - ParameterValue: aws.String(v.(string)), - }) - } - return -} - func refreshOrganizationConformancePackStatus(d *schema.ResourceData, conn *configservice.ConfigService) func() (interface{}, string, error) { return func() (interface{}, string, error) { out, err := conn.DescribeOrganizationConformancePackStatuses(&configservice.DescribeOrganizationConformancePackStatusesInput{ @@ -226,14 +216,6 @@ func resourceAwsConfigOrganizationConformancePackRead(d *schema.ResourceData, me return nil } -func flattenConformancePackInputParameters(parameters []*configservice.ConformancePackInputParameter) (m map[string]string) { - m = make(map[string]string) - for _, p := range parameters { - m[*p.ParameterName] = *p.ParameterValue - } - return -} - func resourceAwsConfigOrganizationConformancePackDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).configconn From f814caf758750187b56ef49f0b9ef58bbe37e01f Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Wed, 17 Feb 2021 17:11:52 -0500 Subject: [PATCH 019/398] CR updates; update waiter funcs, acctest coverage, and documentation --- aws/configservice.go | 278 +++++---- aws/provider_test.go | 32 -- ...ws_config_organization_conformance_pack.go | 296 +++++----- ...nfig_organization_conformance_pack_test.go | 540 +++++++++++++----- aws/resource_aws_config_test.go | 21 +- ...rganization_conformance_pack.html.markdown | 89 ++- 6 files changed, 813 insertions(+), 443 deletions(-) diff --git a/aws/configservice.go b/aws/configservice.go index a395eb3b30f..e03ba2e6966 100644 --- a/aws/configservice.go +++ b/aws/configservice.go @@ -15,8 +15,15 @@ const ( ConfigConformancePackCreateTimeout = 5 * time.Minute ConfigConformancePackDeleteTimeout = 5 * time.Minute + ConfigOrganizationConformancePackCreateTimeout = 10 * time.Minute + ConfigOrganizationConformancePackUpdateTimeout = 10 * time.Minute + ConfigOrganizationConformancePackDeleteTimeout = 10 * time.Minute + ConfigConformancePackStatusNotFound = "NotFound" ConfigConformancePackStatusUnknown = "Unknown" + + ConfigOrganizationConformancePackStatusNotFound = "NotFound" + ConfigOrganizationConformancePackStatusUnknown = "Unknown" ) func configDescribeConformancePack(conn *configservice.ConfigService, name string) (*configservice.ConformancePackDetail, error) { @@ -135,6 +142,62 @@ func configDescribeOrganizationConfigRuleStatus(conn *configservice.ConfigServic return nil, nil } +func configDescribeOrganizationConformancePack(conn *configservice.ConfigService, name string) (*configservice.OrganizationConformancePack, error) { + input := &configservice.DescribeOrganizationConformancePacksInput{ + OrganizationConformancePackNames: []*string{aws.String(name)}, + } + + for { + output, err := conn.DescribeOrganizationConformancePacks(input) + + if err != nil { + return nil, err + } + + for _, pack := range output.OrganizationConformancePacks { + if aws.StringValue(pack.OrganizationConformancePackName) == name { + return pack, nil + } + } + + if aws.StringValue(output.NextToken) == "" { + break + } + + input.NextToken = output.NextToken + } + + return nil, nil +} + +func configDescribeOrganizationConformancePackStatus(conn *configservice.ConfigService, name string) (*configservice.OrganizationConformancePackStatus, error) { + input := &configservice.DescribeOrganizationConformancePackStatusesInput{ + OrganizationConformancePackNames: []*string{aws.String(name)}, + } + + for { + output, err := conn.DescribeOrganizationConformancePackStatuses(input) + + if err != nil { + return nil, err + } + + for _, status := range output.OrganizationConformancePackStatuses { + if aws.StringValue(status.OrganizationConformancePackName) == name { + return status, nil + } + } + + if aws.StringValue(output.NextToken) == "" { + break + } + + input.NextToken = output.NextToken + } + + return nil, nil +} + func configGetOrganizationConfigRuleDetailedStatus(conn *configservice.ConfigService, ruleName, ruleStatus string) ([]*configservice.MemberAccountStatus, error) { input := &configservice.GetOrganizationConfigRuleDetailedStatusInput{ Filters: &configservice.StatusDetailFilters{ @@ -163,6 +226,35 @@ func configGetOrganizationConfigRuleDetailedStatus(conn *configservice.ConfigSer return statuses, nil } +func configGetOrganizationConformancePackDetailedStatus(conn *configservice.ConfigService, name, status string) ([]*configservice.OrganizationConformancePackDetailedStatus, error) { + input := &configservice.GetOrganizationConformancePackDetailedStatusInput{ + Filters: &configservice.OrganizationResourceDetailedStatusFilters{ + Status: aws.String(status), + }, + OrganizationConformancePackName: aws.String(name), + } + + var statuses []*configservice.OrganizationConformancePackDetailedStatus + + for { + output, err := conn.GetOrganizationConformancePackDetailedStatus(input) + + if err != nil { + return nil, err + } + + statuses = append(statuses, output.OrganizationConformancePackDetailedStatuses...) + + if aws.StringValue(output.NextToken) == "" { + break + } + + input.NextToken = output.NextToken + } + + return statuses, nil +} + func configRefreshConformancePackStatus(conn *configservice.ConfigService, name string) resource.StateRefreshFunc { return func() (interface{}, string, error) { status, err := configDescribeConformancePackStatus(conn, name) @@ -221,6 +313,44 @@ func configRefreshOrganizationConfigRuleStatus(conn *configservice.ConfigService } } +func configRefreshOrganizationConformancePackStatus(conn *configservice.ConfigService, name string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + status, err := configDescribeOrganizationConformancePackStatus(conn, name) + + if err != nil { + return nil, ConfigOrganizationConformancePackStatusUnknown, err + } + + if status == nil { + return nil, ConfigOrganizationConformancePackStatusNotFound, nil + } + + if status.ErrorCode != nil { + return status, aws.StringValue(status.Status), fmt.Errorf("%s: %s", aws.StringValue(status.ErrorCode), aws.StringValue(status.ErrorMessage)) + } + + switch aws.StringValue(status.Status) { + case configservice.OrganizationResourceStatusCreateFailed, configservice.OrganizationResourceStatusDeleteFailed, configservice.OrganizationResourceStatusUpdateFailed: + // Display detailed errors for failed member accounts + memberAccountStatuses, err := configGetOrganizationConformancePackDetailedStatus(conn, name, aws.StringValue(status.Status)) + + if err != nil { + return status, aws.StringValue(status.Status), fmt.Errorf("unable to get Config Organization Conformance Pack detailed status for showing member account errors: %w", err) + } + + var errBuilder strings.Builder + + for _, mas := range memberAccountStatuses { + errBuilder.WriteString(fmt.Sprintf("Account ID (%s): %s: %s\n", aws.StringValue(mas.AccountId), aws.StringValue(mas.ErrorCode), aws.StringValue(mas.ErrorMessage))) + } + + return status, aws.StringValue(status.Status), fmt.Errorf("Failed in %d account(s):\n\n%s", len(memberAccountStatuses), errBuilder.String()) + } + + return status, aws.StringValue(status.Status), nil + } +} + func configWaitForConformancePackStateCreateComplete(conn *configservice.ConfigService, name string) error { stateChangeConf := resource.StateChangeConf{ Pending: []string{configservice.ConformancePackStateCreateInProgress}, @@ -231,10 +361,6 @@ func configWaitForConformancePackStateCreateComplete(conn *configservice.ConfigS _, err := stateChangeConf.WaitForState() - if tfawserr.ErrCodeEquals(err, configservice.ErrCodeNoSuchConformancePackException) { - return nil - } - return err } @@ -256,6 +382,52 @@ func configWaitForConformancePackStateDeleteComplete(conn *configservice.ConfigS return err } +func configWaitForOrganizationConformancePackStatusCreateSuccessful(conn *configservice.ConfigService, name string) error { + stateChangeConf := resource.StateChangeConf{ + Pending: []string{configservice.OrganizationResourceStatusCreateInProgress}, + Target: []string{configservice.OrganizationResourceStatusCreateSuccessful}, + Timeout: ConfigOrganizationConformancePackCreateTimeout, + Refresh: configRefreshOrganizationConformancePackStatus(conn, name), + // Include a Delay to avoid transient error i.e. OrganizationAccessDeniedException + Delay: 1 * time.Minute, + } + + _, err := stateChangeConf.WaitForState() + + return err + +} + +func configWaitForOrganizationConformancePackStatusUpdateSuccessful(conn *configservice.ConfigService, name string) error { + stateChangeConf := resource.StateChangeConf{ + Pending: []string{configservice.OrganizationResourceStatusUpdateInProgress}, + Target: []string{configservice.OrganizationResourceStatusUpdateSuccessful}, + Timeout: ConfigOrganizationConformancePackUpdateTimeout, + Refresh: configRefreshOrganizationConformancePackStatus(conn, name), + } + + _, err := stateChangeConf.WaitForState() + + return err +} + +func configWaitForOrganizationConformancePackStatusDeleteSuccessful(conn *configservice.ConfigService, name string) error { + stateChangeConf := resource.StateChangeConf{ + Pending: []string{configservice.OrganizationResourceStatusDeleteInProgress}, + Target: []string{configservice.OrganizationResourceStatusDeleteSuccessful}, + Timeout: ConfigOrganizationConformancePackDeleteTimeout, + Refresh: configRefreshOrganizationConformancePackStatus(conn, name), + } + + _, err := stateChangeConf.WaitForState() + + if tfawserr.ErrCodeEquals(err, configservice.ErrCodeNoSuchOrganizationConformancePackException) { + return nil + } + + return err +} + func configWaitForOrganizationRuleStatusCreateSuccessful(conn *configservice.ConfigService, name string, timeout time.Duration) error { stateChangeConf := &resource.StateChangeConf{ Pending: []string{configservice.OrganizationRuleStatusCreateInProgress}, @@ -301,101 +473,3 @@ func configWaitForOrganizationRuleStatusUpdateSuccessful(conn *configservice.Con return err } - -func configDescribeOrganizationConformancePack(conn *configservice.ConfigService, name string) (*configservice.OrganizationConformancePack, error) { - input := &configservice.DescribeOrganizationConformancePacksInput{ - OrganizationConformancePackNames: []*string{aws.String(name)}, - } - - for { - output, err := conn.DescribeOrganizationConformancePacks(input) - - if err != nil { - return nil, err - } - - for _, pack := range output.OrganizationConformancePacks { - if aws.StringValue(pack.OrganizationConformancePackName) == name { - return pack, nil - } - } - - if aws.StringValue(output.NextToken) == "" { - break - } - - input.NextToken = output.NextToken - } - - return nil, nil -} - -func configDescribeOrganizationConformancePackStatus(conn *configservice.ConfigService, name string) (*configservice.OrganizationConformancePackStatus, error) { - input := &configservice.DescribeOrganizationConformancePackStatusesInput{ - OrganizationConformancePackNames: []*string{aws.String(name)}, - } - - for { - output, err := conn.DescribeOrganizationConformancePackStatuses(input) - - if err != nil { - return nil, err - } - - for _, status := range output.OrganizationConformancePackStatuses { - if aws.StringValue(status.OrganizationConformancePackName) == name { - return status, nil - } - } - - if aws.StringValue(output.NextToken) == "" { - break - } - - input.NextToken = output.NextToken - } - - return nil, nil -} - -func configDescribeOrganizationConformancePackDetailedStatus(conn *configservice.ConfigService, name string) ([]*configservice.OrganizationConformancePackDetailedStatus, error) { - input := &configservice.GetOrganizationConformancePackDetailedStatusInput{ - OrganizationConformancePackName: aws.String(name), - } - var ret []*configservice.OrganizationConformancePackDetailedStatus - for { - output, err := conn.GetOrganizationConformancePackDetailedStatus(input) - - if err != nil { - return nil, err - } - - ret = append(ret, output.OrganizationConformancePackDetailedStatuses...) - - if aws.StringValue(output.NextToken) == "" { - break - } - - input.NextToken = output.NextToken - } - - return ret, nil -} - -func expandConfigConformancePackParameters(m map[string]interface{}) (params []*configservice.ConformancePackInputParameter) { - for k, v := range m { - params = append(params, &configservice.ConformancePackInputParameter{ - ParameterName: aws.String(k), - ParameterValue: aws.String(v.(string)), - }) - } - return -} - -func flattenConformancePackInputParameters(parameters []*configservice.ConformancePackInputParameter) (m map[string]string) { - m = make(map[string]string) - for _, p := range parameters { - m[*p.ParameterName] = *p.ParameterValue - } - return -} diff --git a/aws/provider_test.go b/aws/provider_test.go index 5acdb1bb13c..5a4e7a8cfa5 100644 --- a/aws/provider_test.go +++ b/aws/provider_test.go @@ -792,38 +792,6 @@ func testAccOrganizationsEnabledPreCheck(t *testing.T) { } } -func testAccOrganizationsMasterPreCheck(t *testing.T) { - conn := testAccProvider.Meta().(*AWSClient).organizationsconn - input := &organizations.DescribeOrganizationInput{} - out, err := conn.DescribeOrganization(input) - if isAWSErr(err, organizations.ErrCodeAWSOrganizationsNotInUseException, "") { - t.Skip("this AWS account must be an existing member of an AWS Organization") - } - if err != nil { - t.Fatalf("error describing AWS Organization: %s", err) - } - masterAccountId := *out.Organization.MasterAccountId - thisAccountId := testAccProvider.Meta().(*AWSClient).accountid - if masterAccountId != thisAccountId { - t.Skipf("this AWS account must be master of its AWS Organization ( %q != %q )", masterAccountId, thisAccountId) - } -} - -func testAccOrganizationsMinAccountsPreCheck(t *testing.T, minAccounts int) { - conn := testAccProvider.Meta().(*AWSClient).organizationsconn - input := &organizations.ListAccountsInput{} - out, err := conn.ListAccounts(input) - if isAWSErr(err, organizations.ErrCodeAWSOrganizationsNotInUseException, "") { - t.Skip("this AWS account must be an existing member of an AWS Organization") - } - if err != nil { - t.Fatalf("error listing accounts in AWS Organization: %s", err) - } - if len(out.Accounts) < minAccounts { - t.Skipf("this AWS account must have at least %d accounts in its organization", minAccounts) - } -} - func testAccPreCheckIamServiceLinkedRole(t *testing.T, pathPrefix string) { conn := testAccProvider.Meta().(*AWSClient).iamconn diff --git a/aws/resource_aws_config_organization_conformance_pack.go b/aws/resource_aws_config_organization_conformance_pack.go index 9954c47a4b6..356423e34b2 100644 --- a/aws/resource_aws_config_organization_conformance_pack.go +++ b/aws/resource_aws_config_organization_conformance_pack.go @@ -4,21 +4,21 @@ import ( "fmt" "log" "regexp" - "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/configservice" + "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/tfresource" ) func resourceAwsConfigOrganizationConformancePack() *schema.Resource { return &schema.Resource{ - Create: resourceAwsConfigOrganizationConformancePackPut, + Create: resourceAwsConfigOrganizationConformancePackCreate, Read: resourceAwsConfigOrganizationConformancePackRead, - Update: resourceAwsConfigOrganizationConformancePackPut, + Update: resourceAwsConfigOrganizationConformancePackUpdate, Delete: resourceAwsConfigOrganizationConformancePackDelete, Importer: &schema.ResourceImporter{ @@ -26,40 +26,16 @@ func resourceAwsConfigOrganizationConformancePack() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.All( - validation.StringLenBetween(1, 51200), - validation.StringMatch(regexp.MustCompile(`^[a-zA-Z][-a-zA-Z0-9]*$`), "must be a valid conformance pack name"), - ), - }, "arn": { Type: schema.TypeString, Computed: true, }, - "template_s3_uri": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringLenBetween(1, 256), - }, - "template_body": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.All( - validation.StringLenBetween(1, 51200), - validateStringIsJsonOrYaml), - StateFunc: func(v interface{}) string { - template, _ := normalizeJsonOrYamlString(v) - return template - }, - }, "delivery_s3_bucket": { Type: schema.TypeString, Optional: true, ValidateFunc: validation.All( validation.StringLenBetween(1, 63), - validation.StringMatch(regexp.MustCompile("awsconfigconforms.+"), "must start with 'awsconfigconforms'"), + validation.StringMatch(regexp.MustCompile(`^awsconfigconforms`), `must begin with "awsconfigconforms"`), ), }, "delivery_s3_key_prefix": { @@ -67,24 +43,66 @@ func resourceAwsConfigOrganizationConformancePack() *schema.Resource { Optional: true, ValidateFunc: validation.StringLenBetween(1, 1024), }, - "input_parameters": { - Type: schema.TypeMap, - Elem: &schema.Schema{Type: schema.TypeString}, - Optional: true, - }, "excluded_accounts": { - Type: schema.TypeList, + Type: schema.TypeSet, + Optional: true, + MaxItems: 1000, Elem: &schema.Schema{ Type: schema.TypeString, - ValidateFunc: validation.StringMatch(regexp.MustCompile(`^[0-9]{12}$`), "must be a valid AWS account ID"), + ValidateFunc: validateAwsAccountId, + }, + }, + "input_parameter": { + Type: schema.TypeSet, + Optional: true, + MaxItems: 60, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "parameter_name": { + Type: schema.TypeString, + Required: true, + }, + "parameter_value": { + Type: schema.TypeString, + Required: true, + }, + }, }, + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 128), + validation.StringMatch(regexp.MustCompile(`^[a-zA-Z]`), "must begin with alphabetic character"), + validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9-]+$`), "must contain only alphanumeric and hyphen characters"), + ), + }, + "template_body": { + Type: schema.TypeString, + Optional: true, + DiffSuppressFunc: suppressEquivalentJsonOrYamlDiffs, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 51200), + validateStringIsJsonOrYaml, + ), + ConflictsWith: []string{"template_s3_uri"}, + }, + "template_s3_uri": { + Type: schema.TypeString, Optional: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 1024), + validation.StringMatch(regexp.MustCompile(`^s3://`), "must begin with s3://"), + ), + ConflictsWith: []string{"template_body"}, }, }, } } -func resourceAwsConfigOrganizationConformancePackPut(d *schema.ResourceData, meta interface{}) error { +func resourceAwsConfigOrganizationConformancePackCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).configconn name := d.Get("name").(string) @@ -95,167 +113,179 @@ func resourceAwsConfigOrganizationConformancePackPut(d *schema.ResourceData, met if v, ok := d.GetOk("delivery_s3_bucket"); ok { input.DeliveryS3Bucket = aws.String(v.(string)) } + if v, ok := d.GetOk("delivery_s3_key_prefix"); ok { input.DeliveryS3KeyPrefix = aws.String(v.(string)) } + if v, ok := d.GetOk("excluded_accounts"); ok { - input.ExcludedAccounts = expandConfigConformancePackExcludedAccounts(v.([]interface{})) + input.ExcludedAccounts = expandStringSet(v.(*schema.Set)) } - if v, ok := d.GetOk("input_parameters"); ok { - input.ConformancePackInputParameters = expandConfigConformancePackParameters(v.(map[string]interface{})) + + if v, ok := d.GetOk("input_parameter"); ok { + input.ConformancePackInputParameters = expandConfigConformancePackInputParameters(v.(*schema.Set).List()) } + if v, ok := d.GetOk("template_body"); ok { input.TemplateBody = aws.String(v.(string)) } + if v, ok := d.GetOk("template_s3_uri"); ok { input.TemplateS3Uri = aws.String(v.(string)) } - _, err := conn.PutOrganizationConformancePack(&input) + err := resource.Retry(ConfigOrganizationConformancePackCreateTimeout, func() *resource.RetryError { + _, err := conn.PutOrganizationConformancePack(&input) + + if err != nil { + // OrganizationAccessDeniedException seems to be a transient error + if tfawserr.ErrCodeEquals(err, configservice.ErrCodeOrganizationAccessDeniedException) { + return resource.RetryableError(err) + } + + return resource.NonRetryableError(err) + } + + return nil + }) + + if tfresource.TimedOut(err) { + _, err = conn.PutOrganizationConformancePack(&input) + } + if err != nil { - return fmt.Errorf("failed to put AWSConfig organization conformance pack %q: %s", name, err) + return fmt.Errorf("error creating Config Organization Conformance Pack (%s): %w", name, err) } d.SetId(name) - conf := resource.StateChangeConf{ - Pending: []string{ - configservice.OrganizationResourceDetailedStatusCreateInProgress, - configservice.OrganizationResourceDetailedStatusUpdateInProgress, - }, - Target: []string{ - configservice.OrganizationResourceDetailedStatusCreateSuccessful, - configservice.OrganizationResourceDetailedStatusUpdateSuccessful, - }, - Timeout: 30 * time.Minute, - Refresh: refreshOrganizationConformancePackStatus(d, conn), - } - if _, err := conf.WaitForState(); err != nil { - return err - } - return resourceAwsConfigOrganizationConformancePackRead(d, meta) -} -func expandConfigConformancePackExcludedAccounts(i []interface{}) (ret []*string) { - for _, v := range i { - ret = append(ret, aws.String(v.(string))) + if err := configWaitForOrganizationConformancePackStatusCreateSuccessful(conn, d.Id()); err != nil { + return fmt.Errorf("error waiting for Config Organization Conformance Pack (%s) to be created: %w", d.Id(), err) } - return -} -func refreshOrganizationConformancePackStatus(d *schema.ResourceData, conn *configservice.ConfigService) func() (interface{}, string, error) { - return func() (interface{}, string, error) { - out, err := conn.DescribeOrganizationConformancePackStatuses(&configservice.DescribeOrganizationConformancePackStatusesInput{ - OrganizationConformancePackNames: []*string{aws.String(d.Id())}, - }) - if err != nil { - if awsErr, ok := err.(awserr.Error); ok && isAWSErr(awsErr, configservice.ErrCodeNoSuchOrganizationConformancePackException, "") { - return 42, "", nil - } - return 42, "", fmt.Errorf("failed to describe organization conformance pack %q: %s", d.Id(), err) - } - if len(out.OrganizationConformancePackStatuses) < 1 { - return 42, "", nil - } - status := out.OrganizationConformancePackStatuses[0] - return out, *status.Status, nil - } + return resourceAwsConfigOrganizationConformancePackRead(d, meta) } func resourceAwsConfigOrganizationConformancePackRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).configconn - out, err := conn.DescribeOrganizationConformancePacks(&configservice.DescribeOrganizationConformancePacksInput{ - OrganizationConformancePackNames: []*string{aws.String(d.Id())}, - }) + pack, err := configDescribeOrganizationConformancePack(conn, d.Id()) + + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, configservice.ErrCodeNoSuchOrganizationConformancePackException) { + log.Printf("[WARN] Config Organization Conformance Pack (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + if err != nil { - if awsErr, ok := err.(awserr.Error); ok && isAWSErr(err, configservice.ErrCodeNoSuchOrganizationConformancePackException, "") { - log.Printf("[WARN] Organization Conformance Pack %q is gone (%s)", d.Id(), awsErr.Code()) - d.SetId("") - return nil - } - return err + return fmt.Errorf("error describing Config Organization Conformance Pack (%s): %w", d.Id(), err) } - numberOfPacks := len(out.OrganizationConformancePacks) - if numberOfPacks < 1 { - log.Printf("[WARN] Organization Conformance Pack %q is gone (no packs found)", d.Id()) + if pack == nil { + if d.IsNewResource() { + return fmt.Errorf("error describing Config Organization Conformance Pack (%s): not found", d.Id()) + } + + log.Printf("[WARN] Config Organization Conformance Pack (%s) not found, removing from state", d.Id()) d.SetId("") return nil } - if numberOfPacks > 1 { - return fmt.Errorf("expected exactly 1 organization conformance pack, received %d: %#v", - numberOfPacks, out.OrganizationConformancePacks) + d.Set("arn", pack.OrganizationConformancePackArn) + d.Set("name", pack.OrganizationConformancePackName) + d.Set("delivery_s3_bucket", pack.DeliveryS3Bucket) + d.Set("delivery_s3_key_prefix", pack.DeliveryS3KeyPrefix) + + if err = d.Set("excluded_accounts", flattenStringSet(pack.ExcludedAccounts)); err != nil { + return fmt.Errorf("error setting excluded_accounts: %w", err) + } + + if err = d.Set("input_parameter", flattenConfigConformancePackInputParameters(pack.ConformancePackInputParameters)); err != nil { + return fmt.Errorf("error setting input_parameter: %w", err) } - log.Printf("[DEBUG] AWS Config organization conformance packs received: %s", out) + return nil +} + +func resourceAwsConfigOrganizationConformancePackUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).configconn - pack := out.OrganizationConformancePacks[0] - if err = d.Set("arn", pack.OrganizationConformancePackArn); err != nil { - return err + input := configservice.PutOrganizationConformancePackInput{ + OrganizationConformancePackName: aws.String(d.Id()), } - if err = d.Set("name", pack.OrganizationConformancePackName); err != nil { - return err + + if v, ok := d.GetOk("delivery_s3_bucket"); ok { + input.DeliveryS3Bucket = aws.String(v.(string)) } - if err = d.Set("delivery_s3_bucket", pack.DeliveryS3Bucket); err != nil { - return err + + if v, ok := d.GetOk("delivery_s3_key_prefix"); ok { + input.DeliveryS3KeyPrefix = aws.String(v.(string)) } - if err = d.Set("delivery_s3_key_prefix", pack.DeliveryS3KeyPrefix); err != nil { - return err + + if v, ok := d.GetOk("excluded_accounts"); ok { + input.ExcludedAccounts = expandStringSet(v.(*schema.Set)) } - if err = d.Set("excluded_accounts", pack.ExcludedAccounts); err != nil { - return err + + if v, ok := d.GetOk("input_parameter"); ok { + input.ConformancePackInputParameters = expandConfigConformancePackInputParameters(v.(*schema.Set).List()) } - if pack.ConformancePackInputParameters != nil { - if err = d.Set("input_parameters", flattenConformancePackInputParameters(pack.ConformancePackInputParameters)); err != nil { - return err - } + if v, ok := d.GetOk("template_body"); ok { + input.TemplateBody = aws.String(v.(string)) } - return nil + if v, ok := d.GetOk("template_s3_uri"); ok { + input.TemplateS3Uri = aws.String(v.(string)) + } + + _, err := conn.PutOrganizationConformancePack(&input) + if err != nil { + return fmt.Errorf("error updating Config Organization Conformance Pack (%s): %w", d.Id(), err) + } + + if err := configWaitForOrganizationConformancePackStatusUpdateSuccessful(conn, d.Id()); err != nil { + return fmt.Errorf("error waiting for Config Organization Conformance Pack (%s) to be updated: %w", d.Id(), err) + } + + return resourceAwsConfigOrganizationConformancePackRead(d, meta) } func resourceAwsConfigOrganizationConformancePackDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).configconn - name := d.Get("name").(string) - - log.Printf("[DEBUG] Deleting AWS Config organization conformance pack %q", name) input := &configservice.DeleteOrganizationConformancePackInput{ - OrganizationConformancePackName: aws.String(name), + OrganizationConformancePackName: aws.String(d.Id()), } - err := resource.Retry(30*time.Minute, func() *resource.RetryError { + + err := resource.Retry(ConfigOrganizationConformancePackDeleteTimeout, func() *resource.RetryError { _, err := conn.DeleteOrganizationConformancePack(input) + if err != nil { - if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "ResourceInUseException" { + if tfawserr.ErrCodeEquals(err, configservice.ErrCodeResourceInUseException) { return resource.RetryableError(err) } + return resource.NonRetryableError(err) } + return nil }) - if isResourceTimeoutError(err) { + + if tfresource.TimedOut(err) { _, err = conn.DeleteOrganizationConformancePack(input) } - if err != nil { - return fmt.Errorf("deleting organization conformance pack failed: %s", err) - } - conf := resource.StateChangeConf{ - Pending: []string{ - configservice.OrganizationResourceDetailedStatusDeleteInProgress, - }, - Target: []string{""}, - Timeout: 30 * time.Minute, - Refresh: refreshOrganizationConformancePackStatus(d, conn), + if tfawserr.ErrCodeEquals(err, configservice.ErrCodeNoSuchOrganizationConformancePackException) { + return nil } - _, err = conf.WaitForState() + if err != nil { - return err + return fmt.Errorf("erorr deleting Config Organization Conformance Pack (%s): %w", d.Id(), err) } - log.Printf("[DEBUG] AWS organization conformance pack %q deleted", name) + if err := configWaitForOrganizationConformancePackStatusDeleteSuccessful(conn, d.Id()); err != nil { + return fmt.Errorf("error waiting for Config Organization Conformance Pack (%s) to be deleted: %w", d.Id(), err) + } return nil } diff --git a/aws/resource_aws_config_organization_conformance_pack_test.go b/aws/resource_aws_config_organization_conformance_pack_test.go index b26a42d7025..ed86946725a 100644 --- a/aws/resource_aws_config_organization_conformance_pack_test.go +++ b/aws/resource_aws_config_organization_conformance_pack_test.go @@ -5,7 +5,9 @@ import ( "regexp" "testing" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/configservice" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -14,25 +16,23 @@ import ( func testAccConfigOrganizationConformancePack_basic(t *testing.T) { var pack configservice.OrganizationConformancePack rName := acctest.RandomWithPrefix("tf-acc-test") - rId := "IAM_PASSWORD_POLICY" resourceName := "aws_config_organization_conformance_pack.test" resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccOrganizationsMasterPreCheck(t) }, + PreCheck: func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, Steps: []resource.TestStep{ { - Config: testAccConfigOrganizationConformancePackConfigRuleIdentifier(rName, rId), + Config: testAccConfigOrganizationConformancePackBasicConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckConfigOrganizationConformancePackExists(resourceName, &pack), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "config", regexp.MustCompile(fmt.Sprintf("organization-conformance-pack/%s-.+", rName))), resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "delivery_s3_bucket", ""), resource.TestCheckResourceAttr(resourceName, "delivery_s3_key_prefix", ""), - resource.TestCheckNoResourceAttr(resourceName, "input_parameters"), - resource.TestCheckNoResourceAttr(resourceName, "excluded_accounts"), - testAccCheckConfigOrganizationConformancePackSuccessful(resourceName), + resource.TestCheckResourceAttr(resourceName, "input_parameters.#", "0"), + resource.TestCheckResourceAttr(resourceName, "excluded_accounts.#", "0"), ), }, { @@ -51,12 +51,12 @@ func testAccConfigOrganizationConformancePack_disappears(t *testing.T) { resourceName := "aws_config_organization_conformance_pack.test" resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccOrganizationsMasterPreCheck(t) }, + PreCheck: func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, Steps: []resource.TestStep{ { - Config: testAccConfigOrganizationConformancePackConfigRuleIdentifier(rName, "IAM_PASSWORD_POLICY"), + Config: testAccConfigOrganizationConformancePackBasicConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckConfigOrganizationConformancePackExists(resourceName, &pack), testAccCheckResourceDisappears(testAccProvider, resourceAwsConfigOrganizationConformancePack(), resourceName), @@ -67,30 +67,114 @@ func testAccConfigOrganizationConformancePack_disappears(t *testing.T) { }) } -func testAccConfigOrganizationConformancePack_InputParameters(t *testing.T) { +func testAccConfigOrganizationConformancePack_excludedAccounts(t *testing.T) { var pack configservice.OrganizationConformancePack rName := acctest.RandomWithPrefix("tf-acc-test") - rId := "IAM_PASSWORD_POLICY" - pKey := "ParamKey" - pValue := "ParamValue" resourceName := "aws_config_organization_conformance_pack.test" resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccOrganizationsMasterPreCheck(t) }, + PreCheck: func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, Steps: []resource.TestStep{ { - Config: testAccConfigOrganizationConformancePackConfigRuleIdentifierParameter(rName, rId, pKey, pValue), + Config: testAccConfigOrganizationConformancePackExcludedAccounts1Config(rName), Check: resource.ComposeTestCheckFunc( testAccCheckConfigOrganizationConformancePackExists(resourceName, &pack), - testAccMatchResourceAttrRegionalARN(resourceName, "arn", "config", regexp.MustCompile(fmt.Sprintf("organization-conformance-pack/%s-.+", rName))), - resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "excluded_accounts.#", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"template_body"}, + }, + { + Config: testAccConfigOrganizationConformancePackExcludedAccounts2Config(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigOrganizationConformancePackExists(resourceName, &pack), + resource.TestCheckResourceAttr(resourceName, "excluded_accounts.#", "2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"template_body"}, + }, + { + Config: testAccConfigOrganizationConformancePackBasicConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigOrganizationConformancePackExists(resourceName, &pack), + resource.TestCheckResourceAttr(resourceName, "excluded_accounts.#", "0"), + ), + }, + }, + }) +} + +func testAccConfigOrganizationConformancePack_forceNew(t *testing.T) { + var before, after configservice.OrganizationConformancePack + rName := acctest.RandomWithPrefix("tf-acc-test") + rNameUpdated := acctest.RandomWithPrefix("tf-acc-test-update") + resourceName := "aws_config_organization_conformance_pack.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, + Steps: []resource.TestStep{ + { + Config: testAccConfigOrganizationConformancePackBasicConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigOrganizationConformancePackExists(resourceName, &before), + ), + }, + { + Config: testAccConfigOrganizationConformancePackBasicConfig(rNameUpdated), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigOrganizationConformancePackExists(resourceName, &after), + testAccCheckConfigOrganizationConformancePackRecreated(&before, &after), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "config", regexp.MustCompile(fmt.Sprintf("organization-conformance-pack/%s-.+", rNameUpdated))), + resource.TestCheckResourceAttr(resourceName, "name", rNameUpdated), resource.TestCheckResourceAttr(resourceName, "delivery_s3_bucket", ""), resource.TestCheckResourceAttr(resourceName, "delivery_s3_key_prefix", ""), - resource.TestCheckResourceAttr(resourceName, "input_parameters."+pKey, pValue), - resource.TestCheckNoResourceAttr(resourceName, "excluded_accounts"), - testAccCheckConfigOrganizationConformancePackSuccessful(resourceName), + resource.TestCheckResourceAttr(resourceName, "input_parameter.#", "0"), + resource.TestCheckResourceAttr(resourceName, "excluded_accounts.#", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"template_body"}, + }, + }, + }) +} + +func testAccConfigOrganizationConformancePack_inputParameters(t *testing.T) { + var pack configservice.OrganizationConformancePack + rName := acctest.RandomWithPrefix("tf-acc-test") + pKey := "ParamKey" + pValue := "ParamValue" + resourceName := "aws_config_organization_conformance_pack.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, + Steps: []resource.TestStep{ + { + Config: testAccConfigOrganizationConformancePackInputParameterConfig(rName, pKey, pValue), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigOrganizationConformancePackExists(resourceName, &pack), + resource.TestCheckResourceAttr(resourceName, "input_parameters.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "input_parameters.*", map[string]string{ + "parameter_name": pKey, + "parameter_value": pValue, + }), ), }, { @@ -106,26 +190,20 @@ func testAccConfigOrganizationConformancePack_InputParameters(t *testing.T) { func testAccConfigOrganizationConformancePack_S3Delivery(t *testing.T) { var pack configservice.OrganizationConformancePack rName := acctest.RandomWithPrefix("tf-acc-test") - bName := "awsconfigconforms" + rName - rId := "IAM_PASSWORD_POLICY" + bucketName := acctest.RandomWithPrefix("awsconfigconforms") resourceName := "aws_config_organization_conformance_pack.test" resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccOrganizationsMasterPreCheck(t) }, + PreCheck: func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, Steps: []resource.TestStep{ { - Config: testAccConfigOrganizationConformancePackConfigRuleIdentifierS3Delivery(rName, rId, bName), + Config: testAccConfigOrganizationConformancePackS3DeliveryConfig(rName, bucketName), Check: resource.ComposeTestCheckFunc( testAccCheckConfigOrganizationConformancePackExists(resourceName, &pack), - testAccMatchResourceAttrRegionalARN(resourceName, "arn", "config", regexp.MustCompile(fmt.Sprintf("organization-conformance-pack/%s-.+", rName))), - resource.TestCheckResourceAttr(resourceName, "name", rName), - resource.TestCheckResourceAttr(resourceName, "delivery_s3_bucket", bName), - resource.TestCheckResourceAttr(resourceName, "delivery_s3_key_prefix", rId), - resource.TestCheckNoResourceAttr(resourceName, "input_parameters"), - resource.TestCheckNoResourceAttr(resourceName, "excluded_accounts"), - testAccCheckConfigOrganizationConformancePackSuccessful(resourceName), + resource.TestCheckResourceAttr(resourceName, "delivery_s3_bucket", bucketName), + resource.TestCheckResourceAttr(resourceName, "delivery_s3_key_prefix", rName), ), }, { @@ -141,27 +219,23 @@ func testAccConfigOrganizationConformancePack_S3Delivery(t *testing.T) { func testAccConfigOrganizationConformancePack_S3Template(t *testing.T) { var pack configservice.OrganizationConformancePack rName := acctest.RandomWithPrefix("tf-acc-test") - bName := rName - kName := rName + ".yaml" - rId := "IAM_PASSWORD_POLICY" resourceName := "aws_config_organization_conformance_pack.test" resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccOrganizationsMasterPreCheck(t) }, + PreCheck: func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, Steps: []resource.TestStep{ { - Config: testAccConfigOrganizationConformancePackConfigRuleIdentifierS3Template(rName, rId, bName, kName), + Config: testAccConfigOrganizationConformancePackS3TemplateConfig(rName, rName), Check: resource.ComposeTestCheckFunc( testAccCheckConfigOrganizationConformancePackExists(resourceName, &pack), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "config", regexp.MustCompile(fmt.Sprintf("organization-conformance-pack/%s-.+", rName))), resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "delivery_s3_bucket", ""), resource.TestCheckResourceAttr(resourceName, "delivery_s3_key_prefix", ""), - resource.TestCheckNoResourceAttr(resourceName, "input_parameters"), - resource.TestCheckNoResourceAttr(resourceName, "excluded_accounts"), - testAccCheckConfigOrganizationConformancePackSuccessful(resourceName), + resource.TestCheckResourceAttr(resourceName, "input_parameters.#", "0"), + resource.TestCheckResourceAttr(resourceName, "excluded_accounts.#", "0"), ), }, { @@ -174,35 +248,145 @@ func testAccConfigOrganizationConformancePack_S3Template(t *testing.T) { }) } -func testAccConfigOrganizationConformancePack_ExcludedAccounts(t *testing.T) { +func testAccConfigOrganizationConformancePack_updateInputParameters(t *testing.T) { var pack configservice.OrganizationConformancePack rName := acctest.RandomWithPrefix("tf-acc-test") - rId := "IAM_PASSWORD_POLICY" resourceName := "aws_config_organization_conformance_pack.test" resource.Test(t, resource.TestCase{ - PreCheck: func() { - testAccPreCheck(t) - testAccOrganizationsMasterPreCheck(t) - testAccOrganizationsMinAccountsPreCheck(t, 2) - // TODO: All accounts in the organization must also have configuration recorders in the current region, - // which is a little complicated for a precheck. If you get an 'unexpected state' error with this - // test, try enabling configuration recorders across the org. + PreCheck: func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, + Steps: []resource.TestStep{ + { + Config: testAccConfigOrganizationConformancePackInputParameterConfig(rName, "TestKey", "TestValue"), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigOrganizationConformancePackExists(resourceName, &pack), + ), + }, + { + Config: testAccConfigOrganizationConformancePackUpdateInputParameterConfig(rName, "TestKey1", "TestKey2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigOrganizationConformancePackExists(resourceName, &pack), + resource.TestCheckResourceAttr(resourceName, "input_parameter.#", "2"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "input_parameter.*", map[string]string{ + "parameter_name": "TestKey1", + "parameter_value": "TestValue1", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "input_parameter.*", map[string]string{ + "parameter_name": "TestKey2", + "parameter_value": "TestValue2", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"template_body"}, + }, + { + Config: testAccConfigOrganizationConformancePackBasicConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigOrganizationConformancePackExists(resourceName, &pack), + resource.TestCheckResourceAttr(resourceName, "input_parameter.#", "0"), + ), + }, }, + }) +} + +func testAccConfigOrganizationConformancePack_updateS3Delivery(t *testing.T) { + var pack configservice.OrganizationConformancePack + rName := acctest.RandomWithPrefix("tf-acc-test") + bucketName := acctest.RandomWithPrefix("awsconfigconforms") + updatedBucketName := fmt.Sprintf("%s-update", bucketName) + resourceName := "aws_config_organization_conformance_pack.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, Steps: []resource.TestStep{ { - Config: testAccConfigOrganizationConformancePackConfigRuleIdentifierExcludedAccounts(rName, rId), + Config: testAccConfigOrganizationConformancePackS3DeliveryConfig(rName, bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigOrganizationConformancePackExists(resourceName, &pack), + resource.TestCheckResourceAttr(resourceName, "delivery_s3_bucket", bucketName), + resource.TestCheckResourceAttr(resourceName, "delivery_s3_key_prefix", rName), + ), + }, + { + Config: testAccConfigOrganizationConformancePackS3DeliveryConfig(rName, updatedBucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigOrganizationConformancePackExists(resourceName, &pack), + resource.TestCheckResourceAttr(resourceName, "delivery_s3_bucket", updatedBucketName), + resource.TestCheckResourceAttr(resourceName, "delivery_s3_key_prefix", rName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"template_body"}, + }, + }, + }) +} + +func testAccConfigOrganizationConformancePack_updateS3Template(t *testing.T) { + var pack configservice.OrganizationConformancePack + rName := acctest.RandomWithPrefix("tf-acc-test") + bucketName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_config_organization_conformance_pack.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, + Steps: []resource.TestStep{ + { + Config: testAccConfigOrganizationConformancePackS3TemplateConfig(rName, rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigOrganizationConformancePackExists(resourceName, &pack), + ), + }, + { + Config: testAccConfigOrganizationConformancePackS3TemplateConfig(rName, bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigOrganizationConformancePackExists(resourceName, &pack), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"template_s3_uri"}, + }, + }, + }) +} + +func testAccConfigOrganizationConformancePack_updateTemplateBody(t *testing.T) { + var pack configservice.OrganizationConformancePack + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_config_organization_conformance_pack.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, + Steps: []resource.TestStep{ + { + Config: testAccConfigOrganizationConformancePackBasicConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigOrganizationConformancePackExists(resourceName, &pack), + ), + }, + { + Config: testAccConfigOrganizationConformancePackUpdateConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckConfigOrganizationConformancePackExists(resourceName, &pack), - testAccMatchResourceAttrRegionalARN(resourceName, "arn", "config", regexp.MustCompile(fmt.Sprintf("organization-conformance-pack/%s-.+", rName))), - resource.TestCheckResourceAttr(resourceName, "name", rName), - resource.TestCheckResourceAttr(resourceName, "delivery_s3_bucket", ""), - resource.TestCheckResourceAttr(resourceName, "delivery_s3_key_prefix", ""), - resource.TestCheckNoResourceAttr(resourceName, "input_parameters"), - resource.TestCheckResourceAttr(resourceName, "excluded_accounts.#", "1"), - testAccCheckConfigOrganizationConformancePackSuccessful(resourceName), ), }, { @@ -223,25 +407,25 @@ func testAccCheckConfigOrganizationConformancePackDestroy(s *terraform.State) er continue } - rule, err := configDescribeOrganizationConformancePack(conn, rs.Primary.ID) + pack, err := configDescribeOrganizationConformancePack(conn, rs.Primary.ID) - if isAWSErr(err, configservice.ErrCodeNoSuchOrganizationConformancePackException, "") { + if tfawserr.ErrCodeEquals(err, configservice.ErrCodeNoSuchOrganizationConformancePackException) { continue } if err != nil { - return fmt.Errorf("error describing Config Organization Managed Rule (%s): %s", rs.Primary.ID, err) + return fmt.Errorf("error describing Config Organization Conformance Pack (%s): %w", rs.Primary.ID, err) } - if rule != nil { - return fmt.Errorf("Config Organization Managed Rule (%s) still exists", rs.Primary.ID) + if pack != nil { + return fmt.Errorf("Config Organization Conformance Pack (%s) still exists", rs.Primary.ID) } } return nil } -func testAccCheckConfigOrganizationConformancePackExists(resourceName string, ocr *configservice.OrganizationConformancePack) resource.TestCheckFunc { +func testAccCheckConfigOrganizationConformancePackExists(resourceName string, ocp *configservice.OrganizationConformancePack) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] if !ok { @@ -253,61 +437,31 @@ func testAccCheckConfigOrganizationConformancePackExists(resourceName string, oc pack, err := configDescribeOrganizationConformancePack(conn, rs.Primary.ID) if err != nil { - return fmt.Errorf("error describing organization conformance pack (%s): %s", rs.Primary.ID, err) + return fmt.Errorf("error describing Config Organization Conformance Pack (%s): %w", rs.Primary.ID, err) } if pack == nil { - return fmt.Errorf("organization conformance pack (%s) not found", rs.Primary.ID) + return fmt.Errorf("Config Organization Conformance Pack (%s) not found", rs.Primary.ID) } - *ocr = *pack + *ocp = *pack return nil } } -func testAccCheckConfigOrganizationConformancePackSuccessful(resourceName string) resource.TestCheckFunc { +func testAccCheckConfigOrganizationConformancePackRecreated(before, after *configservice.OrganizationConformancePack) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] - if !ok { - return fmt.Errorf("Not Found: %s", resourceName) - } - - conn := testAccProvider.Meta().(*AWSClient).configconn - - packStatus, err := configDescribeOrganizationConformancePackStatus(conn, rs.Primary.ID) - if err != nil { - return fmt.Errorf("error describing organization conformance pack status (%s): %s", rs.Primary.ID, err) + if aws.StringValue(before.OrganizationConformancePackArn) == aws.StringValue(after.OrganizationConformancePackArn) { + return fmt.Errorf("AWS Config Organization Conformance Pack was not recreated") } - if packStatus == nil { - return fmt.Errorf("organization conformance pack status (%s) not found", rs.Primary.ID) - } - if *packStatus.Status != configservice.OrganizationResourceStatusCreateSuccessful { - return fmt.Errorf("organization conformance pack (%s) returned %s status (%s): %s", rs.Primary.ID, *packStatus.Status, *packStatus.ErrorCode, *packStatus.ErrorMessage) - } - - detailedStatus, err := configDescribeOrganizationConformancePackDetailedStatus(conn, rs.Primary.ID) - if err != nil { - return fmt.Errorf("error describing organization conformance pack detailed status (%s): %s", rs.Primary.ID, err) - } - if detailedStatus == nil { - return fmt.Errorf("organization conformance pack detailed status (%s) not found", rs.Primary.ID) - } - for _, s := range detailedStatus { - if *s.Status != configservice.OrganizationResourceDetailedStatusCreateSuccessful { - return fmt.Errorf("organization conformance pack (%s) on account %s returned %s status (%s): %s", rs.Primary.ID, *s.Status, *s.AccountId, *s.ErrorCode, *s.ErrorMessage) - } - } - return nil } } func testAccConfigOrganizationConformancePackBase(rName string) string { return fmt.Sprintf(` - -data "aws_partition" "current" { -} +data "aws_partition" "current" {} resource "aws_config_configuration_recorder" "test" { depends_on = [aws_iam_role_policy_attachment.test] @@ -339,15 +493,19 @@ resource "aws_iam_role_policy_attachment" "test" { role = aws_iam_role.test.name } +resource "aws_organizations_organization" "test" { + aws_service_access_principals = ["config-multiaccountsetup.${data.aws_partition.current.dns_suffix}"] + feature_set = "ALL" +} `, rName) } -func testAccConfigOrganizationConformancePackConfigRuleIdentifier(rName, ruleIdentifier string) string { - return fmt.Sprintf(` -%[3]s - +func testAccConfigOrganizationConformancePackBasicConfig(rName string) string { + return composeConfig( + testAccConfigOrganizationConformancePackBase(rName), + fmt.Sprintf(` resource "aws_config_organization_conformance_pack" "test" { - depends_on = [aws_config_configuration_recorder.test] + depends_on = [aws_config_configuration_recorder.test, aws_organizations_organization.test] name = %[1]q template_body = < **NOTE:** This resource must be created in the Organization master account or a delegate administrator account and rules will include the master account unless its ID is added to the `excluded_accounts` argument. +~> **NOTE:** This resource must be created in the Organization master account or a delegated administrator account, and the Organization must have all features enabled. Every Organization account except those configured in the `excluded_accounts` argument must have a Configuration Recorder with proper IAM permissions before the Organization Conformance Pack will successfully create or update. See also the [`aws_config_configuration_recorder` resource](/docs/providers/aws/r/config_configuration_recorder.html). -~> **NOTE:** Every Organization account except those configured in the `excluded_accounts` argument must have a Configuration Recorder with proper IAM permissions before the rule will successfully create or update. See also the [`aws_config_configuration_recorder` resource](/docs/providers/aws/r/config_configuration_recorder.html). - -## Example Usage +## Example Usage with Template Body ```hcl -resource "aws_config_organization_conformance_pack" "test" { - name = "example" +resource "aws_config_organization_conformance_pack" "example" { + name = "example" + + input_parameter { + parameter_name = "AccessKeysRotatedParameterMaxAccessKeyAge" + parameter_value = "90" + } + template_body = < Date: Fri, 16 Apr 2021 01:34:37 -0400 Subject: [PATCH 020/398] additional exception handling --- aws/config.go | 14 +++++++++++++ aws/configservice.go | 11 ++++------ ...ws_config_organization_conformance_pack.go | 20 ++----------------- 3 files changed, 20 insertions(+), 25 deletions(-) diff --git a/aws/config.go b/aws/config.go index 6ccc48f1142..f9111cfd21a 100644 --- a/aws/config.go +++ b/aws/config.go @@ -705,6 +705,20 @@ func (c *Config) Client() (interface{}, error) { return } + // We only want to retry briefly as the default max retry count would + // excessively retry when the error could be legitimate. + // We currently depend on the DefaultRetryer exponential backoff here. + // ~10 retries gives a fair backoff of a few seconds. + if r.RetryCount < 9 { + r.Retryable = aws.Bool(true) + } else { + r.Retryable = aws.Bool(false) + } + case "PutOrganizationConformancePack", "DeleteOrganizationConformancePack", "DescribeOrganizationConformancePackStatuses": + if !tfawserr.ErrCodeEquals(r.Error, configservice.ErrCodeOrganizationAccessDeniedException) { + return + } + // We only want to retry briefly as the default max retry count would // excessively retry when the error could be legitimate. // We currently depend on the DefaultRetryer exponential backoff here. diff --git a/aws/configservice.go b/aws/configservice.go index e03ba2e6966..124a5728c9a 100644 --- a/aws/configservice.go +++ b/aws/configservice.go @@ -21,9 +21,6 @@ const ( ConfigConformancePackStatusNotFound = "NotFound" ConfigConformancePackStatusUnknown = "Unknown" - - ConfigOrganizationConformancePackStatusNotFound = "NotFound" - ConfigOrganizationConformancePackStatusUnknown = "Unknown" ) func configDescribeConformancePack(conn *configservice.ConfigService, name string) (*configservice.ConformancePackDetail, error) { @@ -318,11 +315,11 @@ func configRefreshOrganizationConformancePackStatus(conn *configservice.ConfigSe status, err := configDescribeOrganizationConformancePackStatus(conn, name) if err != nil { - return nil, ConfigOrganizationConformancePackStatusUnknown, err + return nil, "", err } if status == nil { - return nil, ConfigOrganizationConformancePackStatusNotFound, nil + return nil, "", nil } if status.ErrorCode != nil { @@ -388,8 +385,8 @@ func configWaitForOrganizationConformancePackStatusCreateSuccessful(conn *config Target: []string{configservice.OrganizationResourceStatusCreateSuccessful}, Timeout: ConfigOrganizationConformancePackCreateTimeout, Refresh: configRefreshOrganizationConformancePackStatus(conn, name), - // Include a Delay to avoid transient error i.e. OrganizationAccessDeniedException - Delay: 1 * time.Minute, + // Include a Delay to avoid transient error + Delay: 30 * time.Second, } _, err := stateChangeConf.WaitForState() diff --git a/aws/resource_aws_config_organization_conformance_pack.go b/aws/resource_aws_config_organization_conformance_pack.go index 356423e34b2..7f98d636f95 100644 --- a/aws/resource_aws_config_organization_conformance_pack.go +++ b/aws/resource_aws_config_organization_conformance_pack.go @@ -134,24 +134,7 @@ func resourceAwsConfigOrganizationConformancePackCreate(d *schema.ResourceData, input.TemplateS3Uri = aws.String(v.(string)) } - err := resource.Retry(ConfigOrganizationConformancePackCreateTimeout, func() *resource.RetryError { - _, err := conn.PutOrganizationConformancePack(&input) - - if err != nil { - // OrganizationAccessDeniedException seems to be a transient error - if tfawserr.ErrCodeEquals(err, configservice.ErrCodeOrganizationAccessDeniedException) { - return resource.RetryableError(err) - } - - return resource.NonRetryableError(err) - } - - return nil - }) - - if tfresource.TimedOut(err) { - _, err = conn.PutOrganizationConformancePack(&input) - } + _, err := conn.PutOrganizationConformancePack(&input) if err != nil { return fmt.Errorf("error creating Config Organization Conformance Pack (%s): %w", name, err) @@ -239,6 +222,7 @@ func resourceAwsConfigOrganizationConformancePackUpdate(d *schema.ResourceData, } _, err := conn.PutOrganizationConformancePack(&input) + if err != nil { return fmt.Errorf("error updating Config Organization Conformance Pack (%s): %w", d.Id(), err) } From 23adde265773d1e624e10c02b252e2c12e92622e Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Fri, 16 Apr 2021 02:02:49 -0400 Subject: [PATCH 021/398] add test ErrorChecks; docs --- ...e_aws_config_organization_conformance_pack_test.go | 11 +++++++++++ ...config_organization_conformance_pack.html.markdown | 8 +++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/aws/resource_aws_config_organization_conformance_pack_test.go b/aws/resource_aws_config_organization_conformance_pack_test.go index ed86946725a..d4ac03ea8d7 100644 --- a/aws/resource_aws_config_organization_conformance_pack_test.go +++ b/aws/resource_aws_config_organization_conformance_pack_test.go @@ -20,6 +20,7 @@ func testAccConfigOrganizationConformancePack_basic(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, configservice.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, Steps: []resource.TestStep{ @@ -52,6 +53,7 @@ func testAccConfigOrganizationConformancePack_disappears(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, configservice.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, Steps: []resource.TestStep{ @@ -74,6 +76,7 @@ func testAccConfigOrganizationConformancePack_excludedAccounts(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, configservice.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, Steps: []resource.TestStep{ @@ -122,6 +125,7 @@ func testAccConfigOrganizationConformancePack_forceNew(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, configservice.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, Steps: []resource.TestStep{ @@ -163,6 +167,7 @@ func testAccConfigOrganizationConformancePack_inputParameters(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, configservice.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, Steps: []resource.TestStep{ @@ -195,6 +200,7 @@ func testAccConfigOrganizationConformancePack_S3Delivery(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, configservice.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, Steps: []resource.TestStep{ @@ -223,6 +229,7 @@ func testAccConfigOrganizationConformancePack_S3Template(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, configservice.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, Steps: []resource.TestStep{ @@ -255,6 +262,7 @@ func testAccConfigOrganizationConformancePack_updateInputParameters(t *testing.T resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, configservice.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, Steps: []resource.TestStep{ @@ -305,6 +313,7 @@ func testAccConfigOrganizationConformancePack_updateS3Delivery(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, configservice.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, Steps: []resource.TestStep{ @@ -342,6 +351,7 @@ func testAccConfigOrganizationConformancePack_updateS3Template(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, configservice.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, Steps: []resource.TestStep{ @@ -374,6 +384,7 @@ func testAccConfigOrganizationConformancePack_updateTemplateBody(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, configservice.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckConfigOrganizationConformancePackDestroy, Steps: []resource.TestStep{ diff --git a/website/docs/r/config_organization_conformance_pack.html.markdown b/website/docs/r/config_organization_conformance_pack.html.markdown index 843f793b53b..b3798996558 100644 --- a/website/docs/r/config_organization_conformance_pack.html.markdown +++ b/website/docs/r/config_organization_conformance_pack.html.markdown @@ -12,7 +12,9 @@ Manages a Config Organization Conformance Pack. More information can be found in ~> **NOTE:** This resource must be created in the Organization master account or a delegated administrator account, and the Organization must have all features enabled. Every Organization account except those configured in the `excluded_accounts` argument must have a Configuration Recorder with proper IAM permissions before the Organization Conformance Pack will successfully create or update. See also the [`aws_config_configuration_recorder` resource](/docs/providers/aws/r/config_configuration_recorder.html). -## Example Usage with Template Body +## Example Usage + +### Using Template Body ```hcl resource "aws_config_organization_conformance_pack" "example" { @@ -46,7 +48,7 @@ resource "aws_organizations_organization" "example" { } ``` -## Example Usage with Template S3 URI +### Using Template S3 URI ```hcl resource "aws_config_organization_conformance_pack" "example" { @@ -102,7 +104,7 @@ The `input_parameter` configuration block supports the following arguments: ## Attributes Reference -In addition to all arguments above (except for `template_body` and `template_s3_uri`), the following attributes are exported: +In addition to all arguments above, the following attributes are exported: * `arn` - Amazon Resource Name (ARN) of the organization conformance pack. From 6f86f40a931bf14bc15363fa3cfe259dbc228f23 Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Mon, 10 May 2021 21:20:24 +0900 Subject: [PATCH 022/398] feat: add a resource aws_appconfig_environment --- aws/provider.go | 1 + aws/resource_aws_appconfig_environment.go | 167 ++++++++++++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 aws/resource_aws_appconfig_environment.go diff --git a/aws/provider.go b/aws/provider.go index 7d7c14c9309..2d574e62657 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -457,6 +457,7 @@ func Provider() *schema.Provider { "aws_appautoscaling_policy": resourceAwsAppautoscalingPolicy(), "aws_appautoscaling_scheduled_action": resourceAwsAppautoscalingScheduledAction(), "aws_appconfig_application": resourceAwsAppconfigApplication(), + "aws_appconfig_environment": resourceAwsAppconfigEnvironment(), "aws_appmesh_gateway_route": resourceAwsAppmeshGatewayRoute(), "aws_appmesh_mesh": resourceAwsAppmeshMesh(), "aws_appmesh_route": resourceAwsAppmeshRoute(), diff --git a/aws/resource_aws_appconfig_environment.go b/aws/resource_aws_appconfig_environment.go new file mode 100644 index 00000000000..bce9b087488 --- /dev/null +++ b/aws/resource_aws_appconfig_environment.go @@ -0,0 +1,167 @@ +package aws + +import ( + "fmt" + "log" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/appconfig" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceAwsAppconfigEnvironment() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsAppconfigEnvironmentCreate, + Read: resourceAwsAppconfigEnvironmentRead, + Update: resourceAwsAppconfigEnvironmentUpdate, + Delete: resourceAwsAppconfigEnvironmentDelete, + Importer: &schema.ResourceImporter{ + State: resourceAwsAppconfigEnvironmentImport, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 64), + ), + }, + "application_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(4, 7), + ), + }, + "description": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.All( + validation.StringLenBetween(0, 1024), + ), + }, + // TODO monitors + "tags": tagsSchema(), + }, + } +} + +func resourceAwsAppconfigEnvironmentCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).appconfigconn + + input := &appconfig.CreateEnvironmentInput{ + Name: aws.String(d.Get("name").(string)), + Description: aws.String(d.Get("description").(string)), + ApplicationId: aws.String(d.Get("application_id").(string)), + // TODO tags + // TODO monitors + } + + environment, err := conn.CreateEnvironment(input) + if err != nil { + return fmt.Errorf("Error creating AppConfig Environment: %s", err) + } + + d.SetId(aws.StringValue(environment.Id)) + + return resourceAwsAppconfigEnvironmentRead(d, meta) +} + +func resourceAwsAppconfigEnvironmentRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).appconfigconn + + input := &appconfig.GetEnvironmentInput{ + ApplicationId: aws.String(d.Get("application_id").(string)), + EnvironmentId: aws.String(d.Id()), + } + + output, err := conn.GetEnvironment(input) + + if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + log.Printf("[WARN] Appconfig Environment (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error getting AppConfig Environment (%s): %s", d.Id(), err) + } + + if output == nil { + return fmt.Errorf("error getting AppConfig Environment (%s): empty response", d.Id()) + } + + d.Set("name", output.Name) + d.Set("description", output.Description) + d.Set("application_id", output.ApplicationId) + // TODO tags + // TODO monitors + + return nil +} + +func resourceAwsAppconfigEnvironmentUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).appconfigconn + + updateInput := &appconfig.UpdateEnvironmentInput{ + EnvironmentId: aws.String(d.Id()), + ApplicationId: aws.String(d.Get("application_id").(string)), + } + + if d.HasChange("description") { + _, n := d.GetChange("description") + updateInput.Description = aws.String(n.(string)) + } + + if d.HasChange("name") { + _, n := d.GetChange("name") + updateInput.Name = aws.String(n.(string)) + } + + // TODO tags + // TODO monitors + + _, err := conn.UpdateEnvironment(updateInput) + if err != nil { + return fmt.Errorf("error updating AppConfig Environment(%s): %s", d.Id(), err) + } + + return resourceAwsAppconfigEnvironmentRead(d, meta) +} + +func resourceAwsAppconfigEnvironmentDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).appconfigconn + + input := &appconfig.DeleteEnvironmentInput{ + EnvironmentId: aws.String(d.Id()), + ApplicationId: aws.String(d.Get("application_id").(string)), + } + + _, err := conn.DeleteEnvironment(input) + + if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + return nil + } + + if err != nil { + return fmt.Errorf("error deleting Appconfig Environment (%s): %s", d.Id(), err) + } + + return nil +} + +func resourceAwsAppconfigEnvironmentImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + parts := strings.Split(d.Id(), "/") + if len(parts) != 2 { + return []*schema.ResourceData{}, fmt.Errorf("Wrong format of resource: %s. Please follow 'application-id/environment-id'", d.Id()) + } + + d.SetId(parts[1]) + d.Set("application_id", parts[0]) + + return []*schema.ResourceData{d}, nil +} From 542f15184dfb048004832a368057741709d9b58c Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Mon, 10 May 2021 22:34:52 +0900 Subject: [PATCH 023/398] feat: support tags of AppConfig Environment --- aws/resource_aws_appconfig_environment.go | 39 ++++++++++++++++++++--- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/aws/resource_aws_appconfig_environment.go b/aws/resource_aws_appconfig_environment.go index bce9b087488..ea757c151e3 100644 --- a/aws/resource_aws_appconfig_environment.go +++ b/aws/resource_aws_appconfig_environment.go @@ -6,9 +6,11 @@ import ( "strings" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/appconfig" "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" ) func resourceAwsAppconfigEnvironment() *schema.Resource { @@ -46,6 +48,10 @@ func resourceAwsAppconfigEnvironment() *schema.Resource { }, // TODO monitors "tags": tagsSchema(), + "arn": { + Type: schema.TypeString, + Computed: true, + }, }, } } @@ -57,7 +63,7 @@ func resourceAwsAppconfigEnvironmentCreate(d *schema.ResourceData, meta interfac Name: aws.String(d.Get("name").(string)), Description: aws.String(d.Get("description").(string)), ApplicationId: aws.String(d.Get("application_id").(string)), - // TODO tags + Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().AppconfigTags(), // TODO monitors } @@ -73,9 +79,12 @@ func resourceAwsAppconfigEnvironmentCreate(d *schema.ResourceData, meta interfac func resourceAwsAppconfigEnvironmentRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).appconfigconn + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + + appID := d.Get("application_id").(string) input := &appconfig.GetEnvironmentInput{ - ApplicationId: aws.String(d.Get("application_id").(string)), + ApplicationId: aws.String(appID), EnvironmentId: aws.String(d.Id()), } @@ -98,9 +107,26 @@ func resourceAwsAppconfigEnvironmentRead(d *schema.ResourceData, meta interface{ d.Set("name", output.Name) d.Set("description", output.Description) d.Set("application_id", output.ApplicationId) - // TODO tags // TODO monitors + environmentARN := arn.ARN{ + AccountID: meta.(*AWSClient).accountid, + Partition: meta.(*AWSClient).partition, + Region: meta.(*AWSClient).region, + Resource: fmt.Sprintf("application/%s/environment/%s", appID, d.Id()), + Service: "appconfig", + }.String() + d.Set("arn", environmentARN) + + tags, err := keyvaluetags.AppconfigListTags(conn, environmentARN) + if err != nil { + return fmt.Errorf("error getting tags for AppConfig Environment (%s): %s", d.Id(), err) + } + + if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting tags: %s", err) + } + return nil } @@ -122,7 +148,12 @@ func resourceAwsAppconfigEnvironmentUpdate(d *schema.ResourceData, meta interfac updateInput.Name = aws.String(n.(string)) } - // TODO tags + if d.HasChange("tags") { + o, n := d.GetChange("tags") + if err := keyvaluetags.AppconfigUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return fmt.Errorf("error updating AppConfig (%s) tags: %s", d.Id(), err) + } + } // TODO monitors _, err := conn.UpdateEnvironment(updateInput) From 7f31e81c2828a4be53a0264616e20ca9fc1107e5 Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Mon, 10 May 2021 23:14:56 +0900 Subject: [PATCH 024/398] feat: support monitors of AppConfig Environment --- aws/resource_aws_appconfig_environment.go | 62 ++++++++++++++++++++--- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/aws/resource_aws_appconfig_environment.go b/aws/resource_aws_appconfig_environment.go index ea757c151e3..8ac430b4df6 100644 --- a/aws/resource_aws_appconfig_environment.go +++ b/aws/resource_aws_appconfig_environment.go @@ -46,12 +46,34 @@ func resourceAwsAppconfigEnvironment() *schema.Resource { validation.StringLenBetween(0, 1024), ), }, - // TODO monitors "tags": tagsSchema(), "arn": { Type: schema.TypeString, Computed: true, }, + "monitors": { + Type: schema.TypeList, + Optional: true, + MaxItems: 5, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "alarm_arn": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.All( + validation.StringLenBetween(20, 2048), + ), + }, + "alarm_role_arn": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.All( + validation.StringLenBetween(20, 2048), + ), + }, + }, + }, + }, }, } } @@ -64,7 +86,7 @@ func resourceAwsAppconfigEnvironmentCreate(d *schema.ResourceData, meta interfac Description: aws.String(d.Get("description").(string)), ApplicationId: aws.String(d.Get("application_id").(string)), Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().AppconfigTags(), - // TODO monitors + Monitors: expandAppconfigEnvironmentMonitors(d.Get("monitors").([]interface{})), } environment, err := conn.CreateEnvironment(input) @@ -77,6 +99,29 @@ func resourceAwsAppconfigEnvironmentCreate(d *schema.ResourceData, meta interfac return resourceAwsAppconfigEnvironmentRead(d, meta) } +func expandAppconfigEnvironmentMonitors(list []interface{}) []*appconfig.Monitor { + monitors := make([]*appconfig.Monitor, len(list)) + for i, monitorInterface := range list { + m := monitorInterface.(map[string]interface{}) + monitors[i] = &appconfig.Monitor{ + AlarmArn: aws.String(m["alarm_arn"].(string)), + AlarmRoleArn: aws.String(m["alarm_role_arn"].(string)), + } + } + return monitors +} + +func flattenAwsAppconfigEnvironmentMonitors(monitors []*appconfig.Monitor) []interface{} { + list := make([]interface{}, len(monitors)) + for i, monitor := range monitors { + list[i] = map[string]interface{}{ + "alarm_arn": aws.StringValue(monitor.AlarmArn), + "alarm_role_arn": aws.StringValue(monitor.AlarmRoleArn), + } + } + return list +} + func resourceAwsAppconfigEnvironmentRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).appconfigconn ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig @@ -107,7 +152,7 @@ func resourceAwsAppconfigEnvironmentRead(d *schema.ResourceData, meta interface{ d.Set("name", output.Name) d.Set("description", output.Description) d.Set("application_id", output.ApplicationId) - // TODO monitors + d.Set("monitors", flattenAwsAppconfigEnvironmentMonitors(output.Monitors)) environmentARN := arn.ARN{ AccountID: meta.(*AWSClient).accountid, @@ -139,13 +184,11 @@ func resourceAwsAppconfigEnvironmentUpdate(d *schema.ResourceData, meta interfac } if d.HasChange("description") { - _, n := d.GetChange("description") - updateInput.Description = aws.String(n.(string)) + updateInput.Description = aws.String(d.Get("description").(string)) } if d.HasChange("name") { - _, n := d.GetChange("name") - updateInput.Name = aws.String(n.(string)) + updateInput.Name = aws.String(d.Get("name").(string)) } if d.HasChange("tags") { @@ -154,7 +197,10 @@ func resourceAwsAppconfigEnvironmentUpdate(d *schema.ResourceData, meta interfac return fmt.Errorf("error updating AppConfig (%s) tags: %s", d.Id(), err) } } - // TODO monitors + + if d.HasChange("monitors") { + updateInput.Monitors = expandAppconfigEnvironmentMonitors(d.Get("monitors").([]interface{})) + } _, err := conn.UpdateEnvironment(updateInput) if err != nil { From 3b973d5915886f4132c1bd281ed4ba7a1cb0b08a Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Mon, 10 May 2021 23:31:33 +0900 Subject: [PATCH 025/398] docs: add document for aws_appconfig_environment --- .../r/appconfig_environment.html.markdown | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 website/docs/r/appconfig_environment.html.markdown diff --git a/website/docs/r/appconfig_environment.html.markdown b/website/docs/r/appconfig_environment.html.markdown new file mode 100644 index 00000000000..8b4b9135e80 --- /dev/null +++ b/website/docs/r/appconfig_environment.html.markdown @@ -0,0 +1,68 @@ +--- +subcategory: "AppConfig" +layout: "aws" +page_title: "AWS: aws_appconfig_environment" +description: |- + Provides an AppConfig Environment resource. +--- + +# Resource: aws_appconfig_environment + +Provides an AppConfig Environment resource. + +## Example Usage + +### AppConfig Environment + +```hcl +resource "aws_appconfig_environment" "production" { + name = "production" + description = "Production" + application_id = aws_appconfig_application.test.id + tags = { + Type = "Production" + } + monitors { + alarm_arn = "arn:aws:cloudwatch:us-east-1:111111111111:alarm:test-appconfig" + alarm_role_arn = "arn:aws:iam::111111111111:role/service-role/test-appconfig" + } +} + +resource "aws_appconfig_application" "test" { + name = "test" + description = "Test" + tags = { + Type = "Test" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +- `name` - (Required) The environment name. Must be between 1 and 64 characters in length. +- `application_id` - (Required) The application id. Must be between 4 and 7 characters in length. +- `description` - (Optional) The description of the environment. Can be at most 1024 characters. +- `monitors` - (Optional) Amazon CloudWatch alarms to monitor during the deployment process. Detailed below. +- `tags` - (Optional) A map of tags to assign to the resource. + +### monitor + +- `alarm_arn` - (Optional) ARN of the Amazon CloudWatch alarm. Must be between 20 and 2048 characters in length. +- `alarm_role_arn` - (Optional) ARN of an IAM role for AWS AppConfig to monitor AlarmArn. Must be between 20 and 2048 characters in length. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +- `arn` - The Amazon Resource Name (ARN) of the AppConfig Environment. +- `id` - The AppConfig Environment ID + +## Import + +`aws_appconfig_environment` can be imported by the Application ID and Environment ID, e.g. + +``` +$ terraform import aws_appconfig_environment.production 71abcde/11xxxxx +``` From 543c60c0dda07dba353bd870d12abd206a21baf1 Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Tue, 11 May 2021 08:14:06 +0900 Subject: [PATCH 026/398] test: add tests of aws_appconfig_environment --- ...resource_aws_appconfig_environment_test.go | 287 ++++++++++++++++++ 1 file changed, 287 insertions(+) create mode 100644 aws/resource_aws_appconfig_environment_test.go diff --git a/aws/resource_aws_appconfig_environment_test.go b/aws/resource_aws_appconfig_environment_test.go new file mode 100644 index 00000000000..17d2da94047 --- /dev/null +++ b/aws/resource_aws_appconfig_environment_test.go @@ -0,0 +1,287 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/appconfig" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccAWSAppConfigEnvironment_basic(t *testing.T) { + var environment appconfig.GetEnvironmentOutput + roleName := acctest.RandomWithPrefix("tf-acc-test") + alarmName := acctest.RandomWithPrefix("tf-acc-test") + appName := acctest.RandomWithPrefix("tf-acc-test") + appDesc := acctest.RandomWithPrefix("desc") + envName := acctest.RandomWithPrefix("tf-acc-test") + envDesc := acctest.RandomWithPrefix("desc") + resourceName := "aws_appconfig_environment.test" + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigEnvironmentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigEnvironmentWithMonitors(roleName, alarmName, appName, appDesc, envName, envDesc), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigEnvironmentExists(resourceName, &environment), + resource.TestCheckResourceAttr(resourceName, "name", envName), + testAccCheckAWSAppConfigEnvironmentARN(resourceName, &environment), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "description", envDesc), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSAppConfigEnvironment_disappears(t *testing.T) { + var environment appconfig.GetEnvironmentOutput + + appName := acctest.RandomWithPrefix("tf-acc-test") + appDesc := acctest.RandomWithPrefix("desc") + envName := acctest.RandomWithPrefix("tf-acc-test") + envDesc := acctest.RandomWithPrefix("desc") + resourceName := "aws_appconfig_application.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigEnvironmentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigEnvironment(appName, appDesc, envName, envDesc), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigEnvironmentExists(resourceName, &environment), + testAccCheckAWSAppConfigEnvironmentDisappears(&environment), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAWSAppConfigEnvironment_Tags(t *testing.T) { + var environment appconfig.GetEnvironmentOutput + + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_appconfig_environment.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigEnvironmentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigEnvironmentTags1(rName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigEnvironmentExists(resourceName, &environment), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSAppConfigEnvironmentTags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigEnvironmentExists(resourceName, &environment), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccAWSAppConfigEnvironmentTags1(rName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigEnvironmentExists(resourceName, &environment), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + }, + }) +} + +func testAccCheckAppConfigEnvironmentDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).appconfigconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_appconfig_environment" { + continue + } + + input := &appconfig.GetEnvironmentInput{ + ApplicationId: aws.String(rs.Primary.Attributes["application_id"]), + EnvironmentId: aws.String(rs.Primary.ID), + } + + output, err := conn.GetEnvironment(input) + + if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + continue + } + + if err != nil { + return err + } + + if output != nil { + return fmt.Errorf("AppConfig Environment (%s) still exists", rs.Primary.ID) + } + } + + return nil +} + +func testAccCheckAWSAppConfigEnvironmentDisappears(environment *appconfig.GetEnvironmentOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).appconfigconn + + _, err := conn.DeleteEnvironment(&appconfig.DeleteEnvironmentInput{ + ApplicationId: environment.ApplicationId, + EnvironmentId: environment.Id, + }) + + return err + } +} + +func testAccCheckAWSAppConfigEnvironmentExists(resourceName string, environment *appconfig.GetEnvironmentOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Resource not found: %s", resourceName) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("Resource (%s) ID not set", resourceName) + } + + conn := testAccProvider.Meta().(*AWSClient).appconfigconn + + output, err := conn.GetEnvironment(&appconfig.GetEnvironmentInput{ + ApplicationId: aws.String(rs.Primary.Attributes["application_id"]), + EnvironmentId: aws.String(rs.Primary.ID), + }) + if err != nil { + return err + } + + *environment = *output + + return nil + } +} + +func testAccCheckAWSAppConfigEnvironmentARN(resourceName string, environment *appconfig.GetEnvironmentOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + return testAccCheckResourceAttrRegionalARN(resourceName, "arn", "appconfig", fmt.Sprintf("application/%s/environment/%s", aws.StringValue(environment.ApplicationId), aws.StringValue(environment.Id)))(s) + } +} + +func testAccAWSAppConfigEnvironmentWithMonitors(roleName, alarmName, appName, appDesc, envName, envDesc string) string { + return testAccAWSAppConfigMonitor_ServiceRole(roleName) + testAccAWSCloudWatchMetricAlarmConfig(alarmName) + testAccAWSAppConfigApplicationName(appName, appDesc) + fmt.Sprintf(` +resource "aws_appconfig_environment" "test" { + name = %[1]q + description = %[2]q + application_id = aws_appconfig_application.test + + monitors { + alarm_arn = aws_cloudwatch_metric_alarm.test.arn + alarm_role_arn = aws_iam_role.test.arn + } +} +`, envName, envDesc) +} + +func testAccAWSAppConfigEnvironment(appName, appDesc, envName, envDesc string) string { + return testAccAWSAppConfigApplicationName(appName, appDesc) + fmt.Sprintf(` +resource "aws_appconfig_environment" "test" { + name = %[1]q + description = %[2]q + application_id = aws_appconfig_application.test +} +`, envName, envDesc) +} + +func testAccAWSAppConfigEnvironmentTags1(rName, tagKey1, tagValue1 string) string { + return testAccAWSAppConfigApplicationTags1(rName, tagKey1, tagValue1) + fmt.Sprintf(` +resource "aws_appconfig_environment" "test" { + name = %[1]q + application_id = aws_appconfig_application.test.id + + tags = { + %[2]q = %[3]q + } +} +`, rName, tagKey1, tagValue1) +} + +func testAccAWSAppConfigEnvironmentTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return testAccAWSAppConfigApplicationTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2) + fmt.Sprintf(` +resource "aws_appconfig_environment" "test" { + name = %[1]q + application_id = aws_appconfig_application.test.id + + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } +} +`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +} + +func testAccAWSAppConfigMonitor_ServiceRole(rName string) string { + return fmt.Sprintf(` +resource "aws_iam_role" "test" { + name = "%s" + + assume_role_policy = < Date: Tue, 11 May 2021 09:48:47 +0900 Subject: [PATCH 027/398] fix: fix tests --- aws/resource_aws_appconfig_environment_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/aws/resource_aws_appconfig_environment_test.go b/aws/resource_aws_appconfig_environment_test.go index 17d2da94047..a663c90ecfd 100644 --- a/aws/resource_aws_appconfig_environment_test.go +++ b/aws/resource_aws_appconfig_environment_test.go @@ -197,7 +197,7 @@ func testAccAWSAppConfigEnvironmentWithMonitors(roleName, alarmName, appName, ap resource "aws_appconfig_environment" "test" { name = %[1]q description = %[2]q - application_id = aws_appconfig_application.test + application_id = aws_appconfig_application.test.id monitors { alarm_arn = aws_cloudwatch_metric_alarm.test.arn @@ -212,7 +212,7 @@ func testAccAWSAppConfigEnvironment(appName, appDesc, envName, envDesc string) s resource "aws_appconfig_environment" "test" { name = %[1]q description = %[2]q - application_id = aws_appconfig_application.test + application_id = aws_appconfig_application.test.id } `, envName, envDesc) } @@ -221,7 +221,7 @@ func testAccAWSAppConfigEnvironmentTags1(rName, tagKey1, tagValue1 string) strin return testAccAWSAppConfigApplicationTags1(rName, tagKey1, tagValue1) + fmt.Sprintf(` resource "aws_appconfig_environment" "test" { name = %[1]q - application_id = aws_appconfig_application.test.id + application_id = aws_appconfig_application.test.id tags = { %[2]q = %[3]q @@ -234,7 +234,7 @@ func testAccAWSAppConfigEnvironmentTags2(rName, tagKey1, tagValue1, tagKey2, tag return testAccAWSAppConfigApplicationTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2) + fmt.Sprintf(` resource "aws_appconfig_environment" "test" { name = %[1]q - application_id = aws_appconfig_application.test.id + application_id = aws_appconfig_application.test.id tags = { %[2]q = %[3]q From 45d2f09168cfabe3553b0906523b961682ab5fb6 Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Tue, 11 May 2021 09:52:32 +0900 Subject: [PATCH 028/398] docs: add change log --- .changelog/19307.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/19307.txt diff --git a/.changelog/19307.txt b/.changelog/19307.txt new file mode 100644 index 00000000000..446c1c8af69 --- /dev/null +++ b/.changelog/19307.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_appconfig_environment +``` From fd94cb278e33924486b3c6f1c6efdf07742b6aff Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Tue, 11 May 2021 18:04:35 +0900 Subject: [PATCH 029/398] fix: specify ErrorCheck https://github.com/bflad/tfproviderlint/tree/main/xpasses/XAT001 https://github.com/hashicorp/terraform-provider-aws/pull/19307/checks?check_run_id=2551044817 --- aws/resource_aws_appconfig_application_test.go | 3 +++ aws/resource_aws_appconfig_environment_test.go | 3 +++ 2 files changed, 6 insertions(+) diff --git a/aws/resource_aws_appconfig_application_test.go b/aws/resource_aws_appconfig_application_test.go index 1721363742a..41ff1c82031 100644 --- a/aws/resource_aws_appconfig_application_test.go +++ b/aws/resource_aws_appconfig_application_test.go @@ -18,6 +18,7 @@ func TestAccAWSAppConfigApplication_basic(t *testing.T) { resourceName := "aws_appconfig_application.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckAppConfigApplicationDestroy, Steps: []resource.TestStep{ @@ -49,6 +50,7 @@ func TestAccAWSAppConfigApplication_disappears(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckAppConfigApplicationDestroy, Steps: []resource.TestStep{ @@ -72,6 +74,7 @@ func TestAccAWSAppConfigApplication_Tags(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckAppConfigApplicationDestroy, Steps: []resource.TestStep{ diff --git a/aws/resource_aws_appconfig_environment_test.go b/aws/resource_aws_appconfig_environment_test.go index a663c90ecfd..6e32abf0302 100644 --- a/aws/resource_aws_appconfig_environment_test.go +++ b/aws/resource_aws_appconfig_environment_test.go @@ -22,6 +22,7 @@ func TestAccAWSAppConfigEnvironment_basic(t *testing.T) { resourceName := "aws_appconfig_environment.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckAppConfigEnvironmentDestroy, Steps: []resource.TestStep{ @@ -55,6 +56,7 @@ func TestAccAWSAppConfigEnvironment_disappears(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckAppConfigEnvironmentDestroy, Steps: []resource.TestStep{ @@ -78,6 +80,7 @@ func TestAccAWSAppConfigEnvironment_Tags(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckAppConfigEnvironmentDestroy, Steps: []resource.TestStep{ From b47ccb62dfd46185ab4a7451ebd38d82d57c31df Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Tue, 11 May 2021 18:25:12 +0900 Subject: [PATCH 030/398] fix: check !d.IsNewResource() before d.SetId("") https://github.com/hashicorp/terraform-provider-aws/issues/16796 https://github.com/hashicorp/terraform-provider-aws/pull/19307/checks?check_run_id=2554016542 ``` Calling `d.SetId("")` should ensure `!d.IsNewResource()` is checked first ``` --- aws/resource_aws_appconfig_application.go | 2 +- aws/resource_aws_appconfig_environment.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_appconfig_application.go b/aws/resource_aws_appconfig_application.go index 743b609df61..adcd5c9a8de 100644 --- a/aws/resource_aws_appconfig_application.go +++ b/aws/resource_aws_appconfig_application.go @@ -81,7 +81,7 @@ func resourceAwsAppconfigApplicationRead(d *schema.ResourceData, meta interface{ output, err := conn.GetApplication(input) - if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + if !d.IsNewResource() && isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { log.Printf("[WARN] Appconfig Application (%s) not found, removing from state", d.Id()) d.SetId("") return nil diff --git a/aws/resource_aws_appconfig_environment.go b/aws/resource_aws_appconfig_environment.go index 8ac430b4df6..3a66831f269 100644 --- a/aws/resource_aws_appconfig_environment.go +++ b/aws/resource_aws_appconfig_environment.go @@ -135,7 +135,7 @@ func resourceAwsAppconfigEnvironmentRead(d *schema.ResourceData, meta interface{ output, err := conn.GetEnvironment(input) - if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + if !d.IsNewResource() && isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { log.Printf("[WARN] Appconfig Environment (%s) not found, removing from state", d.Id()) d.SetId("") return nil From 4eb7092aafb803ef6502f8ce5b068a02f7909617 Mon Sep 17 00:00:00 2001 From: Suzuki Shunsuke Date: Tue, 11 May 2021 20:46:14 +0900 Subject: [PATCH 031/398] test: specify ImportStateIdFunc --- aws/resource_aws_appconfig_environment_test.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/aws/resource_aws_appconfig_environment_test.go b/aws/resource_aws_appconfig_environment_test.go index 6e32abf0302..c833513097e 100644 --- a/aws/resource_aws_appconfig_environment_test.go +++ b/aws/resource_aws_appconfig_environment_test.go @@ -38,6 +38,7 @@ func TestAccAWSAppConfigEnvironment_basic(t *testing.T) { }, { ResourceName: resourceName, + ImportStateIdFunc: testAccAWSAppConfigEnvironmentImportStateIdFunc(resourceName), ImportState: true, ImportStateVerify: true, }, @@ -94,6 +95,7 @@ func TestAccAWSAppConfigEnvironment_Tags(t *testing.T) { }, { ResourceName: resourceName, + ImportStateIdFunc: testAccAWSAppConfigEnvironmentImportStateIdFunc(resourceName), ImportState: true, ImportStateVerify: true, }, @@ -288,3 +290,14 @@ POLICY } `, rName) } + +func testAccAWSAppConfigEnvironmentImportStateIdFunc(resourceName string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return "", fmt.Errorf("Not Found: %s", resourceName) + } + + return fmt.Sprintf("%s/%s", rs.Primary.Attributes["application_id"], rs.Primary.ID), nil + } +} From e99712be0cee90d9ff73b9be6ec33d07b1feb041 Mon Sep 17 00:00:00 2001 From: Suzuki Shunsuke Date: Tue, 11 May 2021 20:46:45 +0900 Subject: [PATCH 032/398] test: fix resourceName --- aws/resource_aws_appconfig_environment_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_appconfig_environment_test.go b/aws/resource_aws_appconfig_environment_test.go index c833513097e..fff5d3affa1 100644 --- a/aws/resource_aws_appconfig_environment_test.go +++ b/aws/resource_aws_appconfig_environment_test.go @@ -53,7 +53,7 @@ func TestAccAWSAppConfigEnvironment_disappears(t *testing.T) { appDesc := acctest.RandomWithPrefix("desc") envName := acctest.RandomWithPrefix("tf-acc-test") envDesc := acctest.RandomWithPrefix("desc") - resourceName := "aws_appconfig_application.test" + resourceName := "aws_appconfig_environment.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, From 570f425247cbce87c9417f26ffa9985751fc0095 Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Thu, 17 Jun 2021 14:12:31 -0400 Subject: [PATCH 033/398] add concurrency and ignore errors when deleting firewall manager web acls --- aws/resource_aws_wafv2_web_acl_test.go | 42 +++++++++++++++++--------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/aws/resource_aws_wafv2_web_acl_test.go b/aws/resource_aws_wafv2_web_acl_test.go index 7451f528b19..d0de3847183 100644 --- a/aws/resource_aws_wafv2_web_acl_test.go +++ b/aws/resource_aws_wafv2_web_acl_test.go @@ -8,6 +8,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/wafv2" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -24,12 +25,14 @@ func init() { func testSweepWafv2WebAcls(region string) error { client, err := sharedClientForRegion(region) + if err != nil { return fmt.Errorf("error getting client: %s", err) } - conn := client.(*AWSClient).wafv2conn - var sweeperErrs *multierror.Error + conn := client.(*AWSClient).wafv2conn + sweepResources := make([]*testSweepResource, 0) + var errs *multierror.Error input := &wafv2.ListWebACLsInput{ Scope: aws.String(wafv2.ScopeRegional), @@ -41,6 +44,10 @@ func testSweepWafv2WebAcls(region string) error { } for _, webAcl := range page.WebACLs { + if webAcl == nil { + continue + } + id := aws.StringValue(webAcl.Id) r := resourceAwsWafv2WebACL() @@ -49,29 +56,36 @@ func testSweepWafv2WebAcls(region string) error { d.Set("lock_token", webAcl.LockToken) d.Set("name", webAcl.Name) d.Set("scope", input.Scope) - err := r.Delete(d, client) - if err != nil { - sweeperErr := fmt.Errorf("error deleting WAFv2 Web ACL (%s): %w", id, err) - log.Printf("[ERROR] %s", sweeperErr) - sweeperErrs = multierror.Append(sweeperErrs, sweeperErr) - continue - } + sweepResources = append(sweepResources, NewTestSweepResource(r, d, client)) } return !lastPage }) - if testSweepSkipSweepError(err) { - log.Printf("[WARN] Skipping WAFv2 Web ACL sweep for %s: %s", region, err) - return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors + if err != nil { + errs = multierror.Append(errs, fmt.Errorf("error describing WAFv2 Web ACLs for %s: %w", region, err)) + } + + err = testSweepResourceOrchestrator(sweepResources) + + // Since we cannot exclude Firewall Manager WebACLs from the sweepResources var above, + // we instead catch and ignore the following expected AccessDeniedException. + // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/19149 + if tfawserr.ErrMessageContains(err, "AccessDeniedException", "managed by Firewall Manager") { + return errs.ErrorOrNil() } if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error describing WAFv2 Web ACLs: %w", err)) + errs = multierror.Append(errs, fmt.Errorf("error sweeping WAFv2 Web ACLs for %s: %w", region, err)) + } + + if testSweepSkipSweepError(errs.ErrorOrNil()) { + log.Printf("[WARN] Skipping WAFv2 Web ACLs sweep for %s: %s", region, errs) + return nil } - return sweeperErrs.ErrorOrNil() + return errs.ErrorOrNil() } func TestAccAwsWafv2WebACL_basic(t *testing.T) { From 80ee3b835673d08a77551a43312eb9eb7ba44575 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Fri, 18 Jun 2021 11:13:47 -0700 Subject: [PATCH 034/398] Uses resource Delete operation for API Gateway VPC Link sweeper and adds concurrency --- aws/resource_aws_api_gateway_vpc_link_test.go | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/aws/resource_aws_api_gateway_vpc_link_test.go b/aws/resource_aws_api_gateway_vpc_link_test.go index 253cf639529..27254afbbda 100644 --- a/aws/resource_aws_api_gateway_vpc_link_test.go +++ b/aws/resource_aws_api_gateway_vpc_link_test.go @@ -8,6 +8,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/apigateway" + multierror "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -23,40 +24,39 @@ func init() { func testSweepAPIGatewayVpcLinks(region string) error { client, err := sharedClientForRegion(region) if err != nil { - return fmt.Errorf("error getting client: %s", err) + return fmt.Errorf("error getting client: %w", err) } conn := client.(*AWSClient).apigatewayconn + sweepResources := make([]*testSweepResource, 0) + var sweeperErrs *multierror.Error + err = conn.GetVpcLinksPages(&apigateway.GetVpcLinksInput{}, func(page *apigateway.GetVpcLinksOutput, lastPage bool) bool { for _, item := range page.Items { - input := &apigateway.DeleteVpcLinkInput{ - VpcLinkId: item.Id, - } id := aws.StringValue(item.Id) - log.Printf("[INFO] Deleting API Gateway VPC Link: %s", id) - _, err := conn.DeleteVpcLink(input) + log.Printf("[INFO] Deleting API Gateway VPC Link (%s)", id) + r := resourceAwsApiGatewayVpcLink() + d := r.Data(nil) + d.SetId(id) - if err != nil { - log.Printf("[ERROR] Failed to delete API Gateway VPC Link %s: %s", id, err) - continue - } - - if err := waitForApiGatewayVpcLinkDeletion(conn, id); err != nil { - log.Printf("[ERROR] Error waiting for API Gateway VPC Link (%s) deletion: %s", id, err) - } + sweepResources = append(sweepResources, NewTestSweepResource(r, d, client)) } return !lastPage }) + if testSweepSkipSweepError(err) { + log.Printf("[WARN] Skipping API Gateway VPC Link sweep for %s: %s", region, err) + return nil + } if err != nil { - if testSweepSkipSweepError(err) { - log.Printf("[WARN] Skipping API Gateway VPC Link sweep for %s: %s", region, err) - return nil - } - return fmt.Errorf("Error retrieving API Gateway VPC Links: %s", err) + return fmt.Errorf("error retrieving API Gateway VPC Links: %w", err) } - return nil + if err := testSweepResourceOrchestrator(sweepResources); err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping App Runner AutoScaling Configuration Version for %s: %w", region, err)) + } + + return sweeperErrs.ErrorOrNil() } func TestAccAWSAPIGatewayVpcLink_basic(t *testing.T) { From 4d4335d305d3e53e4d9cbcab5f8546443a79f2f3 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Fri, 18 Jun 2021 11:15:30 -0700 Subject: [PATCH 035/398] Moves API Gateway VPC Link deletion waiter to `waiter` package --- .../service/apigateway/waiter/status.go | 31 +++++++++++++++ .../service/apigateway/waiter/waiter.go | 29 ++++++++++++++ aws/resource_aws_api_gateway_vpc_link.go | 39 ++----------------- 3 files changed, 64 insertions(+), 35 deletions(-) create mode 100644 aws/internal/service/apigateway/waiter/status.go create mode 100644 aws/internal/service/apigateway/waiter/waiter.go diff --git a/aws/internal/service/apigateway/waiter/status.go b/aws/internal/service/apigateway/waiter/status.go new file mode 100644 index 00000000000..864915761a1 --- /dev/null +++ b/aws/internal/service/apigateway/waiter/status.go @@ -0,0 +1,31 @@ +package waiter + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/apigateway" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func apiGatewayVpcLinkStatus(conn *apigateway.APIGateway, vpcLinkId string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := conn.GetVpcLink(&apigateway.GetVpcLinkInput{ + VpcLinkId: aws.String(vpcLinkId), + }) + if tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { + return nil, "", nil + } + if err != nil { + return nil, "", err + } + + // Error messages can also be contained in the response with FAILED status + if aws.StringValue(output.Status) == apigateway.VpcLinkStatusFailed { + return output, apigateway.VpcLinkStatusFailed, fmt.Errorf("%s: %s", apigateway.VpcLinkStatusFailed, aws.StringValue(output.StatusMessage)) + } + + return output, aws.StringValue(output.Status), nil + } +} diff --git a/aws/internal/service/apigateway/waiter/waiter.go b/aws/internal/service/apigateway/waiter/waiter.go new file mode 100644 index 00000000000..0b837c166ca --- /dev/null +++ b/aws/internal/service/apigateway/waiter/waiter.go @@ -0,0 +1,29 @@ +package waiter + +import ( + "time" + + "github.com/aws/aws-sdk-go/service/apigateway" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +const ( + // Maximum amount of time for VpcLink to delete + ApiGatewayVpcLinkDeleteTimeout = 20 * time.Minute +) + +func ApiGatewayVpcLinkDeleted(conn *apigateway.APIGateway, vpcLinkId string) error { + stateConf := resource.StateChangeConf{ + Pending: []string{apigateway.VpcLinkStatusPending, + apigateway.VpcLinkStatusAvailable, + apigateway.VpcLinkStatusDeleting}, + Target: []string{""}, + Timeout: ApiGatewayVpcLinkDeleteTimeout, + MinTimeout: 1 * time.Second, + Refresh: apiGatewayVpcLinkStatus(conn, vpcLinkId), + } + + _, err := stateConf.WaitForState() + + return err +} diff --git a/aws/resource_aws_api_gateway_vpc_link.go b/aws/resource_aws_api_gateway_vpc_link.go index 4a58946e084..ca167fcd1b9 100644 --- a/aws/resource_aws_api_gateway_vpc_link.go +++ b/aws/resource_aws_api_gateway_vpc_link.go @@ -11,13 +11,12 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/apigateway/waiter" ) const ( // Maximum amount of time for VpcLink to become available apigatewayVpcLinkAvailableTimeout = 20 * time.Minute - // Maximum amount of time for VpcLink to delete - apigatewayVpcLinkDeleteTimeout = 20 * time.Minute ) func resourceAwsApiGatewayVpcLink() *schema.Resource { @@ -213,11 +212,11 @@ func resourceAwsApiGatewayVpcLinkDelete(d *schema.ResourceData, meta interface{} } if err != nil { - return fmt.Errorf("error deleting API Gateway VPC Link (%s): %s", d.Id(), err) + return fmt.Errorf("error deleting API Gateway VPC Link (%s): %w", d.Id(), err) } - if err := waitForApiGatewayVpcLinkDeletion(conn, d.Id()); err != nil { - return fmt.Errorf("error waiting for API Gateway VPC Link (%s) deletion: %s", d.Id(), err) + if err := waiter.ApiGatewayVpcLinkDeleted(conn, d.Id()); err != nil { + return fmt.Errorf("error waiting for API Gateway VPC Link (%s) deletion: %w", d.Id(), err) } return nil @@ -235,33 +234,3 @@ func apigatewayVpcLinkRefreshStatusFunc(conn *apigateway.APIGateway, vl string) return resp, *resp.Status, nil } } - -func waitForApiGatewayVpcLinkDeletion(conn *apigateway.APIGateway, vpcLinkID string) error { - stateConf := resource.StateChangeConf{ - Pending: []string{apigateway.VpcLinkStatusPending, - apigateway.VpcLinkStatusAvailable, - apigateway.VpcLinkStatusDeleting}, - Target: []string{""}, - Timeout: apigatewayVpcLinkDeleteTimeout, - MinTimeout: 1 * time.Second, - Refresh: func() (interface{}, string, error) { - resp, err := conn.GetVpcLink(&apigateway.GetVpcLinkInput{ - VpcLinkId: aws.String(vpcLinkID), - }) - - if isAWSErr(err, apigateway.ErrCodeNotFoundException, "") { - return 1, "", nil - } - - if err != nil { - return nil, apigateway.VpcLinkStatusFailed, err - } - - return resp, aws.StringValue(resp.Status), nil - }, - } - - _, err := stateConf.WaitForState() - - return err -} From eeabf4740c1985e311d51314c4696dae4b75236f Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Fri, 18 Jun 2021 12:10:59 -0700 Subject: [PATCH 036/398] Fixes copy-paste error --- aws/resource_aws_api_gateway_vpc_link_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_api_gateway_vpc_link_test.go b/aws/resource_aws_api_gateway_vpc_link_test.go index 27254afbbda..60d3f191829 100644 --- a/aws/resource_aws_api_gateway_vpc_link_test.go +++ b/aws/resource_aws_api_gateway_vpc_link_test.go @@ -53,7 +53,7 @@ func testSweepAPIGatewayVpcLinks(region string) error { } if err := testSweepResourceOrchestrator(sweepResources); err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping App Runner AutoScaling Configuration Version for %s: %w", region, err)) + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping API Gateway VPC Links")) } return sweeperErrs.ErrorOrNil() From c491ce7f03ea9f1a22c7edf653b839f99f9e00e4 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Fri, 18 Jun 2021 12:13:01 -0700 Subject: [PATCH 037/398] Includes nested error --- aws/resource_aws_api_gateway_vpc_link_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_api_gateway_vpc_link_test.go b/aws/resource_aws_api_gateway_vpc_link_test.go index 60d3f191829..db537773058 100644 --- a/aws/resource_aws_api_gateway_vpc_link_test.go +++ b/aws/resource_aws_api_gateway_vpc_link_test.go @@ -53,7 +53,7 @@ func testSweepAPIGatewayVpcLinks(region string) error { } if err := testSweepResourceOrchestrator(sweepResources); err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping API Gateway VPC Links")) + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping API Gateway VPC Links: %w", err)) } return sweeperErrs.ErrorOrNil() From 05ad3b0f51ddb55c5cfb2dabf69d705c743f8092 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Fri, 18 Jun 2021 15:02:33 -0700 Subject: [PATCH 038/398] Returns an error from `aws_lb` sweeper when deletion fails --- aws/resource_aws_lb_test.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/aws/resource_aws_lb_test.go b/aws/resource_aws_lb_test.go index 122281d19d3..b3779c3eae5 100644 --- a/aws/resource_aws_lb_test.go +++ b/aws/resource_aws_lb_test.go @@ -11,6 +11,7 @@ import ( "github.com/aws/aws-sdk-go/service/elb" "github.com/aws/aws-sdk-go/service/elbv2" "github.com/hashicorp/aws-sdk-go-base/tfawserr" + multierror "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -44,6 +45,7 @@ func testSweepLBs(region string) error { } conn := client.(*AWSClient).elbv2conn + var sweeperErrs *multierror.Error err = conn.DescribeLoadBalancersPages(&elbv2.DescribeLoadBalancersInput{}, func(page *elbv2.DescribeLoadBalancersOutput, lastPage bool) bool { if page == nil || len(page.LoadBalancers) == 0 { log.Print("[DEBUG] No LBs to sweep") @@ -58,19 +60,21 @@ func testSweepLBs(region string) error { LoadBalancerArn: loadBalancer.LoadBalancerArn, }) if err != nil { - log.Printf("[ERROR] Failed to delete LB (%s): %s", name, err) + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("failed to delete LB (%s): %w", name, err)) + continue } } return !lastPage }) + if testSweepSkipSweepError(err) { + log.Printf("[WARN] Skipping LB sweep for %s: %s", region, err) + return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors + } if err != nil { - if testSweepSkipSweepError(err) { - log.Printf("[WARN] Skipping LB sweep for %s: %s", region, err) - return nil - } - return fmt.Errorf("Error retrieving LBs: %s", err) + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error retrieving LBs: %w", err)) } - return nil + + return sweeperErrs.ErrorOrNil() } func TestLBCloudwatchSuffixFromARN(t *testing.T) { From 4f3e6f3e0bfbd1576cb81022fdebf8a214d4c088 Mon Sep 17 00:00:00 2001 From: Tyler Lynch Date: Thu, 24 Jun 2021 13:43:58 -0400 Subject: [PATCH 039/398] Added AccTests for MicrosoftAD to StorageGateway --- ...esource_aws_storagegateway_gateway_test.go | 214 ++++++++++++++++++ 1 file changed, 214 insertions(+) diff --git a/aws/resource_aws_storagegateway_gateway_test.go b/aws/resource_aws_storagegateway_gateway_test.go index 8673a60e86f..fefdb6fe544 100644 --- a/aws/resource_aws_storagegateway_gateway_test.go +++ b/aws/resource_aws_storagegateway_gateway_test.go @@ -462,6 +462,67 @@ func TestAccAWSStorageGatewayGateway_SmbActiveDirectorySettings_timeout(t *testi }) } +func TestAccAWSStorageGatewayGateway_SmbMicrosoftActiveDirectorySettings(t *testing.T) { + var gateway storagegateway.DescribeGatewayInformationOutput + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_storagegateway_gateway.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, storagegateway.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSStorageGatewayGatewayDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSStorageGatewayGatewayConfig_SmbMicrosoftActiveDirectorySettings(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSStorageGatewayGatewayExists(resourceName, &gateway), + resource.TestCheckResourceAttr(resourceName, "smb_active_directory_settings.#", "1"), + resource.TestCheckResourceAttr(resourceName, "smb_active_directory_settings.0.domain_name", "terraformtesting.com"), + resource.TestCheckResourceAttr(resourceName, "smb_active_directory_settings.0.username", "Admin"), + resource.TestCheckResourceAttrSet(resourceName, "smb_active_directory_settings.0.active_directory_status"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"activation_key", "gateway_ip_address", "smb_active_directory_settings"}, + }, + }, + }) +} + +func TestAccAWSStorageGatewayGateway_SmbMicrosoftActiveDirectorySettings_timeout(t *testing.T) { + var gateway storagegateway.DescribeGatewayInformationOutput + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_storagegateway_gateway.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, storagegateway.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSStorageGatewayGatewayDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSStorageGatewayGatewayConfig_SmbMicrosoftActiveDirectorySettingsTimeout(rName, 50), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSStorageGatewayGatewayExists(resourceName, &gateway), + resource.TestCheckResourceAttr(resourceName, "smb_active_directory_settings.#", "1"), + resource.TestCheckResourceAttr(resourceName, "smb_active_directory_settings.0.domain_name", "terraformtesting.com"), + resource.TestCheckResourceAttr(resourceName, "smb_active_directory_settings.0.timeout_in_seconds", "50"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"activation_key", "gateway_ip_address", "smb_active_directory_settings"}, + }, + }, + }) +} + func TestAccAWSStorageGatewayGateway_SmbGuestPassword(t *testing.T) { var gateway storagegateway.DescribeGatewayInformationOutput rName := acctest.RandomWithPrefix("tf-acc-test") @@ -1105,6 +1166,120 @@ resource "aws_instance" "test" { `, rName)) } +func testAccAWSStorageGatewayGatewayConfigSmbMicrosoftActiveDirectorySettingsBase(rName string) string { + return composeConfig( + // Reference: https://docs.aws.amazon.com/storagegateway/latest/userguide/Requirements.html + testAccAvailableEc2InstanceTypeForAvailabilityZone("aws_subnet.test[0].availability_zone", "m5.xlarge", "m4.xlarge"), + testAccAvailableAZsNoOptInConfig(), + fmt.Sprintf(` +# Directory Service Directories must be deployed across multiple EC2 Availability Zones +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" + + tags = { + Name = %[1]q + } +} + +resource "aws_subnet" "test" { + count = 2 + + availability_zone = data.aws_availability_zones.available.names[count.index] + cidr_block = "10.0.${count.index}.0/24" + vpc_id = aws_vpc.test.id + + tags = { + Name = %[1]q + } +} + +resource "aws_internet_gateway" "test" { + vpc_id = aws_vpc.test.id + + tags = { + Name = %[1]q + } +} + +resource "aws_route" "test" { + destination_cidr_block = "0.0.0.0/0" + gateway_id = aws_internet_gateway.test.id + route_table_id = aws_vpc.test.main_route_table_id +} + +resource "aws_security_group" "test" { + vpc_id = aws_vpc.test.id + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + tags = { + Name = %[1]q + } +} + +resource "aws_directory_service_directory" "test" { + edition = "Standard" + name = "terraformtesting.com" + password = "SuperSecretPassw0rd" + type = "MicrosoftAD" + + vpc_settings { + subnet_ids = aws_subnet.test[*].id + vpc_id = aws_vpc.test.id + } + + tags = { + Name = %[1]q + } +} + +resource "aws_vpc_dhcp_options" "test" { + domain_name = aws_directory_service_directory.test.name + domain_name_servers = aws_directory_service_directory.test.dns_ip_addresses + + tags = { + Name = %[1]q + } +} + +resource "aws_vpc_dhcp_options_association" "test" { + dhcp_options_id = aws_vpc_dhcp_options.test.id + vpc_id = aws_vpc.test.id +} + +# Reference: https://docs.aws.amazon.com/storagegateway/latest/userguide/ec2-gateway-file.html +data "aws_ssm_parameter" "aws_service_storagegateway_ami_FILE_S3_latest" { + name = "/aws/service/storagegateway/ami/FILE_S3/latest" +} + +resource "aws_instance" "test" { + depends_on = [aws_route.test, aws_vpc_dhcp_options_association.test] + + ami = data.aws_ssm_parameter.aws_service_storagegateway_ami_FILE_S3_latest.value + associate_public_ip_address = true + instance_type = data.aws_ec2_instance_type_offering.available.instance_type + vpc_security_group_ids = [aws_security_group.test.id] + subnet_id = aws_subnet.test[0].id + + tags = { + Name = %[1]q + } +} +`, rName)) +} + func testAccAWSStorageGatewayGatewayConfig_SmbActiveDirectorySettings(rName string) string { return composeConfig( testAccAWSStorageGatewayGatewayConfigSmbActiveDirectorySettingsBase(rName), @@ -1144,6 +1319,45 @@ resource "aws_storagegateway_gateway" "test" { `, rName, timeout)) } +func testAccAWSStorageGatewayGatewayConfig_SmbMicrosoftActiveDirectorySettings(rName string) string { + return composeConfig( + testAccAWSStorageGatewayGatewayConfigSmbMicrosoftActiveDirectorySettingsBase(rName), + fmt.Sprintf(` +resource "aws_storagegateway_gateway" "test" { + gateway_ip_address = aws_instance.test.public_ip + gateway_name = %[1]q + gateway_timezone = "GMT" + gateway_type = "FILE_S3" + + smb_active_directory_settings { + domain_name = aws_directory_service_directory.test.name + password = aws_directory_service_directory.test.password + username = "Admin" + } +} +`, rName)) +} + +func testAccAWSStorageGatewayGatewayConfig_SmbMicrosoftActiveDirectorySettingsTimeout(rName string, timeout int) string { + return composeConfig( + testAccAWSStorageGatewayGatewayConfigSmbMicrosoftActiveDirectorySettingsBase(rName), + fmt.Sprintf(` +resource "aws_storagegateway_gateway" "test" { + gateway_ip_address = aws_instance.test.public_ip + gateway_name = %[1]q + gateway_timezone = "GMT" + gateway_type = "FILE_S3" + + smb_active_directory_settings { + domain_name = aws_directory_service_directory.test.name + password = aws_directory_service_directory.test.password + username = "Admin" + timeout_in_seconds = %[2]d + } +} +`, rName, timeout)) +} + func testAccAWSStorageGatewayGatewayConfig_SmbGuestPassword(rName, smbGuestPassword string) string { return testAccAWSStorageGateway_FileGatewayBase(rName) + fmt.Sprintf(` resource "aws_storagegateway_gateway" "test" { From 10dc6f523542b51f698ba50a2af6581b2316f39e Mon Sep 17 00:00:00 2001 From: Farhan Angullia Date: Fri, 25 Jun 2021 02:46:42 +0800 Subject: [PATCH 040/398] feat(s3logs): added support for datasources and added acc tests --- aws/resource_aws_guardduty_detector.go | 94 ++++++++++++++++++++- aws/resource_aws_guardduty_detector_test.go | 48 +++++++++++ aws/resource_aws_guardduty_test.go | 9 +- 3 files changed, 146 insertions(+), 5 deletions(-) diff --git a/aws/resource_aws_guardduty_detector.go b/aws/resource_aws_guardduty_detector.go index f3cd98b16bd..cf9b6bd6da4 100644 --- a/aws/resource_aws_guardduty_detector.go +++ b/aws/resource_aws_guardduty_detector.go @@ -45,6 +45,32 @@ func resourceAwsGuardDutyDetector() *schema.Resource { Optional: true, Computed: true, }, + + "datasources": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "s3_logs": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enable": { + Type: schema.TypeBool, + Required: true, + }, + }, + }, + }, + }, + }, + }, + "tags": tagsSchema(), "tags_all": tagsSchemaComputed(), @@ -67,6 +93,10 @@ func resourceAwsGuardDutyDetectorCreate(d *schema.ResourceData, meta interface{} input.FindingPublishingFrequency = aws.String(v.(string)) } + if v, ok := d.GetOk("datasources"); ok { + input.DataSources = expandDataSourceConfigurations(v.([]interface{})) + } + if len(tags) > 0 { input.Tags = tags.IgnoreAws().GuarddutyTags() } @@ -114,6 +144,10 @@ func resourceAwsGuardDutyDetectorRead(d *schema.ResourceData, meta interface{}) d.Set("enable", *gdo.Status == guardduty.DetectorStatusEnabled) d.Set("finding_publishing_frequency", gdo.FindingPublishingFrequency) + if err := d.Set("datasources", flattenDataSourceConfigurations(gdo.DataSources)); err != nil { + return fmt.Errorf("error setting datasources: %s", err) + } + tags := keyvaluetags.GuarddutyKeyValueTags(gdo.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig) //lintignore:AWSR002 @@ -131,13 +165,17 @@ func resourceAwsGuardDutyDetectorRead(d *schema.ResourceData, meta interface{}) func resourceAwsGuardDutyDetectorUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).guarddutyconn - if d.HasChanges("enable", "finding_publishing_frequency") { + if d.HasChanges("enable", "finding_publishing_frequency", "datasources") { input := guardduty.UpdateDetectorInput{ DetectorId: aws.String(d.Id()), Enable: aws.Bool(d.Get("enable").(bool)), FindingPublishingFrequency: aws.String(d.Get("finding_publishing_frequency").(string)), } + if d.HasChange("datasources") { + input.DataSources = expandDataSourceConfigurations(d.Get("datasources").([]interface{})) + } + log.Printf("[DEBUG] Update GuardDuty Detector: %s", input) _, err := conn.UpdateDetector(&input) if err != nil { @@ -187,3 +225,57 @@ func resourceAwsGuardDutyDetectorDelete(d *schema.ResourceData, meta interface{} return nil } + +func expandDataSourceConfigurations(dsc []interface{}) *guardduty.DataSourceConfigurations { + if len(dsc) < 1 || dsc[0] == nil { + return nil + } + + m := dsc[0].(map[string]interface{}) + + dataSourceConfigurations := &guardduty.DataSourceConfigurations{} + + if v, ok := m["s3_logs"]; ok && v != "" && (len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil) { + dataSourceConfigurations.S3Logs = expandS3LogsConfiguration(v.([]interface{})) + } + + return dataSourceConfigurations +} + +func expandS3LogsConfiguration(slc []interface{}) *guardduty.S3LogsConfiguration { + if len(slc) < 1 || slc[0] == nil { + return nil + } + + m := slc[0].(map[string]interface{}) + + s3LogsConfiguration := &guardduty.S3LogsConfiguration{ + Enable: aws.Bool(m["enable"].(bool)), + } + + return s3LogsConfiguration +} + +func flattenDataSourceConfigurations(dsc *guardduty.DataSourceConfigurationsResult) []interface{} { + if dsc == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "s3_logs": flattenS3LogsConfiguration(dsc.S3Logs), + } + + return []interface{}{m} +} + +func flattenS3LogsConfiguration(slc *guardduty.S3LogsConfigurationResult) []interface{} { + if slc == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "enable": aws.StringValue(slc.Status) == guardduty.DataSourceStatusEnabled, + } + + return []interface{}{m} +} diff --git a/aws/resource_aws_guardduty_detector_test.go b/aws/resource_aws_guardduty_detector_test.go index cdb38919126..d51c794d26b 100644 --- a/aws/resource_aws_guardduty_detector_test.go +++ b/aws/resource_aws_guardduty_detector_test.go @@ -160,6 +160,42 @@ func testAccAwsGuardDutyDetector_tags(t *testing.T) { }) } +func testAccAwsGuardDutyDetector_datasources_s3logs(t *testing.T) { + resourceName := "aws_guardduty_detector.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, guardduty.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsGuardDutyDetectorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccGuardDutyDetectorConfigDatasourcesS3Logs(true), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsGuardDutyDetectorExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "datasources.#", "1"), + resource.TestCheckResourceAttr(resourceName, "datasources.0.s3_logs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "datasources.0.s3_logs.0.enable", "true"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccGuardDutyDetectorConfigDatasourcesS3Logs(false), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsGuardDutyDetectorExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "datasources.#", "1"), + resource.TestCheckResourceAttr(resourceName, "datasources.0.s3_logs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "datasources.0.s3_logs.0.enable", "false"), + ), + }, + }, + }) +} + func testAccCheckAwsGuardDutyDetectorDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).guarddutyconn @@ -239,3 +275,15 @@ resource "aws_guardduty_detector" "test" { } `, tagKey1, tagValue1, tagKey2, tagValue2) } + +func testAccGuardDutyDetectorConfigDatasourcesS3Logs(enable bool) string { + return fmt.Sprintf(` +resource "aws_guardduty_detector" "test" { + datasources { + s3_logs { + enable = %[1]t + } + } +} +`, enable) +} diff --git a/aws/resource_aws_guardduty_test.go b/aws/resource_aws_guardduty_test.go index 596655cb7aa..9d09d258a17 100644 --- a/aws/resource_aws_guardduty_test.go +++ b/aws/resource_aws_guardduty_test.go @@ -8,10 +8,11 @@ import ( func TestAccAWSGuardDuty_serial(t *testing.T) { testCases := map[string]map[string]func(t *testing.T){ "Detector": { - "basic": testAccAwsGuardDutyDetector_basic, - "tags": testAccAwsGuardDutyDetector_tags, - "datasource_basic": testAccAWSGuarddutyDetectorDataSource_basic, - "datasource_id": testAccAWSGuarddutyDetectorDataSource_Id, + "basic": testAccAwsGuardDutyDetector_basic, + "datasources_s3logs": testAccAwsGuardDutyDetector_datasources_s3logs, + "tags": testAccAwsGuardDutyDetector_tags, + "datasource_basic": testAccAWSGuarddutyDetectorDataSource_basic, + "datasource_id": testAccAWSGuarddutyDetectorDataSource_Id, }, "Filter": { "basic": testAccAwsGuardDutyFilter_basic, From dd37fee59f777450a7a5c77c02d8512f230ac362 Mon Sep 17 00:00:00 2001 From: Farhan Angullia Date: Fri, 25 Jun 2021 02:47:47 +0800 Subject: [PATCH 041/398] docs: updated site for new args --- .../docs/r/guardduty_detector.html.markdown | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/website/docs/r/guardduty_detector.html.markdown b/website/docs/r/guardduty_detector.html.markdown index 8431be3e1b2..f9d5cf5f5bf 100644 --- a/website/docs/r/guardduty_detector.html.markdown +++ b/website/docs/r/guardduty_detector.html.markdown @@ -17,6 +17,12 @@ Provides a resource to manage a GuardDuty detector. ```terraform resource "aws_guardduty_detector" "MyDetector" { enable = true + + datasources { + s3_logs { + enable = true + } + } } ``` @@ -26,8 +32,21 @@ The following arguments are supported: * `enable` - (Optional) Enable monitoring and feedback reporting. Setting to `false` is equivalent to "suspending" GuardDuty. Defaults to `true`. * `finding_publishing_frequency` - (Optional) Specifies the frequency of notifications sent for subsequent finding occurrences. If the detector is a GuardDuty member account, the value is determined by the GuardDuty primary account and cannot be modified, otherwise defaults to `SIX_HOURS`. For standalone and GuardDuty primary accounts, it must be configured in Terraform to enable drift detection. Valid values for standalone and primary accounts: `FIFTEEN_MINUTES`, `ONE_HOUR`, `SIX_HOURS`. See [AWS Documentation](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_findings_cloudwatch.html#guardduty_findings_cloudwatch_notification_frequency) for more information. +* `datasources` - (Optional) Describes which data sources will be enabled for the detector. See [Data Sources](#data-sources) below for more details. * `tags` - (Optional) Key-value map of resource tags. If configured with a provider [`default_tags` configuration block](/docs/providers/aws/index.html#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. +### Data Sources + +The `datasources` block supports the following: + +* `s3_logs` - (Optional) Describes whether S3 data event logs are enabled as a data source. See [S3 Logs](#s3-logs) below for more details. + +### S3 Logs + +This `s3_logs` block supports the following: + +* `enable` - (Required) If true, enables [S3 Protection](https://docs.aws.amazon.com/guardduty/latest/ug/s3_detection.html). Defaults to `true`. + ## Attributes Reference In addition to all arguments above, the following attributes are exported: From c7133ae6ce4a37fe4300cbe48ab63f63126212ff Mon Sep 17 00:00:00 2001 From: Farhan Angullia Date: Fri, 25 Jun 2021 03:26:47 +0800 Subject: [PATCH 042/398] fix terrafmt for acc tests --- aws/resource_aws_guardduty_detector_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/aws/resource_aws_guardduty_detector_test.go b/aws/resource_aws_guardduty_detector_test.go index d51c794d26b..4a3fc48a650 100644 --- a/aws/resource_aws_guardduty_detector_test.go +++ b/aws/resource_aws_guardduty_detector_test.go @@ -279,11 +279,11 @@ resource "aws_guardduty_detector" "test" { func testAccGuardDutyDetectorConfigDatasourcesS3Logs(enable bool) string { return fmt.Sprintf(` resource "aws_guardduty_detector" "test" { - datasources { - s3_logs { - enable = %[1]t - } - } + datasources { + s3_logs { + enable = %[1]t + } + } } `, enable) } From 5d3f9dc1505ae5b763ef3b9e4ad9e5af4844a983 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Fri, 25 Jun 2021 15:56:33 +0300 Subject: [PATCH 043/398] add `multi_region` --- aws/resource_aws_kms_key.go | 68 +++++++++++++++----------------- aws/resource_aws_kms_key_test.go | 55 +++++++++++++++++++------- 2 files changed, 72 insertions(+), 51 deletions(-) diff --git a/aws/resource_aws_kms_key.go b/aws/resource_aws_kms_key.go index 590a88b4e3b..37b05687b69 100644 --- a/aws/resource_aws_kms_key.go +++ b/aws/resource_aws_kms_key.go @@ -6,7 +6,6 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/kms" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -40,35 +39,24 @@ func resourceAwsKmsKey() *schema.Resource { Computed: true, }, "description": { - Type: schema.TypeString, - Optional: true, - Computed: true, + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringLenBetween(0, 8192), }, "key_usage": { - Type: schema.TypeString, - Optional: true, - Default: kms.KeyUsageTypeEncryptDecrypt, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - kms.KeyUsageTypeEncryptDecrypt, - kms.KeyUsageTypeSignVerify, - }, false), + Type: schema.TypeString, + Optional: true, + Default: kms.KeyUsageTypeEncryptDecrypt, + ForceNew: true, + ValidateFunc: validation.StringInSlice(kms.KeyUsageType_Values(), false), }, "customer_master_key_spec": { - Type: schema.TypeString, - Optional: true, - Default: kms.CustomerMasterKeySpecSymmetricDefault, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - kms.CustomerMasterKeySpecSymmetricDefault, - kms.CustomerMasterKeySpecRsa2048, - kms.CustomerMasterKeySpecRsa3072, - kms.CustomerMasterKeySpecRsa4096, - kms.CustomerMasterKeySpecEccNistP256, - kms.CustomerMasterKeySpecEccNistP384, - kms.CustomerMasterKeySpecEccNistP521, - kms.CustomerMasterKeySpecEccSecgP256k1, - }, false), + Type: schema.TypeString, + Optional: true, + Default: kms.CustomerMasterKeySpecSymmetricDefault, + ForceNew: true, + ValidateFunc: validation.StringInSlice(kms.CustomerMasterKeySpec_Values(), false), }, "policy": { Type: schema.TypeString, @@ -82,6 +70,12 @@ func resourceAwsKmsKey() *schema.Resource { Optional: true, Default: true, }, + "multi_region": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + Default: false, + }, "enable_key_rotation": { Type: schema.TypeBool, Optional: true, @@ -107,6 +101,7 @@ func resourceAwsKmsKeyCreate(d *schema.ResourceData, meta interface{}) error { req := &kms.CreateKeyInput{ CustomerMasterKeySpec: aws.String(d.Get("customer_master_key_spec").(string)), KeyUsage: aws.String(d.Get("key_usage").(string)), + MultiRegion: aws.Bool(d.Get("multi_region").(bool)), } if v, exists := d.GetOk("description"); exists { req.Description = aws.String(v.(string)) @@ -196,6 +191,7 @@ func resourceAwsKmsKeyRead(d *schema.ResourceData, meta interface{}) error { d.Set("key_usage", metadata.KeyUsage) d.Set("customer_master_key_spec", metadata.CustomerMasterKeySpec) d.Set("is_enabled", metadata.Enabled) + d.Set("multi_region", metadata.MultiRegion) pOut, err := retryOnAwsCode(kms.ErrCodeNotFoundException, func() (interface{}, error) { return conn.GetKeyPolicy(&kms.GetKeyPolicyInput{ @@ -210,7 +206,7 @@ func resourceAwsKmsKeyRead(d *schema.ResourceData, meta interface{}) error { p := pOut.(*kms.GetKeyPolicyOutput) policy, err := structure.NormalizeJsonString(*p.Policy) if err != nil { - return fmt.Errorf("policy contains an invalid JSON: %s", err) + return fmt.Errorf("policy contains an invalid JSON: %w", err) } d.Set("policy", policy) @@ -246,7 +242,7 @@ func resourceAwsKmsKeyRead(d *schema.ResourceData, meta interface{}) error { } if err != nil { - return fmt.Errorf("error listing tags for KMS Key (%s): %s", d.Id(), err) + return fmt.Errorf("error listing tags for KMS Key (%s): %w", d.Id(), err) } tags = tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig) @@ -302,7 +298,7 @@ func resourceAwsKmsKeyUpdate(d *schema.ResourceData, meta interface{}) error { o, n := d.GetChange("tags_all") if err := keyvaluetags.KmsUpdateTags(conn, d.Id(), o, n); err != nil { - return fmt.Errorf("error updating KMS Key (%s) tags: %s", d.Id(), err) + return fmt.Errorf("error updating KMS Key (%s) tags: %w", d.Id(), err) } } @@ -327,7 +323,7 @@ func resourceAwsKmsKeyDescriptionUpdate(conn *kms.KMS, d *schema.ResourceData) e func resourceAwsKmsKeyPolicyUpdate(conn *kms.KMS, d *schema.ResourceData) error { policy, err := structure.NormalizeJsonString(d.Get("policy").(string)) if err != nil { - return fmt.Errorf("policy contains an invalid JSON: %s", err) + return fmt.Errorf("policy contains an invalid JSON: %w", err) } keyId := d.Get("key_id").(string) @@ -377,8 +373,7 @@ func updateKmsKeyStatus(conn *kms.KMS, id string, shouldBeEnabled bool) error { KeyId: aws.String(id), }) if err != nil { - awsErr, ok := err.(awserr.Error) - if ok && awsErr.Code() == "NotFoundException" { + if isAWSErr(err, kms.ErrCodeNotFoundException, "") { return nil, fmt.Sprintf("%t", !shouldBeEnabled), nil } return resp, "FAILED", err @@ -392,7 +387,7 @@ func updateKmsKeyStatus(conn *kms.KMS, id string, shouldBeEnabled bool) error { _, err = wait.WaitForState() if err != nil { - return fmt.Errorf("Failed setting KMS key status to %t: %s", shouldBeEnabled, err) + return fmt.Errorf("Failed setting KMS key status to %t: %w", shouldBeEnabled, err) } return nil @@ -405,11 +400,10 @@ func updateKmsKeyRotationStatus(conn *kms.KMS, d *schema.ResourceData) error { err := handleKeyRotation(conn, shouldEnableRotation, aws.String(d.Id())) if err != nil { - awsErr, ok := err.(awserr.Error) - if ok && awsErr.Code() == "DisabledException" { + if isAWSErr(err, kms.ErrCodeDisabledException, "") { return resource.RetryableError(err) } - if ok && awsErr.Code() == "NotFoundException" { + if isAWSErr(err, kms.ErrCodeNotFoundException, "") { return resource.RetryableError(err) } @@ -438,7 +432,7 @@ func updateKmsKeyRotationStatus(conn *kms.KMS, d *schema.ResourceData) error { log.Printf("[DEBUG] Checking if KMS key %s rotation status is %t", d.Id(), shouldEnableRotation) - out, err := retryOnAwsCode("NotFoundException", func() (interface{}, error) { + out, err := retryOnAwsCode(kms.ErrCodeNotFoundException, func() (interface{}, error) { return conn.GetKeyRotationStatus(&kms.GetKeyRotationStatusInput{ KeyId: aws.String(d.Id()), }) diff --git a/aws/resource_aws_kms_key_test.go b/aws/resource_aws_kms_key_test.go index 300f0ffffb2..cd5912940d2 100644 --- a/aws/resource_aws_kms_key_test.go +++ b/aws/resource_aws_kms_key_test.go @@ -84,6 +84,8 @@ func TestAccAWSKmsKey_basic(t *testing.T) { testAccCheckAWSKmsKeyExists(resourceName, &key), resource.TestCheckResourceAttr(resourceName, "customer_master_key_spec", "SYMMETRIC_DEFAULT"), resource.TestCheckResourceAttr(resourceName, "key_usage", "ENCRYPT_DECRYPT"), + resource.TestCheckResourceAttr(resourceName, "multi_region", "false"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, { @@ -134,7 +136,7 @@ func TestAccAWSKmsKey_disappears(t *testing.T) { Config: testAccAWSKmsKey(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSKmsKeyExists(resourceName, &key), - testAccCheckAWSKmsKeyDisappears(&key), + testAccCheckResourceDisappears(testAccProvider, resourceAwsKmsKey(), resourceName), ), ExpectNonEmptyPlan: true, }, @@ -315,6 +317,34 @@ func TestAccAWSKmsKey_tags(t *testing.T) { }) } +func TestAccAWSKmsKey_multiRegion(t *testing.T) { + var key kms.KeyMetadata + rName := fmt.Sprintf("tf-testacc-kms-key-%s", acctest.RandString(13)) + resourceName := "aws_kms_key.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, kms.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSKmsKeyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSKmsKeyMultiRegionConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSKmsKeyExists(resourceName, &key), + resource.TestCheckResourceAttr(resourceName, "multi_region", "true"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"deletion_window_in_days"}, + }, + }, + }) +} + func testAccCheckAWSKmsKeyHasPolicy(name string, expectedPolicyText string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[name] @@ -417,19 +447,6 @@ func testAccCheckAWSKmsKeyIsEnabled(key *kms.KeyMetadata, isEnabled bool) resour } } -func testAccCheckAWSKmsKeyDisappears(key *kms.KeyMetadata) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).kmsconn - - _, err := conn.ScheduleKeyDeletion(&kms.ScheduleKeyDeletionInput{ - KeyId: key.KeyId, - PendingWindowInDays: aws.Int64(int64(7)), - }) - - return err - } -} - func testAccAWSKmsKey(rName string) string { return fmt.Sprintf(` resource "aws_kms_key" "test" { @@ -656,3 +673,13 @@ resource "aws_kms_key" "test" { } `, rName) } + +func testAccAWSKmsKeyMultiRegionConfig(rName string) string { + return fmt.Sprintf(` +resource "aws_kms_key" "test" { + description = %[1]q + deletion_window_in_days = 7 + multi_region = true +} +`, rName) +} From 584f3aacb12b1acbbd56041f2dd1544ab71c609a Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Fri, 25 Jun 2021 15:58:07 +0300 Subject: [PATCH 044/398] docs --- website/docs/r/kms_key.html.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/website/docs/r/kms_key.html.markdown b/website/docs/r/kms_key.html.markdown index 7248feffd5b..5dbcbdee834 100644 --- a/website/docs/r/kms_key.html.markdown +++ b/website/docs/r/kms_key.html.markdown @@ -35,6 +35,7 @@ Valid values: `SYMMETRIC_DEFAULT`, `RSA_2048`, `RSA_3072`, `RSA_4096`, `ECC_NIS * `deletion_window_in_days` - (Optional) Duration in days after which the key is deleted after destruction of the resource, must be between 7 and 30 days. Defaults to 30 days. * `is_enabled` - (Optional) Specifies whether the key is enabled. Defaults to true. * `enable_key_rotation` - (Optional) Specifies whether [key rotation](http://docs.aws.amazon.com/kms/latest/developerguide/rotate-keys.html) is enabled. Defaults to false. +* `multi_region` - (Optional) Creates a multi-Region primary key that you can replicate into other AWS Regions. You cannot change this value after you create the CMK. Defaults to false. * `tags` - (Optional) A map of tags to assign to the object. If configured with a provider [`default_tags` configuration block](https://www.terraform.io/docs/providers/aws/index.html#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. ## Attributes Reference From 6af2e26ebba4ab7b8923f63a176a2cd46b370740 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Fri, 25 Jun 2021 16:01:29 +0300 Subject: [PATCH 045/398] change log --- .changelog/19967.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changelog/19967.txt diff --git a/.changelog/19967.txt b/.changelog/19967.txt new file mode 100644 index 00000000000..1c64698511d --- /dev/null +++ b/.changelog/19967.txt @@ -0,0 +1,7 @@ +```release-note:enhancement +resource/aws_kms_key: Add `multi_region` argument. +``` + +```release-note:enhancement +resource/aws_kms_key: Add plan time validation to `description`. +``` From 490b7400ef89f878e65babc86eaad063da5c73dc Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Fri, 25 Jun 2021 16:12:38 +0300 Subject: [PATCH 046/398] sweeper --- aws/resource_aws_kms_key_test.go | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/aws/resource_aws_kms_key_test.go b/aws/resource_aws_kms_key_test.go index cd5912940d2..3d33f1acadb 100644 --- a/aws/resource_aws_kms_key_test.go +++ b/aws/resource_aws_kms_key_test.go @@ -23,34 +23,37 @@ func init() { func testSweepKmsKeys(region string) error { client, err := sharedClientForRegion(region) if err != nil { - return fmt.Errorf("error getting client: %s", err) + return fmt.Errorf("error getting client: %w", err) } conn := client.(*AWSClient).kmsconn err = conn.ListKeysPages(&kms.ListKeysInput{Limit: aws.Int64(int64(1000))}, func(out *kms.ListKeysOutput, lastPage bool) bool { for _, k := range out.Keys { + kKeyId := aws.StringValue(k.KeyId) kOut, err := conn.DescribeKey(&kms.DescribeKeyInput{ KeyId: k.KeyId, }) if err != nil { - log.Printf("Error: Failed to describe key %q: %s", *k.KeyId, err) + log.Printf("Error: Failed to describe key %q: %s", kKeyId, err) return false } - if *kOut.KeyMetadata.KeyManager == kms.KeyManagerTypeAws { + if aws.StringValue(kOut.KeyMetadata.KeyManager) == kms.KeyManagerTypeAws { // Skip (default) keys which are managed by AWS continue } - if *kOut.KeyMetadata.KeyState == kms.KeyStatePendingDeletion { + if aws.StringValue(kOut.KeyMetadata.KeyState) == kms.KeyStatePendingDeletion { // Skip keys which are already scheduled for deletion continue } - _, err = conn.ScheduleKeyDeletion(&kms.ScheduleKeyDeletionInput{ - KeyId: k.KeyId, - PendingWindowInDays: aws.Int64(int64(7)), - }) + r := resourceAwsKmsKey() + d := r.Data(nil) + d.SetId(kKeyId) + d.Set("key_id", kKeyId) + d.Set("deletion_window_in_days", "7") + err = r.Delete(d, client) if err != nil { - log.Printf("Error: Failed to schedule key %q for deletion: %s", *k.KeyId, err) + log.Printf("Error: Failed to schedule key %q for deletion: %s", kKeyId, err) return false } } @@ -61,7 +64,7 @@ func testSweepKmsKeys(region string) error { log.Printf("[WARN] Skipping KMS Key sweep for %s: %s", region, err) return nil } - return fmt.Errorf("Error describing KMS keys: %s", err) + return fmt.Errorf("Error describing KMS keys: %w", err) } return nil From 8c6b5dab4c96229499fbbc6ec817e5637b416445 Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Fri, 25 Jun 2021 12:14:14 -0400 Subject: [PATCH 047/398] loop over Firewall Manager WebACLs --- aws/resource_aws_wafv2_web_acl_test.go | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/aws/resource_aws_wafv2_web_acl_test.go b/aws/resource_aws_wafv2_web_acl_test.go index d0de3847183..aa59aa5e35e 100644 --- a/aws/resource_aws_wafv2_web_acl_test.go +++ b/aws/resource_aws_wafv2_web_acl_test.go @@ -4,11 +4,11 @@ import ( "fmt" "log" "regexp" + "strings" "testing" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/wafv2" - "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -48,13 +48,23 @@ func testSweepWafv2WebAcls(region string) error { continue } + name := aws.StringValue(webAcl.Name) + + // Exclude WebACLs managed by Firewall Manager as deletion returns AccessDeniedException. + // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/19149 + // Prefix Reference: https://docs.aws.amazon.com/waf/latest/developerguide/get-started-fms-create-security-policy.html + if strings.HasPrefix(name, "FMManagedWebACLV2") { + log.Printf("[WARN] Skipping WAFv2 Web ACL: %s", name) + continue + } + id := aws.StringValue(webAcl.Id) r := resourceAwsWafv2WebACL() d := r.Data(nil) d.SetId(id) d.Set("lock_token", webAcl.LockToken) - d.Set("name", webAcl.Name) + d.Set("name", name) d.Set("scope", input.Scope) sweepResources = append(sweepResources, NewTestSweepResource(r, d, client)) @@ -67,16 +77,7 @@ func testSweepWafv2WebAcls(region string) error { errs = multierror.Append(errs, fmt.Errorf("error describing WAFv2 Web ACLs for %s: %w", region, err)) } - err = testSweepResourceOrchestrator(sweepResources) - - // Since we cannot exclude Firewall Manager WebACLs from the sweepResources var above, - // we instead catch and ignore the following expected AccessDeniedException. - // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/19149 - if tfawserr.ErrMessageContains(err, "AccessDeniedException", "managed by Firewall Manager") { - return errs.ErrorOrNil() - } - - if err != nil { + if err := testSweepResourceOrchestrator(sweepResources); err != nil { errs = multierror.Append(errs, fmt.Errorf("error sweeping WAFv2 Web ACLs for %s: %w", region, err)) } From dceb5a7c2cc875fa2d60680de17538f44ad6959f Mon Sep 17 00:00:00 2001 From: Tyler Lynch Date: Fri, 25 Jun 2021 13:15:40 -0400 Subject: [PATCH 048/398] Fixed duplicated code (DRY) --- ...esource_aws_storagegateway_gateway_test.go | 128 +++--------------- 1 file changed, 22 insertions(+), 106 deletions(-) diff --git a/aws/resource_aws_storagegateway_gateway_test.go b/aws/resource_aws_storagegateway_gateway_test.go index fefdb6fe544..36d46a9ee8b 100644 --- a/aws/resource_aws_storagegateway_gateway_test.go +++ b/aws/resource_aws_storagegateway_gateway_test.go @@ -1053,73 +1053,33 @@ resource "aws_storagegateway_gateway" "test" { `, rName) } -func testAccAWSStorageGatewayGatewayConfigSmbActiveDirectorySettingsBase(rName string) string { - return composeConfig( - // Reference: https://docs.aws.amazon.com/storagegateway/latest/userguide/Requirements.html - testAccAvailableEc2InstanceTypeForAvailabilityZone("aws_subnet.test[0].availability_zone", "m5.xlarge", "m4.xlarge"), - testAccAvailableAZsNoOptInConfig(), - fmt.Sprintf(` -# Directory Service Directories must be deployed across multiple EC2 Availability Zones -resource "aws_vpc" "test" { - cidr_block = "10.0.0.0/16" +func testAccAWSStorageGatewayGatewayConfig_DirectoryServiceSimpleDirectory(rName string) string { + return fmt.Sprintf(` +resource "aws_directory_service_directory" "test" { + name = "terraformtesting.com" + password = "SuperSecretPassw0rd" + size = "Small" - tags = { - Name = %[1]q + vpc_settings { + subnet_ids = aws_subnet.test[*].id + vpc_id = aws_vpc.test.id } -} - -resource "aws_subnet" "test" { - count = 2 - - availability_zone = data.aws_availability_zones.available.names[count.index] - cidr_block = "10.0.${count.index}.0/24" - vpc_id = aws_vpc.test.id tags = { Name = %[1]q } } -resource "aws_internet_gateway" "test" { - vpc_id = aws_vpc.test.id - - tags = { - Name = %[1]q - } -} - -resource "aws_route" "test" { - destination_cidr_block = "0.0.0.0/0" - gateway_id = aws_internet_gateway.test.id - route_table_id = aws_vpc.test.main_route_table_id -} - -resource "aws_security_group" "test" { - vpc_id = aws_vpc.test.id - - egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] - } - - ingress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] - } - - tags = { - Name = %[1]q - } +`, rName) } +func testAccAWSStorageGatewayGatewayConfig_DirectoryServiceMicrosoftAD(rName string) string { + return fmt.Sprintf(` resource "aws_directory_service_directory" "test" { + edition = "Standard" name = "terraformtesting.com" password = "SuperSecretPassw0rd" - size = "Small" + type = "MicrosoftAD" vpc_settings { subnet_ids = aws_subnet.test[*].id @@ -1131,42 +1091,10 @@ resource "aws_directory_service_directory" "test" { } } -resource "aws_vpc_dhcp_options" "test" { - domain_name = aws_directory_service_directory.test.name - domain_name_servers = aws_directory_service_directory.test.dns_ip_addresses - - tags = { - Name = %[1]q - } -} - -resource "aws_vpc_dhcp_options_association" "test" { - dhcp_options_id = aws_vpc_dhcp_options.test.id - vpc_id = aws_vpc.test.id -} - -# Reference: https://docs.aws.amazon.com/storagegateway/latest/userguide/ec2-gateway-file.html -data "aws_ssm_parameter" "aws_service_storagegateway_ami_FILE_S3_latest" { - name = "/aws/service/storagegateway/ami/FILE_S3/latest" -} - -resource "aws_instance" "test" { - depends_on = [aws_route.test, aws_vpc_dhcp_options_association.test] - - ami = data.aws_ssm_parameter.aws_service_storagegateway_ami_FILE_S3_latest.value - associate_public_ip_address = true - instance_type = data.aws_ec2_instance_type_offering.available.instance_type - vpc_security_group_ids = [aws_security_group.test.id] - subnet_id = aws_subnet.test[0].id - - tags = { - Name = %[1]q - } -} -`, rName)) +`, rName) } -func testAccAWSStorageGatewayGatewayConfigSmbMicrosoftActiveDirectorySettingsBase(rName string) string { +func testAccAWSStorageGatewayGatewayConfigSmbActiveDirectorySettingsBase(rName string) string { return composeConfig( // Reference: https://docs.aws.amazon.com/storagegateway/latest/userguide/Requirements.html testAccAvailableEc2InstanceTypeForAvailabilityZone("aws_subnet.test[0].availability_zone", "m5.xlarge", "m4.xlarge"), @@ -1229,22 +1157,6 @@ resource "aws_security_group" "test" { } } -resource "aws_directory_service_directory" "test" { - edition = "Standard" - name = "terraformtesting.com" - password = "SuperSecretPassw0rd" - type = "MicrosoftAD" - - vpc_settings { - subnet_ids = aws_subnet.test[*].id - vpc_id = aws_vpc.test.id - } - - tags = { - Name = %[1]q - } -} - resource "aws_vpc_dhcp_options" "test" { domain_name = aws_directory_service_directory.test.name domain_name_servers = aws_directory_service_directory.test.dns_ip_addresses @@ -1283,6 +1195,7 @@ resource "aws_instance" "test" { func testAccAWSStorageGatewayGatewayConfig_SmbActiveDirectorySettings(rName string) string { return composeConfig( testAccAWSStorageGatewayGatewayConfigSmbActiveDirectorySettingsBase(rName), + testAccAWSStorageGatewayGatewayConfig_DirectoryServiceSimpleDirectory(rName), fmt.Sprintf(` resource "aws_storagegateway_gateway" "test" { gateway_ip_address = aws_instance.test.public_ip @@ -1302,6 +1215,7 @@ resource "aws_storagegateway_gateway" "test" { func testAccAWSStorageGatewayGatewayConfig_SmbActiveDirectorySettingsTimeout(rName string, timeout int) string { return composeConfig( testAccAWSStorageGatewayGatewayConfigSmbActiveDirectorySettingsBase(rName), + testAccAWSStorageGatewayGatewayConfig_DirectoryServiceSimpleDirectory(rName), fmt.Sprintf(` resource "aws_storagegateway_gateway" "test" { gateway_ip_address = aws_instance.test.public_ip @@ -1321,7 +1235,8 @@ resource "aws_storagegateway_gateway" "test" { func testAccAWSStorageGatewayGatewayConfig_SmbMicrosoftActiveDirectorySettings(rName string) string { return composeConfig( - testAccAWSStorageGatewayGatewayConfigSmbMicrosoftActiveDirectorySettingsBase(rName), + testAccAWSStorageGatewayGatewayConfigSmbActiveDirectorySettingsBase(rName), + testAccAWSStorageGatewayGatewayConfig_DirectoryServiceMicrosoftAD(rName), fmt.Sprintf(` resource "aws_storagegateway_gateway" "test" { gateway_ip_address = aws_instance.test.public_ip @@ -1340,7 +1255,8 @@ resource "aws_storagegateway_gateway" "test" { func testAccAWSStorageGatewayGatewayConfig_SmbMicrosoftActiveDirectorySettingsTimeout(rName string, timeout int) string { return composeConfig( - testAccAWSStorageGatewayGatewayConfigSmbMicrosoftActiveDirectorySettingsBase(rName), + testAccAWSStorageGatewayGatewayConfigSmbActiveDirectorySettingsBase(rName), + testAccAWSStorageGatewayGatewayConfig_DirectoryServiceMicrosoftAD(rName), fmt.Sprintf(` resource "aws_storagegateway_gateway" "test" { gateway_ip_address = aws_instance.test.public_ip From d9aab916290f3f28763f8ef0465f8e3e8eb57dd5 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Sun, 4 Apr 2021 12:43:17 +0300 Subject: [PATCH 049/398] retention policy for efs file system --- aws/resource_aws_sagemaker_app_test.go | 4 + aws/resource_aws_sagemaker_domain.go | 38 +++++- aws/resource_aws_sagemaker_domain_test.go | 118 +++++++++++++----- ...esource_aws_sagemaker_user_profile_test.go | 4 + website/docs/r/sagemaker_domain.html.markdown | 5 + 5 files changed, 136 insertions(+), 33 deletions(-) diff --git a/aws/resource_aws_sagemaker_app_test.go b/aws/resource_aws_sagemaker_app_test.go index 5870e50db72..0cf5787350e 100644 --- a/aws/resource_aws_sagemaker_app_test.go +++ b/aws/resource_aws_sagemaker_app_test.go @@ -318,6 +318,10 @@ resource "aws_sagemaker_domain" "test" { default_user_settings { execution_role = aws_iam_role.test.arn } + + retention_policy { + home_efs_file_system = "Delete" + } } resource "aws_sagemaker_user_profile" "test" { diff --git a/aws/resource_aws_sagemaker_domain.go b/aws/resource_aws_sagemaker_domain.go index 4ca348eb607..175d35b15bb 100644 --- a/aws/resource_aws_sagemaker_domain.go +++ b/aws/resource_aws_sagemaker_domain.go @@ -226,6 +226,22 @@ func resourceAwsSagemakerDomain() *schema.Resource { }, "tags": tagsSchema(), "tags_all": tagsSchemaComputed(), + "retention_policy": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "home_efs_file_system": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(sagemaker.RetentionType_Values(), false), + Default: sagemaker.RetentionTypeRetain, + }, + }, + }, + }, "url": { Type: schema.TypeString, Computed: true, @@ -377,9 +393,10 @@ func resourceAwsSagemakerDomainDelete(d *schema.ResourceData, meta interface{}) input := &sagemaker.DeleteDomainInput{ DomainId: aws.String(d.Id()), - RetentionPolicy: &sagemaker.RetentionPolicy{ - HomeEfsFileSystem: aws.String(sagemaker.RetentionTypeDelete), - }, + } + + if v, ok := d.GetOk("retention_policy"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.RetentionPolicy = expandSagemakerRetentionPolicy(v.([]interface{})) } if _, err := conn.DeleteDomain(input); err != nil { @@ -396,6 +413,21 @@ func resourceAwsSagemakerDomainDelete(d *schema.ResourceData, meta interface{}) return nil } +func expandSagemakerRetentionPolicy(l []interface{}) *sagemaker.RetentionPolicy { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + config := &sagemaker.RetentionPolicy{} + + if v, ok := m["home_efs_file_system"].(string); ok && v != "" { + config.HomeEfsFileSystem = aws.String(v) + } + + return config +} func expandSagemakerDomainDefaultUserSettings(l []interface{}) *sagemaker.UserSettings { if len(l) == 0 || l[0] == nil { diff --git a/aws/resource_aws_sagemaker_domain_test.go b/aws/resource_aws_sagemaker_domain_test.go index 793e7949726..fa54d8718ec 100644 --- a/aws/resource_aws_sagemaker_domain_test.go +++ b/aws/resource_aws_sagemaker_domain_test.go @@ -142,9 +142,10 @@ func testAccAWSSagemakerDomain_basic(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"retention_policy"}, }, }, }) @@ -169,9 +170,10 @@ func testAccAWSSagemakerDomain_kms(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"retention_policy"}, }, }, }) @@ -197,9 +199,10 @@ func testAccAWSSagemakerDomain_tags(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"retention_policy"}, }, { Config: testAccAWSSagemakerDomainConfigTags2(rName, "key1", "value1updated", "key2", "value2"), @@ -242,9 +245,10 @@ func testAccAWSSagemakerDomain_securityGroup(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"retention_policy"}, }, { Config: testAccAWSSagemakerDomainConfigSecurityGroup2(rName), @@ -281,9 +285,10 @@ func testAccAWSSagemakerDomain_sharingSettings(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"retention_policy"}, }, }, }) @@ -311,9 +316,10 @@ func testAccAWSSagemakerDomain_tensorboardAppSettings(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"retention_policy"}, }, }, }) @@ -342,9 +348,10 @@ func testAccAWSSagemakerDomain_tensorboardAppSettingsWithImage(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"retention_policy"}, }, }, }) @@ -372,9 +379,10 @@ func testAccAWSSagemakerDomain_kernelGatewayAppSettings(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"retention_policy"}, }, }, }) @@ -410,9 +418,10 @@ func testAccAWSSagemakerDomain_kernelGatewayAppSettings_customImage(t *testing.T ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"retention_policy"}, }, }, }) @@ -440,9 +449,10 @@ func testAccAWSSagemakerDomain_jupyterServerAppSettings(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"retention_policy"}, }, }, }) @@ -582,6 +592,10 @@ resource "aws_sagemaker_domain" "test" { default_user_settings { execution_role = aws_iam_role.test.arn } + + retention_policy { + home_efs_file_system = "Delete" + } } `, rName) } @@ -603,6 +617,10 @@ resource "aws_sagemaker_domain" "test" { default_user_settings { execution_role = aws_iam_role.test.arn } + + retention_policy { + home_efs_file_system = "Delete" + } } `, rName) } @@ -623,6 +641,10 @@ resource "aws_sagemaker_domain" "test" { execution_role = aws_iam_role.test.arn security_groups = [aws_security_group.test.id] } + + retention_policy { + home_efs_file_system = "Delete" + } } `, rName) } @@ -647,6 +669,10 @@ resource "aws_sagemaker_domain" "test" { execution_role = aws_iam_role.test.arn security_groups = [aws_security_group.test.id, aws_security_group.test2.id] } + + retention_policy { + home_efs_file_system = "Delete" + } } `, rName) } @@ -663,6 +689,10 @@ resource "aws_sagemaker_domain" "test" { execution_role = aws_iam_role.test.arn } + retention_policy { + home_efs_file_system = "Delete" + } + tags = { %[2]q = %[3]q } @@ -682,6 +712,10 @@ resource "aws_sagemaker_domain" "test" { execution_role = aws_iam_role.test.arn } + retention_policy { + home_efs_file_system = "Delete" + } + tags = { %[2]q = %[3]q %[4]q = %[5]q @@ -718,6 +752,10 @@ resource "aws_sagemaker_domain" "test" { s3_output_path = "s3://${aws_s3_bucket.test.bucket}/sharing" } } + + retention_policy { + home_efs_file_system = "Delete" + } } `, rName) } @@ -739,6 +777,10 @@ resource "aws_sagemaker_domain" "test" { } } } + + retention_policy { + home_efs_file_system = "Delete" + } } `, rName) } @@ -766,6 +808,10 @@ resource "aws_sagemaker_domain" "test" { } } } + + retention_policy { + home_efs_file_system = "Delete" + } } `, rName) } @@ -787,6 +833,10 @@ resource "aws_sagemaker_domain" "test" { } } } + + retention_policy { + home_efs_file_system = "Delete" + } } `, rName) } @@ -808,6 +858,10 @@ resource "aws_sagemaker_domain" "test" { } } } + + retention_policy { + home_efs_file_system = "Delete" + } } `, rName) } @@ -852,6 +906,10 @@ resource "aws_sagemaker_domain" "test" { } } } + + retention_policy { + home_efs_file_system = "Delete" + } } `, rName, baseImage) } diff --git a/aws/resource_aws_sagemaker_user_profile_test.go b/aws/resource_aws_sagemaker_user_profile_test.go index 1aa633c7923..eb5b2f10d1a 100644 --- a/aws/resource_aws_sagemaker_user_profile_test.go +++ b/aws/resource_aws_sagemaker_user_profile_test.go @@ -389,6 +389,10 @@ resource "aws_sagemaker_domain" "test" { default_user_settings { execution_role = aws_iam_role.test.arn } + + retention_policy { + home_efs_file_system = "Delete" + } } `, rName) } diff --git a/website/docs/r/sagemaker_domain.html.markdown b/website/docs/r/sagemaker_domain.html.markdown index 2bd8a25252d..af437b6ce21 100644 --- a/website/docs/r/sagemaker_domain.html.markdown +++ b/website/docs/r/sagemaker_domain.html.markdown @@ -95,6 +95,7 @@ The following arguments are supported: * `vpc_id` - (Required) The ID of the Amazon Virtual Private Cloud (VPC) that Studio uses for communication. * `subnet_ids` - (Required) The VPC subnets that Studio uses for communication. * `default_user_settings` - (Required) The default user settings. See [Default User Settings](#default-user-settings) below. +* `retention_policy` - (Optional) The retention policy for this domain, which specifies whether resources will be retained after the Domain is deleted. By default, all resources are retained. See [Retention Policy](#retention-policy) below. * `kms_key_id` - (Optional) The AWS KMS customer managed CMK used to encrypt the EFS volume attached to the domain. * `app_network_access_type` - (Optional) Specifies the VPC used for non-EFS traffic. The default value is `PublicInternetOnly`. Valid values are `PublicInternetOnly` and `VpcOnly`. * `tags` - (Optional) A map of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](/docs/providers/aws/index.html#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. @@ -138,6 +139,10 @@ The following arguments are supported: * `image_name` - (Required) The name of the Custom Image. * `image_version_number` - (Optional) The version number of the Custom Image. +### Retention Policy + +* `home_efs_file_system` - (Optional) The retention policy for data stored on an Amazon Elastic File System (EFS) volume. Default value is `Retain`. + ## Attributes Reference In addition to all arguments above, the following attributes are exported: From dac2336620914bb219726cd8d965398709e88bb0 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Sun, 4 Apr 2021 12:54:17 +0300 Subject: [PATCH 050/398] changelog --- .changelog/18562.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/18562.txt diff --git a/.changelog/18562.txt b/.changelog/18562.txt new file mode 100644 index 00000000000..7d25f37b7a3 --- /dev/null +++ b/.changelog/18562.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_sagemaker_domain: Add support for `retention_policy` +``` \ No newline at end of file From 1c21d36013e7076339f3873fd1fee6a578820e13 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Sun, 4 Apr 2021 13:02:09 +0300 Subject: [PATCH 051/398] lint --- aws/resource_aws_sagemaker_domain_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aws/resource_aws_sagemaker_domain_test.go b/aws/resource_aws_sagemaker_domain_test.go index fa54d8718ec..feb635451eb 100644 --- a/aws/resource_aws_sagemaker_domain_test.go +++ b/aws/resource_aws_sagemaker_domain_test.go @@ -620,7 +620,7 @@ resource "aws_sagemaker_domain" "test" { retention_policy { home_efs_file_system = "Delete" - } + } } `, rName) } @@ -644,7 +644,7 @@ resource "aws_sagemaker_domain" "test" { retention_policy { home_efs_file_system = "Delete" - } + } } `, rName) } @@ -672,7 +672,7 @@ resource "aws_sagemaker_domain" "test" { retention_policy { home_efs_file_system = "Delete" - } + } } `, rName) } From 4b0ec2639709534f4ab106af3385f95b132de83b Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Sun, 27 Jun 2021 08:48:16 +0300 Subject: [PATCH 052/398] add ecs params --- aws/resource_aws_cloudwatch_event_target.go | 140 +++++++++++++++++++- 1 file changed, 133 insertions(+), 7 deletions(-) diff --git a/aws/resource_aws_cloudwatch_event_target.go b/aws/resource_aws_cloudwatch_event_target.go index 122ffbf7f8a..b9c8722ead3 100644 --- a/aws/resource_aws_cloudwatch_event_target.go +++ b/aws/resource_aws_cloudwatch_event_target.go @@ -12,6 +12,7 @@ import ( "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" tfevents "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/cloudwatchevents" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/cloudwatchevents/finder" ) @@ -137,6 +138,16 @@ func resourceAwsCloudWatchEventTarget() *schema.Resource { MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ + "enable_ecs_managed_tags": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "enable_execute_command": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, "group": { Type: schema.TypeString, Optional: true, @@ -175,11 +186,37 @@ func resourceAwsCloudWatchEventTarget() *schema.Resource { }, }, }, + "placement_constraints": { + Type: schema.TypeSet, + Optional: true, + MaxItems: 10, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "expression": { + Type: schema.TypeString, + Optional: true, + }, + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(events.PlacementConstraintType_Values(), false), + }, + }, + }, + }, "platform_version": { Type: schema.TypeString, Optional: true, ValidateFunc: validation.StringLenBetween(0, 1600), }, + "propagate_tags": { + Type: schema.TypeString, + Optional: true, + Default: events.PropagateTagsTaskDefinition, + ValidateFunc: validation.StringInSlice(events.PropagateTags_Values(), false), + }, + "tags": tagsSchema(), + "tags_all": tagsSchemaComputed(), "task_count": { Type: schema.TypeInt, Optional: true, @@ -331,7 +368,7 @@ func resourceAwsCloudWatchEventTargetCreate(d *schema.ResourceData, meta interfa busName = v.(string) } - input := buildPutTargetInputStruct(d) + input := buildPutTargetInputStruct(d, meta) log.Printf("[DEBUG] Creating CloudWatch Events Target: %s", input) out, err := conn.PutTargets(input) @@ -391,7 +428,7 @@ func resourceAwsCloudWatchEventTargetRead(d *schema.ResourceData, meta interface } if t.EcsParameters != nil { - if err := d.Set("ecs_target", flattenAwsCloudWatchEventTargetEcsParameters(t.EcsParameters)); err != nil { + if err := d.Set("ecs_target", flattenAwsCloudWatchEventTargetEcsParameters(t.EcsParameters, meta)); err != nil { return fmt.Errorf("Error setting ecs_target error: %w", err) } } @@ -438,7 +475,7 @@ func resourceAwsCloudWatchEventTargetRead(d *schema.ResourceData, meta interface func resourceAwsCloudWatchEventTargetUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).cloudwatcheventsconn - input := buildPutTargetInputStruct(d) + input := buildPutTargetInputStruct(d, meta) log.Printf("[DEBUG] Updating CloudWatch Events Target: %s", input) _, err := conn.PutTargets(input) @@ -477,7 +514,7 @@ func resourceAwsCloudWatchEventTargetDelete(d *schema.ResourceData, meta interfa return nil } -func buildPutTargetInputStruct(d *schema.ResourceData) *events.PutTargetsInput { +func buildPutTargetInputStruct(d *schema.ResourceData, meta interface{}) *events.PutTargetsInput { e := &events.Target{ Arn: aws.String(d.Get("arn").(string)), Id: aws.String(d.Get("target_id").(string)), @@ -499,7 +536,7 @@ func buildPutTargetInputStruct(d *schema.ResourceData) *events.PutTargetsInput { } if v, ok := d.GetOk("ecs_target"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - e.EcsParameters = expandAwsCloudWatchEventTargetEcsParameters(v.([]interface{})) + e.EcsParameters = expandAwsCloudWatchEventTargetEcsParameters(v.([]interface{}), meta) } if v, ok := d.GetOk("http_target"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { @@ -559,22 +596,44 @@ func expandAwsCloudWatchEventTargetRunParameters(config []interface{}) *events.R return command } -func expandAwsCloudWatchEventTargetEcsParameters(config []interface{}) *events.EcsParameters { +func expandAwsCloudWatchEventTargetEcsParameters(config []interface{}, meta interface{}) *events.EcsParameters { + ecsParameters := &events.EcsParameters{} for _, c := range config { param := c.(map[string]interface{}) + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig + tags := defaultTagsConfig.MergeTags(keyvaluetags.New(param["tags"].(map[string]interface{}))) + if val, ok := param["group"].(string); ok && val != "" { ecsParameters.Group = aws.String(val) } + if val, ok := param["launch_type"].(string); ok && val != "" { ecsParameters.LaunchType = aws.String(val) } + if val, ok := param["network_configuration"]; ok { ecsParameters.NetworkConfiguration = expandAwsCloudWatchEventTargetEcsParametersNetworkConfiguration(val.([]interface{})) } + if val, ok := param["platform_version"].(string); ok && val != "" { ecsParameters.PlatformVersion = aws.String(val) } + + if v, ok := param["placement_constraints"].(*schema.Set); ok && v.Len() > 0 { + ecsParameters.PlacementConstraints = expandAwsCloudWatchEventTargetPlacementConstraints(v.List()) + } + + if v, ok := param["propagate_tags"].(string); ok { + ecsParameters.PropagateTags = aws.String(v) + } + + if len(tags) > 0 { + ecsParameters.Tags = tags.IgnoreAws().CloudwatcheventsTags() + } + + ecsParameters.EnableExecuteCommand = aws.Bool(param["enable_execute_command"].(bool)) + ecsParameters.EnableECSManagedTags = aws.Bool(param["enable_ecs_managed_tags"].(bool)) ecsParameters.TaskCount = aws.Int64(int64(param["task_count"].(int))) ecsParameters.TaskDefinitionArn = aws.String(param["task_definition_arn"].(string)) } @@ -735,18 +794,38 @@ func flattenAwsCloudWatchEventTargetRunParameters(runCommand *events.RunCommandP return result } -func flattenAwsCloudWatchEventTargetEcsParameters(ecsParameters *events.EcsParameters) []map[string]interface{} { +func flattenAwsCloudWatchEventTargetEcsParameters(ecsParameters *events.EcsParameters, meta interface{}) []map[string]interface{} { + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + config := make(map[string]interface{}) if ecsParameters.Group != nil { config["group"] = aws.StringValue(ecsParameters.Group) } + if ecsParameters.LaunchType != nil { config["launch_type"] = aws.StringValue(ecsParameters.LaunchType) } + config["network_configuration"] = flattenAwsCloudWatchEventTargetEcsParametersNetworkConfiguration(ecsParameters.NetworkConfiguration) if ecsParameters.PlatformVersion != nil { config["platform_version"] = aws.StringValue(ecsParameters.PlatformVersion) } + + if ecsParameters.PropagateTags != nil { + config["propagate_tags"] = aws.StringValue(ecsParameters.PropagateTags) + } + + if ecsParameters.PlacementConstraints != nil { + config["placement_constraints"] = flattenAwsCloudWatchEventTargetPlacementConstraints(ecsParameters.PlacementConstraints) + } + + tags := keyvaluetags.CloudwatcheventsKeyValueTags(ecsParameters.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig) + config["tags"] = tags.RemoveDefaultConfig(defaultTagsConfig).Map() + config["tags_all"] = tags.Map() + + config["enable_execute_command"] = aws.BoolValue(ecsParameters.EnableExecuteCommand) + config["enable_ecs_managed_tags"] = aws.BoolValue(ecsParameters.EnableECSManagedTags) config["task_count"] = aws.Int64Value(ecsParameters.TaskCount) config["task_definition_arn"] = aws.StringValue(ecsParameters.TaskDefinitionArn) result := []map[string]interface{}{config} @@ -851,6 +930,53 @@ func flatternAwsCloudWatchEventTargetDeadLetterConfig(dlc *events.DeadLetterConf return result } +func expandAwsCloudWatchEventTargetPlacementConstraints(tfList []interface{}) []*events.PlacementConstraint { + if len(tfList) == 0 { + return nil + } + + var result []*events.PlacementConstraint + + for _, tfMapRaw := range tfList { + if tfMapRaw == nil { + continue + } + + tfMap := tfMapRaw.(map[string]interface{}) + + apiObject := &events.PlacementConstraint{} + + if v, ok := tfMap["expression"].(string); ok && v != "" { + apiObject.Expression = aws.String(v) + } + + if v, ok := tfMap["type"].(string); ok && v != "" { + apiObject.Type = aws.String(v) + } + + result = append(result, apiObject) + } + + return result +} + +func flattenAwsCloudWatchEventTargetPlacementConstraints(pcs []*events.PlacementConstraint) []map[string]interface{} { + if len(pcs) == 0 { + return nil + } + results := make([]map[string]interface{}, 0) + for _, pc := range pcs { + c := make(map[string]interface{}) + c["type"] = aws.StringValue(pc.Type) + if pc.Expression != nil { + c["expression"] = aws.StringValue(pc.Expression) + } + + results = append(results, c) + } + return results +} + func resourceAwsCloudWatchEventTargetImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { busName, ruleName, targetID, err := tfevents.TargetParseImportID(d.Id()) if err != nil { From 5d9a48438f315c94790b647e386837556100d639 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Sun, 27 Jun 2021 11:19:44 +0300 Subject: [PATCH 053/398] docs --- .../r/cloudwatch_event_target.html.markdown | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/website/docs/r/cloudwatch_event_target.html.markdown b/website/docs/r/cloudwatch_event_target.html.markdown index 9e97e856faf..7e32f1e4c03 100644 --- a/website/docs/r/cloudwatch_event_target.html.markdown +++ b/website/docs/r/cloudwatch_event_target.html.markdown @@ -344,12 +344,12 @@ The following arguments are supported: * `retry_policy` - (Optional) Parameters used when you are providing retry policies. Documented below. A maximum of 1 are allowed. * `dead_letter_config` - (Optional) Parameters used when you are providing a dead letter config. Documented below. A maximum of 1 are allowed. -`run_command_targets` support the following: +### run_command_targets * `key` - (Required) Can be either `tag:tag-key` or `InstanceIds`. * `values` - (Required) If Key is `tag:tag-key`, Values is a list of tag values. If Key is `InstanceIds`, Values is a list of Amazon EC2 instance IDs. -`ecs_target` support the following: +### ecs_target * `group` - (Optional) Specifies an ECS task group for the task. The maximum length is 255 characters. * `launch_type` - (Optional) Specifies the launch type on which your task is running. The launch type that you specify here must match one of the launch type (compatibilities) of the target task. Valid values include: an empty string `""` (to specify no launch type), `EC2`, or `FARGATE`. @@ -357,8 +357,13 @@ The following arguments are supported: * `platform_version` - (Optional) Specifies the platform version for the task. Specify only the numeric portion of the platform version, such as 1.1.0. This is used only if LaunchType is FARGATE. For more information about valid platform versions, see [AWS Fargate Platform Versions](http://docs.aws.amazon.com/AmazonECS/latest/developerguide/platform_versions.html). * `task_count` - (Optional) The number of tasks to create based on the TaskDefinition. The default is 1. * `task_definition_arn` - (Required) The ARN of the task definition to use if the event target is an Amazon ECS cluster. +* `tags` - (Optional) A map of tags to assign to ecs resources. If configured with a provider [`default_tags` configuration block](/docs/providers/aws/index.html#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. +* `propagate_tags` - (Optional) Specifies whether to propagate the tags from the task definition to the task. If no value is specified, the tags are not propagated. Tags can only be propagated to the task during task creation. +* `placement_constraints` - (Optional) An array of placement constraint objects to use for the task. You can specify up to 10 constraints per task (including constraints in the task definition and those specified at runtime). See Below. +* `enable_execute_command` - (Optional) Whether or not to enable the execute command functionality for the containers in this task. If true, this enables execute command functionality on all containers in the task. +* `enable_ecs_managed_tags` - (Optional) Specifies whether to enable Amazon ECS managed tags for the task. -`network_configuration` support the following: +#### network_configuration * `subnets` - (Required) The subnets associated with the task or service. * `security_groups` - (Optional) The security groups associated with the task or service. If you do not specify a security group, the default security group for the VPC is used. @@ -366,18 +371,23 @@ The following arguments are supported: For more information, see [Task Networking](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-networking.html) -`batch_target` support the following: +#### placement_constraints + +* `type` - (Required) Type of constraint. The only valid values at this time are `memberOf` and `distinctInstance`. +* `expression` - (Optional) Cluster Query Language expression to apply to the constraint. Does not need to be specified for the `distinctInstance` type. For more information, see [Cluster Query Language in the Amazon EC2 Container Service Developer Guide](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/cluster-query-language.html). + +### batch_target * `job_definition` - (Required) The ARN or name of the job definition to use if the event target is an AWS Batch job. This job definition must already exist. * `job_name` - (Required) The name to use for this execution of the job, if the target is an AWS Batch job. * `array_size` - (Optional) The size of the array, if this is an array batch job. Valid values are integers between 2 and 10,000. * `job_attempts` - (Optional) The number of times to attempt to retry, if the job fails. Valid values are 1 to 10. -`kinesis_target` support the following: +### kinesis_target * `partition_key_path` - (Optional) The JSON path to be extracted from the event and used as the partition key. -`sqs_target` support the following: +### sqs_target * `message_group_id` - (Optional) The FIFO message group ID to use as the target. @@ -387,7 +397,7 @@ For more information, see [Task Networking](https://docs.aws.amazon.com/AmazonEC * `query_string_parameters` - (Optional) Represents keys/values of query string parameters that are appended to the invoked endpoint. * `header_parameters` - (Optional) Enables you to specify HTTP headers to add to the request. -`input_transformer` support the following: +### input_transformer * `input_paths` - (Optional) Key value pairs specified in the form of JSONPath (for example, time = $.time) * You can have as many as 100 key-value pairs. @@ -396,12 +406,12 @@ For more information, see [Task Networking](https://docs.aws.amazon.com/AmazonEC * `input_template` - (Required) Template to customize data sent to the target. Must be valid JSON. To send a string value, the string value must include double quotes. Values must be escaped for both JSON and Terraform, e.g. `"\"Your string goes here.\\nA new line.\""` -`retry_policy` support the following: +### retry_policy * `maximum_event_age_in_seconds` - (Optional) The age in seconds to continue to make retry attempts. * `maximum_retry_attempts` - (Optional) maximum number of retry attempts to make before the request fails -`dead_letter_config` support the following: +### dead_letter_config * `arn` - (Optional) - ARN of the SQS queue specified as the target for the dead-letter queue. From 2f7d26ebd9081d17385685ca5eb1ffc70940a476 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Sun, 27 Jun 2021 11:25:21 +0300 Subject: [PATCH 054/398] changelog --- .changelog/19975.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/19975.txt diff --git a/.changelog/19975.txt b/.changelog/19975.txt new file mode 100644 index 00000000000..af62320796d --- /dev/null +++ b/.changelog/19975.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_cloudwatch_event_target: Add `enable_ecs_managed_tags`, `enable_execute_command`, `placement_constraints`, `propagate_tags`, and `tags` arguments to `ecs_target` block. +``` \ No newline at end of file From e6d217e3ed935bcfab4da277690f7a7cb7d7f197 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Sun, 27 Jun 2021 14:13:25 +0300 Subject: [PATCH 055/398] test --- ...source_aws_cloudwatch_event_target_test.go | 261 ++++++------------ 1 file changed, 82 insertions(+), 179 deletions(-) diff --git a/aws/resource_aws_cloudwatch_event_target_test.go b/aws/resource_aws_cloudwatch_event_target_test.go index c218afcbeaf..2426e09e423 100644 --- a/aws/resource_aws_cloudwatch_event_target_test.go +++ b/aws/resource_aws_cloudwatch_event_target_test.go @@ -536,6 +536,43 @@ func TestAccAWSCloudWatchEventTarget_ecsWithBlankTaskCount(t *testing.T) { }) } +func TestAccAWSCloudWatchEventTarget_ecsFull(t *testing.T) { + resourceName := "aws_cloudwatch_event_target.test" + var v events.Target + rName := acctest.RandomWithPrefix("tf_ecs_target") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, events.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCloudWatchEventTargetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCloudWatchEventTargetConfigEcsWithBlankTaskCountFull(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudWatchEventTargetExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "ecs_target.#", "1"), + resource.TestCheckResourceAttr(resourceName, "ecs_target.0.task_count", "1"), + resource.TestCheckResourceAttr(resourceName, "ecs_target.0.launch_type", "FARGATE"), + resource.TestCheckResourceAttr(resourceName, "ecs_target.0.enable_execute_command", "true"), + resource.TestCheckResourceAttr(resourceName, "ecs_target.0.enable_ecs_managed_tags", "true"), + resource.TestCheckResourceAttr(resourceName, "ecs_target.0.propagate_tags", "TASK_DEFINITION"), + resource.TestCheckResourceAttr(resourceName, "ecs_target.0.placement_constraints.#", "1"), + resource.TestCheckResourceAttr(resourceName, "ecs_target.0.placement_constraints.0.type", "distinctInstance"), + resource.TestCheckResourceAttr(resourceName, "ecs_target.0.tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "ecs_target.0.tags.test", "test1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: testAccAWSCloudWatchEventTargetImportStateIdFunc(resourceName), + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAWSCloudWatchEventTarget_batch(t *testing.T) { resourceName := "aws_cloudwatch_event_target.test" batchJobDefinitionResourceName := "aws_batch_job_definition.test" @@ -1212,15 +1249,8 @@ data "aws_partition" "current" {} `, rName) } -func testAccAWSCloudWatchEventTargetConfigEcs(rName string) string { +func testAccAWSCloudWatchEventTargetConfigEcsBase(rName string) string { return fmt.Sprintf(` -resource "aws_cloudwatch_event_rule" "test" { - name = %[1]q - description = "schedule_ecs_test" - - schedule_expression = "rate(5 minutes)" -} - resource "aws_vpc" "vpc" { cidr_block = "10.1.0.0/16" } @@ -1230,22 +1260,6 @@ resource "aws_subnet" "subnet" { cidr_block = "10.1.1.0/24" } -resource "aws_cloudwatch_event_target" "test" { - arn = aws_ecs_cluster.test.id - rule = aws_cloudwatch_event_rule.test.id - role_arn = aws_iam_role.test.arn - - ecs_target { - task_count = 1 - task_definition_arn = aws_ecs_task_definition.task.arn - launch_type = "FARGATE" - - network_configuration { - subnets = [aws_subnet.subnet.id] - } - } -} - resource "aws_iam_role" "test" { name = %[1]q @@ -1313,27 +1327,18 @@ EOF } data "aws_partition" "current" {} -`, rName) -} -func testAccAWSCloudWatchEventTargetConfigEcsWithBlankLaunchType(rName string) string { - return fmt.Sprintf(` resource "aws_cloudwatch_event_rule" "test" { name = %[1]q description = "schedule_ecs_test" schedule_expression = "rate(5 minutes)" } - -resource "aws_vpc" "vpc" { - cidr_block = "10.1.0.0/16" -} - -resource "aws_subnet" "subnet" { - vpc_id = aws_vpc.vpc.id - cidr_block = "10.1.1.0/24" +`, rName) } +func testAccAWSCloudWatchEventTargetConfigEcs(rName string) string { + return testAccAWSCloudWatchEventTargetConfigEcsBase(rName) + ` resource "aws_cloudwatch_event_target" "test" { arn = aws_ecs_cluster.test.id rule = aws_cloudwatch_event_rule.test.id @@ -1342,102 +1347,38 @@ resource "aws_cloudwatch_event_target" "test" { ecs_target { task_count = 1 task_definition_arn = aws_ecs_task_definition.task.arn - launch_type = "" + launch_type = "FARGATE" network_configuration { subnets = [aws_subnet.subnet.id] } } } - -resource "aws_iam_role" "test" { - name = %[1]q - - assume_role_policy = < Date: Sun, 27 Jun 2021 14:19:35 +0300 Subject: [PATCH 056/398] fmt --- aws/resource_aws_cloudwatch_event_target_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_cloudwatch_event_target_test.go b/aws/resource_aws_cloudwatch_event_target_test.go index 2426e09e423..74dcace445e 100644 --- a/aws/resource_aws_cloudwatch_event_target_test.go +++ b/aws/resource_aws_cloudwatch_event_target_test.go @@ -1414,9 +1414,9 @@ resource "aws_cloudwatch_event_target" "test" { type = "distinctInstance" } - tags = { + tags = { test = "test1" - } + } network_configuration { subnets = [aws_subnet.subnet.id] From c2f2b1dc016901982f9f1d8b9ce086f7acd2b442 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 28 Jun 2021 12:11:22 -0400 Subject: [PATCH 057/398] r/aws_eks_cluster: Don't associate an encryption_config if there's already one. --- aws/resource_aws_eks_cluster.go | 33 +++++++----- aws/resource_aws_eks_cluster_test.go | 81 ++++++++++++++++++++++++++-- 2 files changed, 97 insertions(+), 17 deletions(-) diff --git a/aws/resource_aws_eks_cluster.go b/aws/resource_aws_eks_cluster.go index 57737fdbb75..c13ed4eb7a9 100644 --- a/aws/resource_aws_eks_cluster.go +++ b/aws/resource_aws_eks_cluster.go @@ -91,14 +91,15 @@ func resourceAwsEksCluster() *schema.Resource { "key_arn": { Type: schema.TypeString, Required: true, + ForceNew: true, }, }, }, }, "resources": { Type: schema.TypeSet, - MinItems: 1, Required: true, + ForceNew: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice(tfeks.Resources_Values(), false), @@ -409,24 +410,28 @@ func resourceAwsEksClusterUpdate(d *schema.ResourceData, meta interface{}) error } if d.HasChange("encryption_config") { - input := &eks.AssociateEncryptionConfigInput{ - ClusterName: aws.String(d.Id()), - EncryptionConfig: expandEksEncryptionConfig(d.Get("encryption_config").([]interface{})), - } + o, n := d.GetChange("encryption_config") - log.Printf("[DEBUG] Associating EKS Cluster (%s) encryption config: %s", d.Id(), input) - output, err := conn.AssociateEncryptionConfig(input) + if len(o.([]interface{})) == 0 && len(n.([]interface{})) == 1 { + input := &eks.AssociateEncryptionConfigInput{ + ClusterName: aws.String(d.Id()), + EncryptionConfig: expandEksEncryptionConfig(d.Get("encryption_config").([]interface{})), + } - if err != nil { - return fmt.Errorf("error associating EKS Cluster (%s) encryption config: %w", d.Id(), err) - } + log.Printf("[DEBUG] Associating EKS Cluster (%s) encryption config: %s", d.Id(), input) + output, err := conn.AssociateEncryptionConfig(input) - updateID := aws.StringValue(output.Update.Id) + if err != nil { + return fmt.Errorf("error associating EKS Cluster (%s) encryption config: %w", d.Id(), err) + } - _, err = waiter.ClusterUpdateSuccessful(conn, d.Id(), updateID, d.Timeout(schema.TimeoutUpdate)) + updateID := aws.StringValue(output.Update.Id) - if err != nil { - return fmt.Errorf("error waiting for EKS Cluster (%s) encryption config association (%s): %w", d.Id(), updateID, err) + _, err = waiter.ClusterUpdateSuccessful(conn, d.Id(), updateID, d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return fmt.Errorf("error waiting for EKS Cluster (%s) encryption config association (%s): %w", d.Id(), updateID, err) + } } } diff --git a/aws/resource_aws_eks_cluster_test.go b/aws/resource_aws_eks_cluster_test.go index c7878053131..9ccc216fee5 100644 --- a/aws/resource_aws_eks_cluster_test.go +++ b/aws/resource_aws_eks_cluster_test.go @@ -174,7 +174,7 @@ func TestAccAWSEksCluster_EncryptionConfig_Create(t *testing.T) { } func TestAccAWSEksCluster_EncryptionConfig_Update(t *testing.T) { - var cluster eks.Cluster + var cluster1, cluster2 eks.Cluster rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_eks_cluster.test" kmsKeyResourceName := "aws_kms_key.test" @@ -188,14 +188,15 @@ func TestAccAWSEksCluster_EncryptionConfig_Update(t *testing.T) { { Config: testAccAWSEksClusterConfig_Required(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSEksClusterExists(resourceName, &cluster), + testAccCheckAWSEksClusterExists(resourceName, &cluster1), resource.TestCheckResourceAttr(resourceName, "encryption_config.#", "0"), ), }, { Config: testAccAWSEksClusterConfig_EncryptionConfig(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSEksClusterExists(resourceName, &cluster), + testAccCheckAWSEksClusterExists(resourceName, &cluster2), + testAccCheckAWSEksClusterNotRecreated(&cluster1, &cluster2), resource.TestCheckResourceAttr(resourceName, "encryption_config.#", "1"), resource.TestCheckResourceAttr(resourceName, "encryption_config.0.provider.#", "1"), resource.TestCheckResourceAttrPair(resourceName, "encryption_config.0.provider.0.key_arn", kmsKeyResourceName, "arn"), @@ -211,6 +212,51 @@ func TestAccAWSEksCluster_EncryptionConfig_Update(t *testing.T) { }) } +// https://github.com/hashicorp/terraform-provider-aws/issues/19968. +func TestAccAWSEksCluster_EncryptionConfig_VersionUpdate(t *testing.T) { + var cluster1, cluster2 eks.Cluster + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_eks_cluster.test" + kmsKeyResourceName := "aws_kms_key.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t) }, + ErrorCheck: testAccErrorCheck(t, eks.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSEksClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEksClusterConfig_EncryptionConfig_Version(rName, "1.19"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEksClusterExists(resourceName, &cluster1), + resource.TestCheckResourceAttr(resourceName, "encryption_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "encryption_config.0.provider.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "encryption_config.0.provider.0.key_arn", kmsKeyResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "encryption_config.0.resources.#", "1"), + resource.TestCheckResourceAttr(resourceName, "version", "1.19"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSEksClusterConfig_EncryptionConfig_Version(rName, "1.20"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEksClusterExists(resourceName, &cluster2), + testAccCheckAWSEksClusterNotRecreated(&cluster1, &cluster2), + resource.TestCheckResourceAttr(resourceName, "encryption_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "encryption_config.0.provider.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "encryption_config.0.provider.0.key_arn", kmsKeyResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "encryption_config.0.resources.#", "1"), + resource.TestCheckResourceAttr(resourceName, "version", "1.20"), + ), + }, + }, + }) +} + func TestAccAWSEksCluster_Version(t *testing.T) { var cluster1, cluster2 eks.Cluster rName := acctest.RandomWithPrefix("tf-acc-test") @@ -816,6 +862,35 @@ resource "aws_eks_cluster" "test" { `, rName)) } +func testAccAWSEksClusterConfig_EncryptionConfig_Version(rName, version string) string { + return composeConfig(testAccAWSEksClusterConfig_Base(rName), fmt.Sprintf(` +resource "aws_kms_key" "test" { + description = %[1]q + deletion_window_in_days = 7 +} + +resource "aws_eks_cluster" "test" { + name = %[1]q + role_arn = aws_iam_role.test.arn + version = %[2]q + + encryption_config { + resources = ["secrets"] + + provider { + key_arn = aws_kms_key.test.arn + } + } + + vpc_config { + subnet_ids = aws_subnet.test[*].id + } + + depends_on = [aws_iam_role_policy_attachment.test-AmazonEKSClusterPolicy] +} +`, rName, version)) +} + func testAccAWSEksClusterConfig_VpcConfig_SecurityGroupIds(rName string) string { return composeConfig(testAccAWSEksClusterConfig_Base(rName), fmt.Sprintf(` resource "aws_security_group" "test" { From 339252731fa13dea74be82acfc6bca4c2b9cfa27 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 28 Jun 2021 12:14:48 -0400 Subject: [PATCH 058/398] Add CHANGELOG entry. --- .changelog/19986.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/19986.txt diff --git a/.changelog/19986.txt b/.changelog/19986.txt new file mode 100644 index 00000000000..0638db645e1 --- /dev/null +++ b/.changelog/19986.txt @@ -0,0 +1,3 @@ +```release-note:bug +resource/aws_eks_cluster: Don't associate an `encryption_config` if there's already one +``` \ No newline at end of file From 062fee7032b9484c699f459b03a8f88d7e26184b Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Mon, 28 Jun 2021 19:59:57 +0300 Subject: [PATCH 059/398] ammend sweeper --- aws/resource_aws_sagemaker_domain_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/aws/resource_aws_sagemaker_domain_test.go b/aws/resource_aws_sagemaker_domain_test.go index feb635451eb..b11720bff58 100644 --- a/aws/resource_aws_sagemaker_domain_test.go +++ b/aws/resource_aws_sagemaker_domain_test.go @@ -90,6 +90,7 @@ func testSweepSagemakerDomains(region string) error { r := resourceAwsSagemakerDomain() d := r.Data(nil) d.SetId(aws.StringValue(domain.DomainId)) + d.Set("retention_policy.0.home_efs_file_system", "Delete") err = r.Delete(d, client) if err != nil { log.Printf("[ERROR] %s", err) From 37a65a7b50161b18fe7a0782720715842e0f0c10 Mon Sep 17 00:00:00 2001 From: Ilia Lazebnik Date: Mon, 28 Jun 2021 20:22:17 +0300 Subject: [PATCH 060/398] Apply suggestions from code review Co-authored-by: Kit Ewbank --- aws/resource_aws_cloudwatch_event_target.go | 6 +++--- aws/resource_aws_cloudwatch_event_target_test.go | 2 +- website/docs/r/cloudwatch_event_target.html.markdown | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/aws/resource_aws_cloudwatch_event_target.go b/aws/resource_aws_cloudwatch_event_target.go index b9c8722ead3..99c12478927 100644 --- a/aws/resource_aws_cloudwatch_event_target.go +++ b/aws/resource_aws_cloudwatch_event_target.go @@ -186,7 +186,7 @@ func resourceAwsCloudWatchEventTarget() *schema.Resource { }, }, }, - "placement_constraints": { + "placement_constraint": { Type: schema.TypeSet, Optional: true, MaxItems: 10, @@ -620,7 +620,7 @@ func expandAwsCloudWatchEventTargetEcsParameters(config []interface{}, meta inte ecsParameters.PlatformVersion = aws.String(val) } - if v, ok := param["placement_constraints"].(*schema.Set); ok && v.Len() > 0 { + if v, ok := param["placement_constraint"].(*schema.Set); ok && v.Len() > 0 { ecsParameters.PlacementConstraints = expandAwsCloudWatchEventTargetPlacementConstraints(v.List()) } @@ -817,7 +817,7 @@ func flattenAwsCloudWatchEventTargetEcsParameters(ecsParameters *events.EcsParam } if ecsParameters.PlacementConstraints != nil { - config["placement_constraints"] = flattenAwsCloudWatchEventTargetPlacementConstraints(ecsParameters.PlacementConstraints) + config["placement_constraint"] = flattenAwsCloudWatchEventTargetPlacementConstraints(ecsParameters.PlacementConstraints) } tags := keyvaluetags.CloudwatcheventsKeyValueTags(ecsParameters.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig) diff --git a/aws/resource_aws_cloudwatch_event_target_test.go b/aws/resource_aws_cloudwatch_event_target_test.go index 74dcace445e..b7b0d23fd93 100644 --- a/aws/resource_aws_cloudwatch_event_target_test.go +++ b/aws/resource_aws_cloudwatch_event_target_test.go @@ -1410,7 +1410,7 @@ resource "aws_cloudwatch_event_target" "test" { enable_ecs_managed_tags = true propagate_tags = "TASK_DEFINITION" - placement_constraints { + placement_constraint { type = "distinctInstance" } diff --git a/website/docs/r/cloudwatch_event_target.html.markdown b/website/docs/r/cloudwatch_event_target.html.markdown index 7e32f1e4c03..09710ceda8d 100644 --- a/website/docs/r/cloudwatch_event_target.html.markdown +++ b/website/docs/r/cloudwatch_event_target.html.markdown @@ -359,7 +359,7 @@ The following arguments are supported: * `task_definition_arn` - (Required) The ARN of the task definition to use if the event target is an Amazon ECS cluster. * `tags` - (Optional) A map of tags to assign to ecs resources. If configured with a provider [`default_tags` configuration block](/docs/providers/aws/index.html#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. * `propagate_tags` - (Optional) Specifies whether to propagate the tags from the task definition to the task. If no value is specified, the tags are not propagated. Tags can only be propagated to the task during task creation. -* `placement_constraints` - (Optional) An array of placement constraint objects to use for the task. You can specify up to 10 constraints per task (including constraints in the task definition and those specified at runtime). See Below. +* `placement_constraint` - (Optional) An array of placement constraint objects to use for the task. You can specify up to 10 constraints per task (including constraints in the task definition and those specified at runtime). See Below. * `enable_execute_command` - (Optional) Whether or not to enable the execute command functionality for the containers in this task. If true, this enables execute command functionality on all containers in the task. * `enable_ecs_managed_tags` - (Optional) Specifies whether to enable Amazon ECS managed tags for the task. @@ -371,7 +371,7 @@ The following arguments are supported: For more information, see [Task Networking](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-networking.html) -#### placement_constraints +#### placement_constraint * `type` - (Required) Type of constraint. The only valid values at this time are `memberOf` and `distinctInstance`. * `expression` - (Optional) Cluster Query Language expression to apply to the constraint. Does not need to be specified for the `distinctInstance` type. For more information, see [Cluster Query Language in the Amazon EC2 Container Service Developer Guide](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/cluster-query-language.html). From 5da1ad38f35ed9d0afc877f4f0904851b0ea0459 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Mon, 28 Jun 2021 20:28:10 +0300 Subject: [PATCH 061/398] remove default tags from ecs tags --- aws/resource_aws_cloudwatch_event_target.go | 23 ++++++------------- .../r/cloudwatch_event_target.html.markdown | 2 +- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/aws/resource_aws_cloudwatch_event_target.go b/aws/resource_aws_cloudwatch_event_target.go index 99c12478927..f793498ddb0 100644 --- a/aws/resource_aws_cloudwatch_event_target.go +++ b/aws/resource_aws_cloudwatch_event_target.go @@ -215,8 +215,7 @@ func resourceAwsCloudWatchEventTarget() *schema.Resource { Default: events.PropagateTagsTaskDefinition, ValidateFunc: validation.StringInSlice(events.PropagateTags_Values(), false), }, - "tags": tagsSchema(), - "tags_all": tagsSchemaComputed(), + "tags": tagsSchema(), "task_count": { Type: schema.TypeInt, Optional: true, @@ -428,7 +427,7 @@ func resourceAwsCloudWatchEventTargetRead(d *schema.ResourceData, meta interface } if t.EcsParameters != nil { - if err := d.Set("ecs_target", flattenAwsCloudWatchEventTargetEcsParameters(t.EcsParameters, meta)); err != nil { + if err := d.Set("ecs_target", flattenAwsCloudWatchEventTargetEcsParameters(t.EcsParameters)); err != nil { return fmt.Errorf("Error setting ecs_target error: %w", err) } } @@ -536,7 +535,7 @@ func buildPutTargetInputStruct(d *schema.ResourceData, meta interface{}) *events } if v, ok := d.GetOk("ecs_target"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - e.EcsParameters = expandAwsCloudWatchEventTargetEcsParameters(v.([]interface{}), meta) + e.EcsParameters = expandAwsCloudWatchEventTargetEcsParameters(v.([]interface{})) } if v, ok := d.GetOk("http_target"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { @@ -596,13 +595,11 @@ func expandAwsCloudWatchEventTargetRunParameters(config []interface{}) *events.R return command } -func expandAwsCloudWatchEventTargetEcsParameters(config []interface{}, meta interface{}) *events.EcsParameters { - +func expandAwsCloudWatchEventTargetEcsParameters(config []interface{}) *events.EcsParameters { ecsParameters := &events.EcsParameters{} for _, c := range config { param := c.(map[string]interface{}) - defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig - tags := defaultTagsConfig.MergeTags(keyvaluetags.New(param["tags"].(map[string]interface{}))) + tags := keyvaluetags.New(param["tags"].(map[string]interface{})) if val, ok := param["group"].(string); ok && val != "" { ecsParameters.Group = aws.String(val) @@ -794,10 +791,7 @@ func flattenAwsCloudWatchEventTargetRunParameters(runCommand *events.RunCommandP return result } -func flattenAwsCloudWatchEventTargetEcsParameters(ecsParameters *events.EcsParameters, meta interface{}) []map[string]interface{} { - defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig - ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig - +func flattenAwsCloudWatchEventTargetEcsParameters(ecsParameters *events.EcsParameters) []map[string]interface{} { config := make(map[string]interface{}) if ecsParameters.Group != nil { config["group"] = aws.StringValue(ecsParameters.Group) @@ -820,10 +814,7 @@ func flattenAwsCloudWatchEventTargetEcsParameters(ecsParameters *events.EcsParam config["placement_constraint"] = flattenAwsCloudWatchEventTargetPlacementConstraints(ecsParameters.PlacementConstraints) } - tags := keyvaluetags.CloudwatcheventsKeyValueTags(ecsParameters.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig) - config["tags"] = tags.RemoveDefaultConfig(defaultTagsConfig).Map() - config["tags_all"] = tags.Map() - + config["tags"] = keyvaluetags.CloudwatcheventsKeyValueTags(ecsParameters.Tags).IgnoreAws().Map() config["enable_execute_command"] = aws.BoolValue(ecsParameters.EnableExecuteCommand) config["enable_ecs_managed_tags"] = aws.BoolValue(ecsParameters.EnableECSManagedTags) config["task_count"] = aws.Int64Value(ecsParameters.TaskCount) diff --git a/website/docs/r/cloudwatch_event_target.html.markdown b/website/docs/r/cloudwatch_event_target.html.markdown index 09710ceda8d..0ab5232a2ca 100644 --- a/website/docs/r/cloudwatch_event_target.html.markdown +++ b/website/docs/r/cloudwatch_event_target.html.markdown @@ -357,7 +357,7 @@ The following arguments are supported: * `platform_version` - (Optional) Specifies the platform version for the task. Specify only the numeric portion of the platform version, such as 1.1.0. This is used only if LaunchType is FARGATE. For more information about valid platform versions, see [AWS Fargate Platform Versions](http://docs.aws.amazon.com/AmazonECS/latest/developerguide/platform_versions.html). * `task_count` - (Optional) The number of tasks to create based on the TaskDefinition. The default is 1. * `task_definition_arn` - (Required) The ARN of the task definition to use if the event target is an Amazon ECS cluster. -* `tags` - (Optional) A map of tags to assign to ecs resources. If configured with a provider [`default_tags` configuration block](/docs/providers/aws/index.html#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. +* `tags` - (Optional) A map of tags to assign to ecs resources. * `propagate_tags` - (Optional) Specifies whether to propagate the tags from the task definition to the task. If no value is specified, the tags are not propagated. Tags can only be propagated to the task during task creation. * `placement_constraint` - (Optional) An array of placement constraint objects to use for the task. You can specify up to 10 constraints per task (including constraints in the task definition and those specified at runtime). See Below. * `enable_execute_command` - (Optional) Whether or not to enable the execute command functionality for the containers in this task. If true, this enables execute command functionality on all containers in the task. From b2b8e9374435aade1786e4c680943cb4d22507b6 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Mon, 28 Jun 2021 20:30:10 +0300 Subject: [PATCH 062/398] missed test config rename --- aws/resource_aws_cloudwatch_event_target_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_cloudwatch_event_target_test.go b/aws/resource_aws_cloudwatch_event_target_test.go index b7b0d23fd93..24c57d41121 100644 --- a/aws/resource_aws_cloudwatch_event_target_test.go +++ b/aws/resource_aws_cloudwatch_event_target_test.go @@ -557,8 +557,8 @@ func TestAccAWSCloudWatchEventTarget_ecsFull(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "ecs_target.0.enable_execute_command", "true"), resource.TestCheckResourceAttr(resourceName, "ecs_target.0.enable_ecs_managed_tags", "true"), resource.TestCheckResourceAttr(resourceName, "ecs_target.0.propagate_tags", "TASK_DEFINITION"), - resource.TestCheckResourceAttr(resourceName, "ecs_target.0.placement_constraints.#", "1"), - resource.TestCheckResourceAttr(resourceName, "ecs_target.0.placement_constraints.0.type", "distinctInstance"), + resource.TestCheckResourceAttr(resourceName, "ecs_target.0.placement_constraint.#", "1"), + resource.TestCheckResourceAttr(resourceName, "ecs_target.0.placement_constraint.0.type", "distinctInstance"), resource.TestCheckResourceAttr(resourceName, "ecs_target.0.tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "ecs_target.0.tags.test", "test1"), ), From 472a694ae82d8826a14db5eab1b3c123b010d18a Mon Sep 17 00:00:00 2001 From: Anderson Madureira Date: Tue, 2 Feb 2021 15:27:44 -0300 Subject: [PATCH 063/398] ADd new key --- aws/resource_aws_transfer_server.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/aws/resource_aws_transfer_server.go b/aws/resource_aws_transfer_server.go index eef991d07ce..7b455f00fff 100644 --- a/aws/resource_aws_transfer_server.go +++ b/aws/resource_aws_transfer_server.go @@ -66,6 +66,14 @@ func resourceAwsTransferServer() *schema.Resource { Set: schema.HashString, ConflictsWith: []string{"endpoint_details.0.vpc_endpoint_id"}, }, + "security_group_ids": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + ConflictsWith: []string{"endpoint_details.0.vpc_endpoint_id"}, + }, "subnet_ids": { Type: schema.TypeSet, Optional: true, @@ -76,7 +84,7 @@ func resourceAwsTransferServer() *schema.Resource { "vpc_endpoint_id": { Type: schema.TypeString, Optional: true, - ConflictsWith: []string{"endpoint_details.0.address_allocation_ids", "endpoint_details.0.subnet_ids", "endpoint_details.0.vpc_id"}, + ConflictsWith: []string{"endpoint_details.0.address_allocation_ids", "endpoint_details.0.security_group_ids", "endpoint_details.0.subnet_ids", "endpoint_details.0.vpc_id"}, Computed: true, }, "vpc_id": { From a1aa8edc05bb916368c5f329f91db6b0f3aa4340 Mon Sep 17 00:00:00 2001 From: Anderson Madureira Date: Tue, 2 Feb 2021 17:35:19 -0300 Subject: [PATCH 064/398] Change --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9470fd018ef..c836d55f192 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -836,6 +836,7 @@ FEATURES: ENHANCEMENTS: + * data-source/aws_subnet: Add `customer_owned_ipv4_pool` and `map_customer_owned_ip_on_launch` attributes ([#16676](https://github.com/hashicorp/terraform-provider-aws/issues/16676)) * resource/aws_glacier_vault: Add plan-time validation for `notification` configuration block `events` and `sns_topic_arn` arguments ([#12645](https://github.com/hashicorp/terraform-provider-aws/issues/12645)) * resource/aws_glue_catalog_table: Adds support for specifying schema from schema registry. ([#17335](https://github.com/hashicorp/terraform-provider-aws/issues/17335)) From fc1a43740549f04b7b4c5498c512807f42c4a0a2 Mon Sep 17 00:00:00 2001 From: Anderson Madureira Date: Thu, 4 Feb 2021 15:13:34 -0300 Subject: [PATCH 065/398] Add security_group_ids parameter. --- aws/resource_aws_transfer_server.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aws/resource_aws_transfer_server.go b/aws/resource_aws_transfer_server.go index 7b455f00fff..3750be1faf8 100644 --- a/aws/resource_aws_transfer_server.go +++ b/aws/resource_aws_transfer_server.go @@ -268,6 +268,9 @@ func resourceAwsTransferServerCreate(d *schema.ResourceData, meta interface{}) e if err := stopTransferServer(conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { return err } + updateEndPoint := d.Get("endpoint_details") + // delete(updateEndPoint, "SecurityGroupIds") + // updateEndPoint.SecurityGroupIds = {} input := &transfer.UpdateServerInput{ ServerId: aws.String(d.Id()), From 2d4c47c9274d3529e3aaec73c250fcea77168925 Mon Sep 17 00:00:00 2001 From: Anderson Madureira Date: Thu, 4 Feb 2021 18:18:03 -0300 Subject: [PATCH 066/398] change --- .goreleaser.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index fe1eb97fe8c..999f7af3d85 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -11,15 +11,15 @@ builds: flags: - -trimpath goarch: - - '386' - - amd64 - - arm +# - '386' +# - amd64 +# - arm - arm64 goos: - darwin - - freebsd - - linux - - windows +# - freebsd +# - linux +# - windows ignore: - goarch: '386' goos: darwin From 8d2dd00a5b05d1e694db5fa7129ccfbd46a1f9bb Mon Sep 17 00:00:00 2001 From: Anderson Madureira Date: Thu, 4 Feb 2021 18:18:58 -0300 Subject: [PATCH 067/398] change --- .goreleaser.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index 999f7af3d85..6b174af7874 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -18,7 +18,7 @@ builds: goos: - darwin # - freebsd -# - linux + - linux # - windows ignore: - goarch: '386' From 708b2671f891aa526eb06f82a76df0764878c4e8 Mon Sep 17 00:00:00 2001 From: Anderson Madureira Date: Thu, 4 Feb 2021 18:35:20 -0300 Subject: [PATCH 068/398] Change --- .goreleaser.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index 6b174af7874..fe1eb97fe8c 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -11,15 +11,15 @@ builds: flags: - -trimpath goarch: -# - '386' -# - amd64 -# - arm + - '386' + - amd64 + - arm - arm64 goos: - darwin -# - freebsd + - freebsd - linux -# - windows + - windows ignore: - goarch: '386' goos: darwin From e1751a8fe5953853026575d5800b280962f48e01 Mon Sep 17 00:00:00 2001 From: Anderson Madureira Date: Thu, 4 Feb 2021 19:15:55 -0300 Subject: [PATCH 069/398] Change --- .github/workflows/release.yml | 54 +++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 90969c0596b..5ed1b05a502 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,3 +1,4 @@ +<<<<<<< HEAD name: Post Publish on: release: @@ -52,3 +53,56 @@ jobs: git add CHANGELOG.md git commit -m "Update CHANGELOG.md after ${{ github.event.release.tag_name }}" git push +======= +# This GitHub action can publish assets for release when a tag is created. +# Currently its setup to run on any tag that matches the pattern "v*" (ie. v0.1.0). +# +# This uses an action (paultyng/ghaction-import-gpg) that assumes you set your +# private key in the `GPG_PRIVATE_KEY` secret and passphrase in the `PASSPHRASE` +# secret. If you would rather own your own GPG handling, please fork this action +# or use an alternative one for key handling. +# +# You will need to pass the `--batch` flag to `gpg` in your signing step +# in `goreleaser` to indicate this is being used in a non-interactive mode. +# +name: release +on: + push: + tags: + - 'v*' +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v2 + - + name: Unshallow + run: git fetch --prune --unshallow + - + name: Set up Go + uses: actions/setup-go@v2 + with: + go-version: 1.14 + - + name: Import GPG key + id: import_gpg + # TODO: move this to HashiCorp namespace or find alternative that is just simple gpg commands + # see https://github.com/hashicorp/terraform-provider-scaffolding/issues/22 + uses: paultyng/ghaction-import-gpg@v2.1.0 + env: + # These secrets will need to be configured for the repository: + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + PASSPHRASE: ${{ secrets.PASSPHRASE }} + - + name: Run GoReleaser + uses: goreleaser/goreleaser-action@v2 + with: + version: latest + args: release --rm-dist + env: + GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} + # GitHub sets this automatically + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +>>>>>>> 19bf7eb9c (Change) From 82f0197fcd53d92c1db54e0db0563030d4b67b2d Mon Sep 17 00:00:00 2001 From: Anderson Madureira Date: Fri, 5 Feb 2021 21:52:10 -0300 Subject: [PATCH 070/398] Update --- aws/resource_aws_transfer_server.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/aws/resource_aws_transfer_server.go b/aws/resource_aws_transfer_server.go index 3750be1faf8..2ed9554e5be 100644 --- a/aws/resource_aws_transfer_server.go +++ b/aws/resource_aws_transfer_server.go @@ -268,9 +268,8 @@ func resourceAwsTransferServerCreate(d *schema.ResourceData, meta interface{}) e if err := stopTransferServer(conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { return err } - updateEndPoint := d.Get("endpoint_details") - // delete(updateEndPoint, "SecurityGroupIds") - // updateEndPoint.SecurityGroupIds = {} + // Here we ansure that SecurityGroupsids is nil. We can't update this + createOpts.EndpointDetails.SecurityGroupIds = nil input := &transfer.UpdateServerInput{ ServerId: aws.String(d.Id()), From 240e4456efd6e7cd0c09c903362f1bb90d842f25 Mon Sep 17 00:00:00 2001 From: Anderson Madureira Date: Fri, 5 Feb 2021 22:03:12 -0300 Subject: [PATCH 071/398] Update Doc --- website/docs/r/transfer_server.html.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/website/docs/r/transfer_server.html.markdown b/website/docs/r/transfer_server.html.markdown index 6b172348db8..ef4616f87e5 100644 --- a/website/docs/r/transfer_server.html.markdown +++ b/website/docs/r/transfer_server.html.markdown @@ -108,6 +108,7 @@ The following arguments are supported: * `address_allocation_ids` - (Optional) A list of address allocation IDs that are required to attach an Elastic IP address to your SFTP server's endpoint. This property can only be used when `endpoint_type` is set to `VPC`. * `subnet_ids` - (Optional) A list of subnet IDs that are required to host your SFTP server endpoint in your VPC. This property can only be used when `endpoint_type` is set to `VPC`. * `vpc_id` - (Optional) The VPC ID of the virtual private cloud in which the SFTP server's endpoint will be hosted. This property can only be used when `endpoint_type` is set to `VPC`. +* `security_group_ids` - (Optional) A list of Security Groups Ids. This property can only be used when `endpoint_type` is set to `VPC`. It can't be change after transfer server creation. ## Attributes Reference In addition to all arguments above, the following attributes are exported: From f8a62376bdb9f7be9e5a1285869168d01ad35483 Mon Sep 17 00:00:00 2001 From: Anderson Madureira Date: Tue, 9 Feb 2021 17:43:16 -0300 Subject: [PATCH 072/398] Add go.yaml --- .github/workflows/go.yml | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .github/workflows/go.yml diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml new file mode 100644 index 00000000000..7fe92da0143 --- /dev/null +++ b/.github/workflows/go.yml @@ -0,0 +1,40 @@ +name: release +on: + push: + tags: + - 'v*' +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v2 + - + name: Unshallow + run: git fetch --prune --unshallow + - + name: Set up Go + uses: actions/setup-go@v2 + with: + go-version: 1.14 + - + name: Import GPG key + id: import_gpg + # TODO: move this to HashiCorp namespace or find alternative that is just simple gpg commands + # see https://github.com/hashicorp/terraform-provider-scaffolding/issues/22 + uses: paultyng/ghaction-import-gpg@v2.1.0 + env: + # These secrets will need to be configured for the repository: + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + PASSPHRASE: ${{ secrets.PASSPHRASE }} + - + name: Run GoReleaser + uses: goreleaser/goreleaser-action@v2 + with: + version: latest + args: release --rm-dist + env: + GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} + # GitHub sets this automatically + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 4b191fb2f384eb8e303de820897967c317a61e75 Mon Sep 17 00:00:00 2001 From: Anderson Madureira Date: Tue, 9 Feb 2021 19:04:02 -0300 Subject: [PATCH 073/398] Change --- .goreleaser.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index fe1eb97fe8c..17ec99994b2 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -11,15 +11,15 @@ builds: flags: - -trimpath goarch: - - '386' +# - '386' - amd64 - - arm +# - arm - arm64 goos: - darwin - - freebsd +# - freebsd - linux - - windows +# - windows ignore: - goarch: '386' goos: darwin From 54698e8999ea81ad257593245b48acf8b4252a9d Mon Sep 17 00:00:00 2001 From: Anderson Madureira Date: Tue, 9 Feb 2021 19:05:05 -0300 Subject: [PATCH 074/398] Change --- .goreleaser.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index 17ec99994b2..a98c6242ceb 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -27,7 +27,7 @@ builds: - -s -w -X version.ProviderVersion={{.Version}} mod_timestamp: '{{ .CommitTimestamp }}' changelog: - skip: true + skip: false checksum: name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS' algorithm: sha256 From 1183799c04a30b5a615c10eca56f30c598c122ab Mon Sep 17 00:00:00 2001 From: Anderson Madureira Date: Tue, 9 Feb 2021 19:13:30 -0300 Subject: [PATCH 075/398] Change --- .goreleaser.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index a98c6242ceb..3fae370f305 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -12,7 +12,7 @@ builds: - -trimpath goarch: # - '386' - - amd64 +# - amd64 # - arm - arm64 goos: @@ -34,7 +34,7 @@ checksum: env: - CGO_ENABLED=0 release: - disable: true + disable: false signs: - artifacts: checksum args: From 18f986ab726b69fc213ec212388fcbd89c0f82d5 Mon Sep 17 00:00:00 2001 From: Anderson Madureira Date: Tue, 9 Feb 2021 19:18:31 -0300 Subject: [PATCH 076/398] Change --- .goreleaser.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.goreleaser.yml b/.goreleaser.yml index 3fae370f305..84a1671a58b 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -35,6 +35,12 @@ env: - CGO_ENABLED=0 release: disable: false + github: + owner: amadureira + name: terraform-provider-aws + + + signs: - artifacts: checksum args: From c439ede8c7b0eee78a63aab7587941f4cfb964fc Mon Sep 17 00:00:00 2001 From: Anderson Madureira Date: Tue, 9 Feb 2021 19:40:34 -0300 Subject: [PATCH 077/398] Change only one line --- .goreleaser.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.goreleaser.yml b/.goreleaser.yml index 84a1671a58b..194a26e9353 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -6,6 +6,7 @@ archives: before: hooks: - go mod download + builds: - binary: '{{ .ProjectName }}_{{ .Version }}' flags: From 471c29e41751369384cbc48fb9c236043aab229d Mon Sep 17 00:00:00 2001 From: Anderson Madureira Date: Tue, 9 Feb 2021 20:00:54 -0300 Subject: [PATCH 078/398] Change --- .goreleaser.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index 194a26e9353..a16b9e5bbc9 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -13,14 +13,14 @@ builds: - -trimpath goarch: # - '386' -# - amd64 -# - arm + - amd64 + - arm - arm64 goos: - darwin -# - freebsd + - freebsd - linux -# - windows + - windows ignore: - goarch: '386' goos: darwin From f96aaa9464aa64bf6cde612d086cbd83726c1a07 Mon Sep 17 00:00:00 2001 From: Anderson Madureira Date: Tue, 9 Feb 2021 23:37:02 -0300 Subject: [PATCH 079/398] Add changelog --- .changelog/17496.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/17496.txt diff --git a/.changelog/17496.txt b/.changelog/17496.txt new file mode 100644 index 00000000000..70e1bc075d3 --- /dev/null +++ b/.changelog/17496.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_transfer_server: Add security_group_ids for endpoint_details block +``` From 8ab5322e57cc93a2654aac0d6034387e5a55ed34 Mon Sep 17 00:00:00 2001 From: Anderson Madureira Date: Tue, 16 Feb 2021 15:20:10 -0300 Subject: [PATCH 080/398] Add changelog, reset .goreleaser.yml and fix aws/resource_aws_transfer_server.go --- .changelog/17539.txt | 3 +++ .goreleaser.yml | 13 +++---------- aws/resource_aws_transfer_server.go | 1 + 3 files changed, 7 insertions(+), 10 deletions(-) create mode 100644 .changelog/17539.txt diff --git a/.changelog/17539.txt b/.changelog/17539.txt new file mode 100644 index 00000000000..3961f2085c9 --- /dev/null +++ b/.changelog/17539.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_transfer_server: Add security_group_ids parameter +``` diff --git a/.goreleaser.yml b/.goreleaser.yml index a16b9e5bbc9..fe1eb97fe8c 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -6,13 +6,12 @@ archives: before: hooks: - go mod download - builds: - binary: '{{ .ProjectName }}_{{ .Version }}' flags: - -trimpath goarch: -# - '386' + - '386' - amd64 - arm - arm64 @@ -28,20 +27,14 @@ builds: - -s -w -X version.ProviderVersion={{.Version}} mod_timestamp: '{{ .CommitTimestamp }}' changelog: - skip: false + skip: true checksum: name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS' algorithm: sha256 env: - CGO_ENABLED=0 release: - disable: false - github: - owner: amadureira - name: terraform-provider-aws - - - + disable: true signs: - artifacts: checksum args: diff --git a/aws/resource_aws_transfer_server.go b/aws/resource_aws_transfer_server.go index 2ed9554e5be..2831f052076 100644 --- a/aws/resource_aws_transfer_server.go +++ b/aws/resource_aws_transfer_server.go @@ -268,6 +268,7 @@ func resourceAwsTransferServerCreate(d *schema.ResourceData, meta interface{}) e if err := stopTransferServer(conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { return err } + // Here we ansure that SecurityGroupsids is nil. We can't update this createOpts.EndpointDetails.SecurityGroupIds = nil From 083b5188bb014a73bd87c6254931a6094532196e Mon Sep 17 00:00:00 2001 From: Anderson Madureira Date: Fri, 19 Feb 2021 10:34:04 -0300 Subject: [PATCH 081/398] A little change --- .changelog/17539.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changelog/17539.txt b/.changelog/17539.txt index 3961f2085c9..085b0471cb7 100644 --- a/.changelog/17539.txt +++ b/.changelog/17539.txt @@ -1,3 +1,3 @@ ```release-note:enhancement -resource/aws_transfer_server: Add security_group_ids parameter +resource/aws_transfer_server: Add security_group_ids parameter. ``` From 3660eb6323ab51a537a8ea7c126e2e9da980db19 Mon Sep 17 00:00:00 2001 From: Anderson Madureira Date: Tue, 16 Feb 2021 21:54:14 -0300 Subject: [PATCH 082/398] Return .goreleaser.yml and CHANGELOG.md to original state --- .goreleaser.yml | 2 +- CHANGELOG.md | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index fe1eb97fe8c..8d1a78155dd 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -24,7 +24,7 @@ builds: - goarch: '386' goos: darwin ldflags: - - -s -w -X version.ProviderVersion={{.Version}} + - -s -w -X aws/version.ProviderVersion={{.Version}} mod_timestamp: '{{ .CommitTimestamp }}' changelog: skip: true diff --git a/CHANGELOG.md b/CHANGELOG.md index c836d55f192..9470fd018ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -836,7 +836,6 @@ FEATURES: ENHANCEMENTS: - * data-source/aws_subnet: Add `customer_owned_ipv4_pool` and `map_customer_owned_ip_on_launch` attributes ([#16676](https://github.com/hashicorp/terraform-provider-aws/issues/16676)) * resource/aws_glacier_vault: Add plan-time validation for `notification` configuration block `events` and `sns_topic_arn` arguments ([#12645](https://github.com/hashicorp/terraform-provider-aws/issues/12645)) * resource/aws_glue_catalog_table: Adds support for specifying schema from schema registry. ([#17335](https://github.com/hashicorp/terraform-provider-aws/issues/17335)) From e6d232091bba6851783572f45cf38fedd2b788ff Mon Sep 17 00:00:00 2001 From: Anderson Madureira Date: Tue, 16 Feb 2021 21:55:11 -0300 Subject: [PATCH 083/398] Remove Custom workflow files --- .github/workflows/go.yml | 40 ---------------------------------------- 1 file changed, 40 deletions(-) delete mode 100644 .github/workflows/go.yml diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml deleted file mode 100644 index 7fe92da0143..00000000000 --- a/.github/workflows/go.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: release -on: - push: - tags: - - 'v*' -jobs: - goreleaser: - runs-on: ubuntu-latest - steps: - - - name: Checkout - uses: actions/checkout@v2 - - - name: Unshallow - run: git fetch --prune --unshallow - - - name: Set up Go - uses: actions/setup-go@v2 - with: - go-version: 1.14 - - - name: Import GPG key - id: import_gpg - # TODO: move this to HashiCorp namespace or find alternative that is just simple gpg commands - # see https://github.com/hashicorp/terraform-provider-scaffolding/issues/22 - uses: paultyng/ghaction-import-gpg@v2.1.0 - env: - # These secrets will need to be configured for the repository: - GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} - PASSPHRASE: ${{ secrets.PASSPHRASE }} - - - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v2 - with: - version: latest - args: release --rm-dist - env: - GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} - # GitHub sets this automatically - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 9c72f21c098a1ec41230c5e1354b8ecad3f785c6 Mon Sep 17 00:00:00 2001 From: Anderson Madureira Date: Fri, 26 Feb 2021 10:34:56 -0300 Subject: [PATCH 084/398] Change --- .changelog/17496.txt | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 .changelog/17496.txt diff --git a/.changelog/17496.txt b/.changelog/17496.txt deleted file mode 100644 index 70e1bc075d3..00000000000 --- a/.changelog/17496.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:enhancement -resource/aws_transfer_server: Add security_group_ids for endpoint_details block -``` From 64d2c2b494d9dc9017c0edead923b48e876f39f7 Mon Sep 17 00:00:00 2001 From: amadureira Date: Fri, 26 Feb 2021 10:34:32 -0300 Subject: [PATCH 085/398] Update .changelog/17539.txt Co-authored-by: Kit Ewbank --- .changelog/17539.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changelog/17539.txt b/.changelog/17539.txt index 085b0471cb7..8f9455ff51b 100644 --- a/.changelog/17539.txt +++ b/.changelog/17539.txt @@ -1,3 +1,3 @@ ```release-note:enhancement -resource/aws_transfer_server: Add security_group_ids parameter. +resource/aws_transfer_server: Add `security_group_ids` attribute. ``` From 5a01d013cc9fc368b9f9a5f9fb419c4182613c34 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 28 Jun 2021 15:46:17 -0400 Subject: [PATCH 086/398] Tidy up after rebase. --- .changelog/17539.txt | 2 +- aws/resource_aws_transfer_server.go | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.changelog/17539.txt b/.changelog/17539.txt index 8f9455ff51b..4ea42cc4155 100644 --- a/.changelog/17539.txt +++ b/.changelog/17539.txt @@ -1,3 +1,3 @@ ```release-note:enhancement -resource/aws_transfer_server: Add `security_group_ids` attribute. +resource/aws_transfer_server: Add `security_group_ids` argument to `endpoint_details` configuration block. ``` diff --git a/aws/resource_aws_transfer_server.go b/aws/resource_aws_transfer_server.go index 2831f052076..7b455f00fff 100644 --- a/aws/resource_aws_transfer_server.go +++ b/aws/resource_aws_transfer_server.go @@ -269,9 +269,6 @@ func resourceAwsTransferServerCreate(d *schema.ResourceData, meta interface{}) e return err } - // Here we ansure that SecurityGroupsids is nil. We can't update this - createOpts.EndpointDetails.SecurityGroupIds = nil - input := &transfer.UpdateServerInput{ ServerId: aws.String(d.Id()), EndpointDetails: expandTransferEndpointDetails(d.Get("endpoint_details").([]interface{})[0].(map[string]interface{})), From 0b55299cb7fb033460c7e339c7a7524f163f655d Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 28 Jun 2021 16:23:43 -0400 Subject: [PATCH 087/398] r/aws_transfer_server: Add TODOs for security group ID updates. Acceptance test output: % make testacc TEST=./aws TESTARGS='-run=TestAccAWSTransferServer_vpc' ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./aws -v -count 1 -parallel 20 -run=TestAccAWSTransferServer_vpc -timeout 180m === RUN TestAccAWSTransferServer_vpc === PAUSE TestAccAWSTransferServer_vpc === RUN TestAccAWSTransferServer_vpcEndpointId === PAUSE TestAccAWSTransferServer_vpcEndpointId === CONT TestAccAWSTransferServer_vpc === CONT TestAccAWSTransferServer_vpcEndpointId --- PASS: TestAccAWSTransferServer_vpcEndpointId (55.36s) --- PASS: TestAccAWSTransferServer_vpc (230.58s) PASS ok github.com/terraform-providers/terraform-provider-aws/aws 235.878s --- aws/resource_aws_transfer_server.go | 17 +++++++++++++++++ website/docs/r/transfer_server.html.markdown | 4 ++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_transfer_server.go b/aws/resource_aws_transfer_server.go index 7b455f00fff..c19d62e809b 100644 --- a/aws/resource_aws_transfer_server.go +++ b/aws/resource_aws_transfer_server.go @@ -40,6 +40,7 @@ func resourceAwsTransferServer() *schema.Resource { Optional: true, ValidateFunc: validateArn, }, + "domain": { Type: schema.TypeString, Optional: true, @@ -269,6 +270,10 @@ func resourceAwsTransferServerCreate(d *schema.ResourceData, meta interface{}) e return err } + // TODO + // TODO You can edit the SecurityGroupIds property in the UpdateServer API only if you are changing the EndpointType from PUBLIC or VPC_ENDPOINT to VPC. To change security groups associated with your server's VPC endpoint after creation, use the Amazon EC2 ModifyVpcEndpoint API. + // TODO + input := &transfer.UpdateServerInput{ ServerId: aws.String(d.Id()), EndpointDetails: expandTransferEndpointDetails(d.Get("endpoint_details").([]interface{})[0].(map[string]interface{})), @@ -398,6 +403,10 @@ func resourceAwsTransferServerUpdate(d *schema.ResourceData, meta interface{}) e if d.HasChange("endpoint_details.0.address_allocation_ids") { stopFlag = true } + + // TODO + // TODO You can edit the SecurityGroupIds property in the UpdateServer API only if you are changing the EndpointType from PUBLIC or VPC_ENDPOINT to VPC. To change security groups associated with your server's VPC endpoint after creation, use the Amazon EC2 ModifyVpcEndpoint API. + // TODO } if d.HasChange("host_key") { @@ -509,6 +518,10 @@ func expandTransferEndpointDetails(tfMap map[string]interface{}) *transfer.Endpo apiObject.AddressAllocationIds = expandStringSet(v) } + if v, ok := tfMap["security_group_ids"].(*schema.Set); ok && v.Len() > 0 { + apiObject.SecurityGroupIds = expandStringSet(v) + } + if v, ok := tfMap["subnet_ids"].(*schema.Set); ok && v.Len() > 0 { apiObject.SubnetIds = expandStringSet(v) } @@ -535,6 +548,10 @@ func flattenTransferEndpointDetails(apiObject *transfer.EndpointDetails) map[str tfMap["address_allocation_ids"] = aws.StringValueSlice(v) } + if v := apiObject.SecurityGroupIds; v != nil { + tfMap["security_group_ids"] = aws.StringValueSlice(v) + } + if v := apiObject.SubnetIds; v != nil { tfMap["subnet_ids"] = aws.StringValueSlice(v) } diff --git a/website/docs/r/transfer_server.html.markdown b/website/docs/r/transfer_server.html.markdown index ef4616f87e5..5ca1f12a3de 100644 --- a/website/docs/r/transfer_server.html.markdown +++ b/website/docs/r/transfer_server.html.markdown @@ -104,11 +104,11 @@ The following arguments are supported: **endpoint_details** requires the following: -* `vpc_endpoint_id` - (Optional) The ID of the VPC endpoint. This property can only be used when `endpoint_type` is set to `VPC_ENDPOINT` * `address_allocation_ids` - (Optional) A list of address allocation IDs that are required to attach an Elastic IP address to your SFTP server's endpoint. This property can only be used when `endpoint_type` is set to `VPC`. +* `security_group_ids` - (Optional) A list of security groups IDs that are available to attach to your server's endpoint. If no security groups are specified, the VPC's default security groups are automatically assigned to your endpoint. This property can only be used when `endpoint_type` is set to `VPC`. * `subnet_ids` - (Optional) A list of subnet IDs that are required to host your SFTP server endpoint in your VPC. This property can only be used when `endpoint_type` is set to `VPC`. +* `vpc_endpoint_id` - (Optional) The ID of the VPC endpoint. This property can only be used when `endpoint_type` is set to `VPC_ENDPOINT` * `vpc_id` - (Optional) The VPC ID of the virtual private cloud in which the SFTP server's endpoint will be hosted. This property can only be used when `endpoint_type` is set to `VPC`. -* `security_group_ids` - (Optional) A list of Security Groups Ids. This property can only be used when `endpoint_type` is set to `VPC`. It can't be change after transfer server creation. ## Attributes Reference In addition to all arguments above, the following attributes are exported: From a470bb6cef96cec15b5d5c72424f1f8e9e1faa56 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 28 Jun 2021 17:17:15 -0400 Subject: [PATCH 088/398] r/aws_transfer_server: Enhance 'TestAccAWSTransferServer_vpc'. --- aws/resource_aws_transfer_server_test.go | 42 +++++++++++++++++++----- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/aws/resource_aws_transfer_server_test.go b/aws/resource_aws_transfer_server_test.go index 1480ef6872c..1d924260e0a 100644 --- a/aws/resource_aws_transfer_server_test.go +++ b/aws/resource_aws_transfer_server_test.go @@ -228,6 +228,11 @@ func TestAccAWSTransferServer_securityPolicy(t *testing.T) { func TestAccAWSTransferServer_vpc(t *testing.T) { var conf transfer.DescribedServer resourceName := "aws_transfer_server.test" + eip1ResourceName := "aws_eip.test.0" + eip2ResourceName := "aws_eip.test.0" + defaultSecurityGroupResourceName := "aws_default_security_group.test" + subnetResourceName := "aws_subnet.test" + vpcResourceName := "aws_vpc.test" rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ @@ -237,12 +242,17 @@ func TestAccAWSTransferServer_vpc(t *testing.T) { CheckDestroy: testAccCheckAWSTransferServerDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSTransferServerVpcConfig(rName), + Config: testAccAWSTransferServerVpcNoSecurityGroupsConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "1"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.address_allocation_ids.*", eip1ResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", defaultSecurityGroupResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.subnet_ids.*", subnetResourceName, "id"), + resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), ), }, { @@ -252,11 +262,17 @@ func TestAccAWSTransferServer_vpc(t *testing.T) { ImportStateVerifyIgnore: []string{"force_destroy"}, }, { - Config: testAccAWSTransferServerVpcUpdateConfig(rName), + Config: testAccAWSTransferServerVpcNoSecurityGroupsUpdateConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.address_allocation_ids.*", eip2ResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", defaultSecurityGroupResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.subnet_ids.*", subnetResourceName, "id"), + resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), ), }, }, @@ -468,7 +484,7 @@ func TestAccAWSTransferServer_vpcEndpointId(t *testing.T) { CheckDestroy: testAccCheckAWSTransferServerDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSTransferServerVpcEndPointConfig(rName), + Config: testAccAWSTransferServerVpcEndpointConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC_ENDPOINT"), @@ -804,7 +820,7 @@ resource "aws_transfer_ssh_key" "test" { `, rName) } -func testAccAWSTransferServerVpcEndPointConfig(rName string) string { +func testAccAWSTransferServerVpcEndpointConfig(rName string) string { return composeConfig( testAccAWSTransferServerConfigBaseVpc(rName), fmt.Sprintf(` @@ -836,7 +852,7 @@ resource "aws_transfer_server" "test" { `, rName)) } -func testAccAWSTransferServerVpcConfig(rName string) string { +func testAccAWSTransferServerVpcNoSecurityGroupsConfig(rName string) string { return composeConfig( testAccAWSTransferServerConfigBaseVpc(rName), fmt.Sprintf(` @@ -850,19 +866,23 @@ resource "aws_eip" "test" { } } +resource "aws_default_security_group" "test" { + vpc_id = aws_vpc.test.id +} + resource "aws_transfer_server" "test" { endpoint_type = "VPC" endpoint_details { address_allocation_ids = [aws_eip.test[0].id] subnet_ids = [aws_subnet.test.id] - vpc_id = aws_vpc.test.id + vpc_id = aws_default_security_group.test.vpc_id } } `, rName)) } -func testAccAWSTransferServerVpcUpdateConfig(rName string) string { +func testAccAWSTransferServerVpcNoSecurityGroupsUpdateConfig(rName string) string { return composeConfig( testAccAWSTransferServerConfigBaseVpc(rName), fmt.Sprintf(` @@ -876,13 +896,17 @@ resource "aws_eip" "test" { } } +resource "aws_default_security_group" "test" { + vpc_id = aws_vpc.test.id +} + resource "aws_transfer_server" "test" { endpoint_type = "VPC" endpoint_details { address_allocation_ids = [aws_eip.test[1].id] subnet_ids = [aws_subnet.test.id] - vpc_id = aws_vpc.test.id + vpc_id = aws_default_security_group.test.vpc_id } } `, rName)) From 5374d7c76669df292114aa8232ac29298b362426 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Tue, 29 Jun 2021 09:42:59 +0300 Subject: [PATCH 089/398] fmt --- aws/resource_aws_cloudwatch_event_target.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aws/resource_aws_cloudwatch_event_target.go b/aws/resource_aws_cloudwatch_event_target.go index f793498ddb0..eae0046ad53 100644 --- a/aws/resource_aws_cloudwatch_event_target.go +++ b/aws/resource_aws_cloudwatch_event_target.go @@ -367,7 +367,7 @@ func resourceAwsCloudWatchEventTargetCreate(d *schema.ResourceData, meta interfa busName = v.(string) } - input := buildPutTargetInputStruct(d, meta) + input := buildPutTargetInputStruct(d) log.Printf("[DEBUG] Creating CloudWatch Events Target: %s", input) out, err := conn.PutTargets(input) @@ -474,7 +474,7 @@ func resourceAwsCloudWatchEventTargetRead(d *schema.ResourceData, meta interface func resourceAwsCloudWatchEventTargetUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).cloudwatcheventsconn - input := buildPutTargetInputStruct(d, meta) + input := buildPutTargetInputStruct(d) log.Printf("[DEBUG] Updating CloudWatch Events Target: %s", input) _, err := conn.PutTargets(input) @@ -513,7 +513,7 @@ func resourceAwsCloudWatchEventTargetDelete(d *schema.ResourceData, meta interfa return nil } -func buildPutTargetInputStruct(d *schema.ResourceData, meta interface{}) *events.PutTargetsInput { +func buildPutTargetInputStruct(d *schema.ResourceData) *events.PutTargetsInput { e := &events.Target{ Arn: aws.String(d.Get("arn").(string)), Id: aws.String(d.Get("target_id").(string)), From 3ebf3a27db02a4fbf35468200be95ff3a61b57b6 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 29 Jun 2021 09:13:27 -0400 Subject: [PATCH 090/398] r/aws_transfer_server: Security Group IDs are not Computed. --- aws/internal/service/transfer/enum.go | 15 +++++++++++++++ aws/resource_aws_transfer_server.go | 18 +++++------------- aws/resource_aws_transfer_server_test.go | 21 +++++---------------- 3 files changed, 25 insertions(+), 29 deletions(-) create mode 100644 aws/internal/service/transfer/enum.go diff --git a/aws/internal/service/transfer/enum.go b/aws/internal/service/transfer/enum.go new file mode 100644 index 00000000000..880b435fa83 --- /dev/null +++ b/aws/internal/service/transfer/enum.go @@ -0,0 +1,15 @@ +package transfer + +const ( + SecurityPolicyName2018_11 = "TransferSecurityPolicy-2018-11" + SecurityPolicyName2020_06 = "TransferSecurityPolicy-2020-06" + SecurityPolicyNameFIPS_2020_06 = "TransferSecurityPolicy-FIPS-2020-06" +) + +func SecurityPolicyName_Values() []string { + return []string{ + SecurityPolicyName2018_11, + SecurityPolicyName2020_06, + SecurityPolicyNameFIPS_2020_06, + } +} diff --git a/aws/resource_aws_transfer_server.go b/aws/resource_aws_transfer_server.go index c19d62e809b..207cec30cba 100644 --- a/aws/resource_aws_transfer_server.go +++ b/aws/resource_aws_transfer_server.go @@ -64,29 +64,25 @@ func resourceAwsTransferServer() *schema.Resource { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, ConflictsWith: []string{"endpoint_details.0.vpc_endpoint_id"}, }, "security_group_ids": { Type: schema.TypeSet, Optional: true, - Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, ConflictsWith: []string{"endpoint_details.0.vpc_endpoint_id"}, }, "subnet_ids": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, ConflictsWith: []string{"endpoint_details.0.vpc_endpoint_id"}, }, "vpc_endpoint_id": { Type: schema.TypeString, Optional: true, - ConflictsWith: []string{"endpoint_details.0.address_allocation_ids", "endpoint_details.0.security_group_ids", "endpoint_details.0.subnet_ids", "endpoint_details.0.vpc_id"}, Computed: true, + ConflictsWith: []string{"endpoint_details.0.address_allocation_ids", "endpoint_details.0.security_group_ids", "endpoint_details.0.subnet_ids", "endpoint_details.0.vpc_id"}, }, "vpc_id": { Type: schema.TypeString, @@ -156,14 +152,10 @@ func resourceAwsTransferServer() *schema.Resource { }, "security_policy_name": { - Type: schema.TypeString, - Optional: true, - Default: "TransferSecurityPolicy-2018-11", - ValidateFunc: validation.StringInSlice([]string{ - "TransferSecurityPolicy-2018-11", - "TransferSecurityPolicy-2020-06", - "TransferSecurityPolicy-FIPS-2020-06", - }, false), + Type: schema.TypeString, + Optional: true, + Default: tftransfer.SecurityPolicyName2018_11, + ValidateFunc: validation.StringInSlice(tftransfer.SecurityPolicyName_Values(), false), }, "tags": tagsSchema(), diff --git a/aws/resource_aws_transfer_server_test.go b/aws/resource_aws_transfer_server_test.go index 1d924260e0a..fc02972f641 100644 --- a/aws/resource_aws_transfer_server_test.go +++ b/aws/resource_aws_transfer_server_test.go @@ -229,8 +229,7 @@ func TestAccAWSTransferServer_vpc(t *testing.T) { var conf transfer.DescribedServer resourceName := "aws_transfer_server.test" eip1ResourceName := "aws_eip.test.0" - eip2ResourceName := "aws_eip.test.0" - defaultSecurityGroupResourceName := "aws_default_security_group.test" + eip2ResourceName := "aws_eip.test.1" subnetResourceName := "aws_subnet.test" vpcResourceName := "aws_vpc.test" rName := acctest.RandomWithPrefix("tf-acc-test") @@ -248,8 +247,7 @@ func TestAccAWSTransferServer_vpc(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.address_allocation_ids.*", eip1ResourceName, "id"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), - resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", defaultSecurityGroupResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "0"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.subnet_ids.*", subnetResourceName, "id"), resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), @@ -268,8 +266,7 @@ func TestAccAWSTransferServer_vpc(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.address_allocation_ids.*", eip2ResourceName, "id"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), - resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", defaultSecurityGroupResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "0"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.subnet_ids.*", subnetResourceName, "id"), resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), @@ -866,17 +863,13 @@ resource "aws_eip" "test" { } } -resource "aws_default_security_group" "test" { - vpc_id = aws_vpc.test.id -} - resource "aws_transfer_server" "test" { endpoint_type = "VPC" endpoint_details { address_allocation_ids = [aws_eip.test[0].id] subnet_ids = [aws_subnet.test.id] - vpc_id = aws_default_security_group.test.vpc_id + vpc_id = aws_vpc.test.id } } `, rName)) @@ -896,17 +889,13 @@ resource "aws_eip" "test" { } } -resource "aws_default_security_group" "test" { - vpc_id = aws_vpc.test.id -} - resource "aws_transfer_server" "test" { endpoint_type = "VPC" endpoint_details { address_allocation_ids = [aws_eip.test[1].id] subnet_ids = [aws_subnet.test.id] - vpc_id = aws_default_security_group.test.vpc_id + vpc_id = aws_vpc.test.id } } `, rName)) From d320a06afe2f5c14aa64778911b13d5223974e4e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 17 Jun 2021 15:13:27 -0400 Subject: [PATCH 091/398] r/aws_transfer_server: 'force_destroy' only applies to SERVICE_MANAGED identity providers. Acceptance test output: % TEST=./aws SWEEP=us-east-1,us-east-2,us-west-1,us-west-2 SWEEPARGS=-sweep-run=aws_transfer_server make sweep WARNING: This will destroy infrastructure. Use only in development accounts. go test ./aws -v -sweep=us-east-1,us-east-2,us-west-1,us-west-2 -sweep-run=aws_transfer_server -timeout 60m 2021/06/17 15:10:15 [DEBUG] Running Sweepers for region (us-east-1): 2021/06/17 15:10:15 [DEBUG] Running Sweeper (aws_transfer_server) in region (us-east-1) 2021/06/17 15:10:15 [INFO] AWS Auth provider used: "EnvProvider" 2021/06/17 15:10:15 [DEBUG] Trying to get account information via sts:GetCallerIdentity 2021/06/17 15:10:15 [DEBUG] Trying to get account information via sts:GetCallerIdentity 2021/06/17 15:10:15 Sweeper Tests ran successfully: - aws_transfer_server 2021/06/17 15:10:15 [DEBUG] Running Sweepers for region (us-east-2): 2021/06/17 15:10:15 [DEBUG] Running Sweeper (aws_transfer_server) in region (us-east-2) 2021/06/17 15:10:15 [INFO] AWS Auth provider used: "EnvProvider" 2021/06/17 15:10:15 [DEBUG] Trying to get account information via sts:GetCallerIdentity 2021/06/17 15:10:15 [DEBUG] Trying to get account information via sts:GetCallerIdentity 2021/06/17 15:10:16 Sweeper Tests ran successfully: - aws_transfer_server 2021/06/17 15:10:16 [DEBUG] Running Sweepers for region (us-west-1): 2021/06/17 15:10:16 [DEBUG] Running Sweeper (aws_transfer_server) in region (us-west-1) 2021/06/17 15:10:16 [INFO] AWS Auth provider used: "EnvProvider" 2021/06/17 15:10:16 [DEBUG] Trying to get account information via sts:GetCallerIdentity 2021/06/17 15:10:16 [DEBUG] Trying to get account information via sts:GetCallerIdentity 2021/06/17 15:10:17 Sweeper Tests ran successfully: - aws_transfer_server 2021/06/17 15:10:17 [DEBUG] Running Sweepers for region (us-west-2): 2021/06/17 15:10:17 [DEBUG] Running Sweeper (aws_transfer_server) in region (us-west-2) 2021/06/17 15:10:17 [INFO] AWS Auth provider used: "EnvProvider" 2021/06/17 15:10:17 [DEBUG] Trying to get account information via sts:GetCallerIdentity 2021/06/17 15:10:17 [DEBUG] Trying to get account information via sts:GetCallerIdentity 2021/06/17 15:10:19 [DEBUG] Deleting Transfer Server: (s-b61cc3bc5a0e40888) 2021/06/17 15:10:19 [DEBUG] Deleting Transfer Server: (s-a26280943c9345d0b) 2021/06/17 15:10:20 [DEBUG] Waiting for state to become: [] 2021/06/17 15:10:20 [DEBUG] Waiting for state to become: [] 2021/06/17 15:10:21 Sweeper Tests ran successfully: - aws_transfer_server ok github.com/terraform-providers/terraform-provider-aws/aws 8.971s --- aws/resource_aws_transfer_server.go | 18 ++---- aws/resource_aws_transfer_server_test.go | 68 +++++++++++++++----- aws/resource_aws_transfer_user.go | 28 +++++--- website/docs/r/transfer_server.html.markdown | 2 +- 4 files changed, 80 insertions(+), 36 deletions(-) diff --git a/aws/resource_aws_transfer_server.go b/aws/resource_aws_transfer_server.go index 207cec30cba..628cd9173c9 100644 --- a/aws/resource_aws_transfer_server.go +++ b/aws/resource_aws_transfer_server.go @@ -8,12 +8,11 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/transfer" "github.com/hashicorp/aws-sdk-go-base/tfawserr" - "github.com/hashicorp/go-multierror" + multierror "github.com/hashicorp/go-multierror" "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" - tftransfer "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/transfer" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/transfer/finder" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/transfer/waiter" "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" @@ -438,7 +437,7 @@ func resourceAwsTransferServerUpdate(d *schema.ResourceData, meta interface{}) e func resourceAwsTransferServerDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).transferconn - if d.Get("force_destroy").(bool) { + if d.Get("force_destroy").(bool) && d.Get("identity_provider_type").(string) == transfer.IdentityProviderTypeServiceManaged { input := &transfer.ListUsersInput{ ServerId: aws.String(d.Id()), } @@ -450,15 +449,12 @@ func resourceAwsTransferServerDelete(d *schema.ResourceData, meta interface{}) e } for _, user := range page.Users { - resourceID := tftransfer.UserCreateResourceID(d.Id(), aws.StringValue(user.UserName)) - - r := resourceAwsTransferUser() - d := r.Data(nil) - d.SetId(resourceID) - err := r.Delete(d, meta) + err := transferUserDelete(conn, d.Id(), aws.StringValue(user.UserName)) if err != nil { - deletionErrs = multierror.Append(deletionErrs, fmt.Errorf("error deleting Transfer User (%s): %w", resourceID, err)) + log.Printf("[ERROR] %s", err) + deletionErrs = multierror.Append(deletionErrs, err) + continue } } @@ -477,7 +473,7 @@ func resourceAwsTransferServerDelete(d *schema.ResourceData, meta interface{}) e } } - log.Printf("[DEBUG] Deleting Transfer Server (%s)", d.Id()) + log.Printf("[DEBUG] Deleting Transfer Server: (%s)", d.Id()) _, err := conn.DeleteServer(&transfer.DeleteServerInput{ ServerId: aws.String(d.Id()), }) diff --git a/aws/resource_aws_transfer_server_test.go b/aws/resource_aws_transfer_server_test.go index fc02972f641..d00b4ff9c3f 100644 --- a/aws/resource_aws_transfer_server_test.go +++ b/aws/resource_aws_transfer_server_test.go @@ -9,7 +9,6 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/acmpca" "github.com/aws/aws-sdk-go/service/transfer" - "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -33,7 +32,7 @@ func testSweepTransferServers(region string) error { } conn := client.(*AWSClient).transferconn input := &transfer.ListServersInput{} - var sweeperErrs *multierror.Error + sweepResources := make([]*testSweepResource, 0) err = conn.ListServersPages(input, func(page *transfer.ListServersOutput, lastPage bool) bool { if page == nil { @@ -45,13 +44,9 @@ func testSweepTransferServers(region string) error { d := r.Data(nil) d.SetId(aws.StringValue(server.ServerId)) d.Set("force_destroy", true) // In lieu of an aws_transfer_user sweeper. - err = r.Delete(d, client) + d.Set("identity_provider_type", server.IdentityProviderType) - if err != nil { - log.Printf("[ERROR] %s", err) - sweeperErrs = multierror.Append(sweeperErrs, err) - continue - } + sweepResources = append(sweepResources, NewTestSweepResource(r, d, client)) } return !lastPage @@ -59,14 +54,20 @@ func testSweepTransferServers(region string) error { if testSweepSkipSweepError(err) { log.Printf("[WARN] Skipping Transfer Server sweep for %s: %s", region, err) - return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors + return nil + } + + if err != nil { + return fmt.Errorf("error listing Transfer Servers (%s): %w", region, err) } + err = testSweepResourceOrchestrator(sweepResources) + if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing Transfer Servers: %w", err)) + return fmt.Errorf("error sweeping Transfer Servers (%s): %w", region, err) } - return sweeperErrs.ErrorOrNil() + return nil } func testAccErrorCheckSkipTransfer(t *testing.T) resource.ErrorCheckFunc { @@ -392,13 +393,48 @@ func TestAccAWSTransferServer_apiGateway(t *testing.T) { CheckDestroy: testAccCheckAWSTransferServerDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSTransferServerApiGatewayIdentityProviderTypeConfig(rName), + Config: testAccAWSTransferServerApiGatewayIdentityProviderTypeConfig(rName, false), Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "identity_provider_type", "API_GATEWAY"), resource.TestCheckResourceAttrPair(resourceName, "invocation_role", "aws_iam_role.test", "arn"), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"force_destroy"}, + }, + }, + }) +} + +func TestAccAWSTransferServer_apiGateway_forceDestroy(t *testing.T) { + var conf transfer.DescribedServer + resourceName := "aws_transfer_server.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccAPIGatewayTypeEDGEPreCheck(t); testAccPreCheckAWSTransfer(t) }, + ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSTransferServerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSTransferServerApiGatewayIdentityProviderTypeConfig(rName, true), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTransferServerExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "identity_provider_type", "API_GATEWAY"), + resource.TestCheckResourceAttrPair(resourceName, "invocation_role", "aws_iam_role.test", "arn"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"force_destroy"}, + }, }, }) } @@ -747,18 +783,20 @@ resource "aws_transfer_server" "test" { `) } -func testAccAWSTransferServerApiGatewayIdentityProviderTypeConfig(rName string) string { +func testAccAWSTransferServerApiGatewayIdentityProviderTypeConfig(rName string, forceDestroy bool) string { return composeConfig( testAccAWSTransferServerConfigBaseApiGateway(rName), testAccAWSTransferServerConfigBaseLoggingRole(rName), - ` + fmt.Sprintf(` resource "aws_transfer_server" "test" { identity_provider_type = "API_GATEWAY" url = "${aws_api_gateway_deployment.test.invoke_url}${aws_api_gateway_resource.test.path}" invocation_role = aws_iam_role.test.arn logging_role = aws_iam_role.test.arn + + force_destroy = %[1]t } -`) +`, forceDestroy)) } func testAccAWSTransferServerForceDestroyConfig(rName string) string { diff --git a/aws/resource_aws_transfer_user.go b/aws/resource_aws_transfer_user.go index 61f8fdc5fd3..b34f1ea9b59 100644 --- a/aws/resource_aws_transfer_user.go +++ b/aws/resource_aws_transfer_user.go @@ -6,6 +6,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/transfer" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "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" @@ -288,30 +289,39 @@ func resourceAwsTransferUserUpdate(d *schema.ResourceData, meta interface{}) err func resourceAwsTransferUserDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).transferconn + serverID, userName, err := tftransfer.UserParseResourceID(d.Id()) + if err != nil { return fmt.Errorf("error parsing Transfer User ID: %w", err) } - delOpts := &transfer.DeleteUserInput{ - UserName: aws.String(userName), + return transferUserDelete(conn, serverID, userName) +} + +// transferUserDelete attempts to delete a transfer user. +func transferUserDelete(conn *transfer.Transfer, serverID, userName string) error { + id := fmt.Sprintf("%s/%s", serverID, userName) + input := &transfer.DeleteUserInput{ ServerId: aws.String(serverID), + UserName: aws.String(userName), } - log.Printf("[DEBUG] Delete Transfer User Option: %#v", delOpts) + log.Printf("[INFO] Deleting Transfer User: %s", id) + _, err := conn.DeleteUser(input) + + if tfawserr.ErrCodeEquals(err, transfer.ErrCodeResourceNotFoundException) { + return nil + } - _, err = conn.DeleteUser(delOpts) if err != nil { - if isAWSErr(err, transfer.ErrCodeResourceNotFoundException, "") { - return nil - } - return fmt.Errorf("error deleting Transfer User (%s) for Server(%s): %w", userName, serverID, err) + return fmt.Errorf("error deleting Transfer User (%s): %w", id, err) } _, err = waiter.UserDeleted(conn, serverID, userName) if err != nil { - return fmt.Errorf("error waiting for Transfer User (%s) delete: %w", d.Id(), err) + return fmt.Errorf("error waiting for Transfer User (%s) delete: %w", id, err) } return nil diff --git a/website/docs/r/transfer_server.html.markdown b/website/docs/r/transfer_server.html.markdown index 5ca1f12a3de..257f4703b11 100644 --- a/website/docs/r/transfer_server.html.markdown +++ b/website/docs/r/transfer_server.html.markdown @@ -98,7 +98,7 @@ The following arguments are supported: * `url` - (Optional) - URL of the service endpoint used to authenticate users with an `identity_provider_type` of `API_GATEWAY`. * `identity_provider_type` - (Optional) The mode of authentication enabled for this service. The default value is `SERVICE_MANAGED`, which allows you to store and access SFTP user credentials within the service. `API_GATEWAY` indicates that user authentication requires a call to an API Gateway endpoint URL provided by you to integrate an identity provider of your choice. * `logging_role` - (Optional) Amazon Resource Name (ARN) of an IAM role that allows the service to write your SFTP users’ activity to your Amazon CloudWatch logs for monitoring and auditing purposes. -* `force_destroy` - (Optional) A boolean that indicates all users associated with the server should be deleted so that the Server can be destroyed without error. The default value is `false`. +* `force_destroy` - (Optional) A boolean that indicates all users associated with the server should be deleted so that the Server can be destroyed without error. The default value is `false`. This option only applies to servers configured with a `SERVICE_MANAGED` `identity_provider_type`. * `security_policy_name` - (Optional) Specifies the name of the security policy that is attached to the server. Possible values are `TransferSecurityPolicy-2018-11`, `TransferSecurityPolicy-2020-06`, and `TransferSecurityPolicy-FIPS-2020-06`. Default value is: `TransferSecurityPolicy-2018-11`. * `tags` - (Optional) A map of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](https://www.terraform.io/docs/providers/aws/index.html#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. From 4a36d75bfe0f3f5be2fae083e0c0d569750e8588 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 17 Jun 2021 17:03:27 -0400 Subject: [PATCH 092/398] Serialize Transfer acceptance tests. --- aws/resource_aws_transfer_server_test.go | 48 +++++++++++------------ aws/resource_aws_transfer_ssh_key_test.go | 4 +- aws/resource_aws_transfer_test.go | 46 ++++++++++++++++++++++ aws/resource_aws_transfer_user_test.go | 24 ++++++------ 4 files changed, 84 insertions(+), 38 deletions(-) create mode 100644 aws/resource_aws_transfer_test.go diff --git a/aws/resource_aws_transfer_server_test.go b/aws/resource_aws_transfer_server_test.go index d00b4ff9c3f..efd9601cd4a 100644 --- a/aws/resource_aws_transfer_server_test.go +++ b/aws/resource_aws_transfer_server_test.go @@ -76,13 +76,13 @@ func testAccErrorCheckSkipTransfer(t *testing.T) resource.ErrorCheckFunc { ) } -func TestAccAWSTransferServer_basic(t *testing.T) { +func testAccAWSTransferServer_basic(t *testing.T) { var conf transfer.DescribedServer resourceName := "aws_transfer_server.test" iamRoleResourceName := "aws_iam_role.test" rName := acctest.RandomWithPrefix("tf-acc-test") - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTransfer(t) }, ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), Providers: testAccProviders, @@ -143,11 +143,11 @@ func TestAccAWSTransferServer_basic(t *testing.T) { }) } -func TestAccAWSTransferServer_domain(t *testing.T) { +func testAccAWSTransferServer_domain(t *testing.T) { var conf transfer.DescribedServer resourceName := "aws_transfer_server.test" - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTransfer(t) }, ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), Providers: testAccProviders, @@ -170,11 +170,11 @@ func TestAccAWSTransferServer_domain(t *testing.T) { }) } -func TestAccAWSTransferServer_disappears(t *testing.T) { +func testAccAWSTransferServer_disappears(t *testing.T) { var conf transfer.DescribedServer resourceName := "aws_transfer_server.test" - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTransfer(t) }, ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), Providers: testAccProviders, @@ -192,11 +192,11 @@ func TestAccAWSTransferServer_disappears(t *testing.T) { }) } -func TestAccAWSTransferServer_securityPolicy(t *testing.T) { +func testAccAWSTransferServer_securityPolicy(t *testing.T) { var conf transfer.DescribedServer resourceName := "aws_transfer_server.test" - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTransfer(t) }, ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), Providers: testAccProviders, @@ -226,7 +226,7 @@ func TestAccAWSTransferServer_securityPolicy(t *testing.T) { }) } -func TestAccAWSTransferServer_vpc(t *testing.T) { +func testAccAWSTransferServer_vpc(t *testing.T) { var conf transfer.DescribedServer resourceName := "aws_transfer_server.test" eip1ResourceName := "aws_eip.test.0" @@ -235,7 +235,7 @@ func TestAccAWSTransferServer_vpc(t *testing.T) { vpcResourceName := "aws_vpc.test" rName := acctest.RandomWithPrefix("tf-acc-test") - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTransfer(t) }, ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), Providers: testAccProviders, @@ -279,12 +279,12 @@ func TestAccAWSTransferServer_vpc(t *testing.T) { // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/16556 /* -func TestAccAWSTransferServer_updateEndpointType(t *testing.T) { +func testAccAWSTransferServer_updateEndpointType(t *testing.T) { var conf transfer.DescribedServer resourceName := "aws_transfer_server.test" rName := acctest.RandomWithPrefix("tf-acc-test") - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTransfer(t) }, ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), Providers: testAccProviders, @@ -318,7 +318,7 @@ func TestAccAWSTransferServer_updateEndpointType(t *testing.T) { } */ -func TestAccAWSTransferServer_protocols(t *testing.T) { +func testAccAWSTransferServer_protocols(t *testing.T) { var s transfer.DescribedServer var ca acmpca.CertificateAuthority resourceName := "aws_transfer_server.test" @@ -326,7 +326,7 @@ func TestAccAWSTransferServer_protocols(t *testing.T) { acmCertificateResourceName := "aws_acm_certificate.test" rName := acctest.RandomWithPrefix("tf-acc-test") - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccAPIGatewayTypeEDGEPreCheck(t); testAccPreCheckAWSTransfer(t) }, ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), Providers: testAccProviders, @@ -381,12 +381,12 @@ func TestAccAWSTransferServer_protocols(t *testing.T) { }) } -func TestAccAWSTransferServer_apiGateway(t *testing.T) { +func testAccAWSTransferServer_apiGateway(t *testing.T) { var conf transfer.DescribedServer resourceName := "aws_transfer_server.test" rName := acctest.RandomWithPrefix("tf-acc-test") - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccAPIGatewayTypeEDGEPreCheck(t); testAccPreCheckAWSTransfer(t) }, ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), Providers: testAccProviders, @@ -410,12 +410,12 @@ func TestAccAWSTransferServer_apiGateway(t *testing.T) { }) } -func TestAccAWSTransferServer_apiGateway_forceDestroy(t *testing.T) { +func testAccAWSTransferServer_apiGateway_forceDestroy(t *testing.T) { var conf transfer.DescribedServer resourceName := "aws_transfer_server.test" rName := acctest.RandomWithPrefix("tf-acc-test") - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccAPIGatewayTypeEDGEPreCheck(t); testAccPreCheckAWSTransfer(t) }, ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), Providers: testAccProviders, @@ -439,7 +439,7 @@ func TestAccAWSTransferServer_apiGateway_forceDestroy(t *testing.T) { }) } -func TestAccAWSTransferServer_forceDestroy(t *testing.T) { +func testAccAWSTransferServer_forceDestroy(t *testing.T) { var s transfer.DescribedServer var u transfer.DescribedUser var k transfer.SshPublicKey @@ -448,7 +448,7 @@ func TestAccAWSTransferServer_forceDestroy(t *testing.T) { sshKeyResourceName := "aws_transfer_ssh_key.test" rName := acctest.RandomWithPrefix("tf-acc-test") - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTransfer(t) }, ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), Providers: testAccProviders, @@ -473,12 +473,12 @@ func TestAccAWSTransferServer_forceDestroy(t *testing.T) { }) } -func TestAccAWSTransferServer_hostKey(t *testing.T) { +func testAccAWSTransferServer_hostKey(t *testing.T) { var conf transfer.DescribedServer resourceName := "aws_transfer_server.test" hostKey := "test-fixtures/transfer-ssh-rsa-key" - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), Providers: testAccProviders, @@ -501,7 +501,7 @@ func TestAccAWSTransferServer_hostKey(t *testing.T) { }) } -func TestAccAWSTransferServer_vpcEndpointId(t *testing.T) { +func testAccAWSTransferServer_vpcEndpointId(t *testing.T) { var conf transfer.DescribedServer resourceName := "aws_transfer_server.test" rName := acctest.RandomWithPrefix("tf-acc-test") @@ -510,7 +510,7 @@ func TestAccAWSTransferServer_vpcEndpointId(t *testing.T) { t.Skip("Transfer Server VPC_ENDPOINT endpoint type is not supported in GovCloud partition") } - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTransfer(t) }, ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), Providers: testAccProviders, diff --git a/aws/resource_aws_transfer_ssh_key_test.go b/aws/resource_aws_transfer_ssh_key_test.go index 9437daee8f8..c7bc79c9c8d 100644 --- a/aws/resource_aws_transfer_ssh_key_test.go +++ b/aws/resource_aws_transfer_ssh_key_test.go @@ -11,11 +11,11 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) -func TestAccAWSTransferSshKey_basic(t *testing.T) { +func testAccAWSTransferSshKey_basic(t *testing.T) { var conf transfer.SshPublicKey rName := acctest.RandString(5) - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTransfer(t) }, ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), Providers: testAccProviders, diff --git a/aws/resource_aws_transfer_test.go b/aws/resource_aws_transfer_test.go new file mode 100644 index 00000000000..49a5ef44dad --- /dev/null +++ b/aws/resource_aws_transfer_test.go @@ -0,0 +1,46 @@ +package aws + +import ( + "testing" +) + +func TestAccAWSTransfer_serial(t *testing.T) { + testCases := map[string]map[string]func(t *testing.T){ + "Server": { + "basic": testAccAWSTransferServer_basic, + "disappears": testAccAWSTransferServer_disappears, + "APIGateway": testAccAWSTransferServer_apiGateway, + "APIGatewayForceDestroy": testAccAWSTransferServer_apiGateway_forceDestroy, + "Domain": testAccAWSTransferServer_domain, + "ForceDestroy": testAccAWSTransferServer_forceDestroy, + "HostKey": testAccAWSTransferServer_hostKey, + "Protocols": testAccAWSTransferServer_protocols, + "SecurityPolicy": testAccAWSTransferServer_securityPolicy, + "VPC": testAccAWSTransferServer_vpc, + "VPCEndpointID": testAccAWSTransferServer_vpcEndpointId, + }, + "SSHKey": { + "basic": testAccAWSTransferSshKey_basic, + }, + "User": { + "basic": testAccAWSTransferUser_basic, + "disappears": testAccAWSTransferUser_disappears, + "HomeDirectoryMappings": testAccAWSTransferUser_homeDirectoryMappings, + "ModifyWithOptions": testAccAWSTransferUser_modifyWithOptions, + "Posix": testAccAWSTransferUser_posix, + "UserNameValidation": testAccAWSTransferUser_UserName_Validation, + }, + } + + for group, m := range testCases { + m := m + t.Run(group, func(t *testing.T) { + for name, tc := range m { + tc := tc + t.Run(name, func(t *testing.T) { + tc(t) + }) + } + }) + } +} diff --git a/aws/resource_aws_transfer_user_test.go b/aws/resource_aws_transfer_user_test.go index c92e735b503..def98d4f753 100644 --- a/aws/resource_aws_transfer_user_test.go +++ b/aws/resource_aws_transfer_user_test.go @@ -13,12 +13,12 @@ import ( "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) -func TestAccAWSTransferUser_basic(t *testing.T) { +func testAccAWSTransferUser_basic(t *testing.T) { var conf transfer.DescribedUser resourceName := "aws_transfer_user.test" rName := acctest.RandString(10) - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTransfer(t) }, ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), Providers: testAccProviders, @@ -43,12 +43,12 @@ func TestAccAWSTransferUser_basic(t *testing.T) { }) } -func TestAccAWSTransferUser_posix(t *testing.T) { +func testAccAWSTransferUser_posix(t *testing.T) { var conf transfer.DescribedUser resourceName := "aws_transfer_user.test" rName := acctest.RandString(10) - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTransfer(t) }, ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), Providers: testAccProviders, @@ -82,13 +82,13 @@ func TestAccAWSTransferUser_posix(t *testing.T) { }) } -func TestAccAWSTransferUser_modifyWithOptions(t *testing.T) { +func testAccAWSTransferUser_modifyWithOptions(t *testing.T) { var conf transfer.DescribedUser resourceName := "aws_transfer_user.test" rName := acctest.RandString(10) rName2 := acctest.RandString(10) - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTransfer(t) }, ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), Providers: testAccProviders, @@ -129,13 +129,13 @@ func TestAccAWSTransferUser_modifyWithOptions(t *testing.T) { }) } -func TestAccAWSTransferUser_disappears(t *testing.T) { +func testAccAWSTransferUser_disappears(t *testing.T) { var serverConf transfer.DescribedServer var userConf transfer.DescribedUser rName := acctest.RandString(10) resourceName := "aws_transfer_user.test" - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTransfer(t) }, ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), Providers: testAccProviders, @@ -154,8 +154,8 @@ func TestAccAWSTransferUser_disappears(t *testing.T) { }) } -func TestAccAWSTransferUser_UserName_Validation(t *testing.T) { - resource.ParallelTest(t, resource.TestCase{ +func testAccAWSTransferUser_UserName_Validation(t *testing.T) { + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTransfer(t) }, ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), Providers: testAccProviders, @@ -191,12 +191,12 @@ func TestAccAWSTransferUser_UserName_Validation(t *testing.T) { }) } -func TestAccAWSTransferUser_homeDirectoryMappings(t *testing.T) { +func testAccAWSTransferUser_homeDirectoryMappings(t *testing.T) { var conf transfer.DescribedUser rName := acctest.RandString(10) resourceName := "aws_transfer_user.test" - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTransfer(t) }, ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), Providers: testAccProviders, From 47f3107079149fc42d4fd3e06f15a7293c563352 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 29 Jun 2021 09:59:31 -0400 Subject: [PATCH 093/398] r/aws_transfer_server: Prevent "InvalidRequestException: Changing VpcId is not supported". --- aws/resource_aws_transfer_server.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_transfer_server.go b/aws/resource_aws_transfer_server.go index 628cd9173c9..7d91952c85f 100644 --- a/aws/resource_aws_transfer_server.go +++ b/aws/resource_aws_transfer_server.go @@ -1,6 +1,7 @@ package aws import ( + "context" "fmt" "log" "time" @@ -9,10 +10,12 @@ import ( "github.com/aws/aws-sdk-go/service/transfer" "github.com/hashicorp/aws-sdk-go-base/tfawserr" multierror "github.com/hashicorp/go-multierror" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "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" + tftransfer "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/transfer" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/transfer/finder" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/transfer/waiter" "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" @@ -28,6 +31,18 @@ func resourceAwsTransferServer() *schema.Resource { State: schema.ImportStatePassthrough, }, + CustomizeDiff: customdiff.Sequence( + SetTagsDiff, + customdiff.ForceNewIfChange("endpoint_details.0.vpc_id", func(_ context.Context, old, new, meta interface{}) bool { + // "InvalidRequestException: Changing VpcId is not supported". + if old, new := old.(string), new.(string); old != "" && new != old { + return true + } + + return false + }), + ), + Schema: map[string]*schema.Schema{ "arn": { Type: schema.TypeString, @@ -165,8 +180,6 @@ func resourceAwsTransferServer() *schema.Resource { Optional: true, }, }, - - CustomizeDiff: SetTagsDiff, } } From 19d7ee958f6c0e4bceffadfaca234957af24381e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 29 Jun 2021 11:07:39 -0400 Subject: [PATCH 094/398] r/aws_transfer_server: Read security group IDs via EC2 DecsribeVpcEndpoints API. Acceptance test output (failures expected): % make testacc TEST=./aws TESTARGS='-run=TestAccAWSTransfer_serial/Server/VPC$' ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./aws -v -count 1 -parallel 20 -run=TestAccAWSTransfer_serial/Server/VPC -timeout 180m === RUN TestAccAWSTransfer_serial === RUN TestAccAWSTransfer_serial/Server === RUN TestAccAWSTransfer_serial/Server/VPCAddressAllocationIDs resource_aws_transfer_server_test.go:288: Step 3/3 error: Error running apply: exit status 1 2021/06/29 10:59:43 [DEBUG] Using modified User-Agent: Terraform/0.12.31 HashiCorp-terraform-exec/0.13.3 Error: error updating Transfer Server (s-45159289697f40d29): InvalidRequestException: Changing Security Group is not supported on terraform_plugin_test.tf line 66, in resource "aws_transfer_server" "test": 66: resource "aws_transfer_server" "test" { === RUN TestAccAWSTransfer_serial/Server/VPC resource_aws_transfer_server_test.go:237: Step 3/3 error: Error running apply: exit status 1 2021/06/29 11:00:57 [DEBUG] Using modified User-Agent: Terraform/0.12.31 HashiCorp-terraform-exec/0.13.3 Error: error updating Transfer Server (s-e9f52c9d825845e5a): InvalidRequestException: Changing Security Group is not supported on terraform_plugin_test.tf line 56, in resource "aws_transfer_server" "test": 56: resource "aws_transfer_server" "test" { === RUN TestAccAWSTransfer_serial/Server/VPCEndpointID === RUN TestAccAWSTransfer_serial/Server/VPCSecurityGroupIDs resource_aws_transfer_server_test.go:340: Step 3/3 error: Error running apply: exit status 1 2021/06/29 11:03:44 [DEBUG] Using modified User-Agent: Terraform/0.12.31 HashiCorp-terraform-exec/0.13.3 Error: error updating Transfer Server (s-1e131250f9944c45a): InvalidRequestException: Changing Security Group is not supported on terraform_plugin_test.tf line 65, in resource "aws_transfer_server" "test": 65: resource "aws_transfer_server" "test" { --- FAIL: TestAccAWSTransfer_serial (411.21s) --- FAIL: TestAccAWSTransfer_serial/Server (411.21s) --- FAIL: TestAccAWSTransfer_serial/Server/VPCAddressAllocationIDs (185.57s) --- FAIL: TestAccAWSTransfer_serial/Server/VPC (58.73s) --- PASS: TestAccAWSTransfer_serial/Server/VPCEndpointID (106.22s) --- FAIL: TestAccAWSTransfer_serial/Server/VPCSecurityGroupIDs (60.69s) FAIL FAIL github.com/terraform-providers/terraform-provider-aws/aws 414.320s FAIL make: *** [testacc] Error 1 --- aws/resource_aws_transfer_server.go | 26 +++- aws/resource_aws_transfer_server_test.go | 184 ++++++++++++++++++++++- aws/resource_aws_transfer_test.go | 24 +-- 3 files changed, 214 insertions(+), 20 deletions(-) diff --git a/aws/resource_aws_transfer_server.go b/aws/resource_aws_transfer_server.go index 7d91952c85f..f249bd117d1 100644 --- a/aws/resource_aws_transfer_server.go +++ b/aws/resource_aws_transfer_server.go @@ -15,6 +15,7 @@ import ( "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" + ec2finder "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2/finder" tftransfer "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/transfer" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/transfer/finder" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/transfer/waiter" @@ -83,6 +84,7 @@ func resourceAwsTransferServer() *schema.Resource { "security_group_ids": { Type: schema.TypeSet, Optional: true, + Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, ConflictsWith: []string{"endpoint_details.0.vpc_endpoint_id"}, }, @@ -317,7 +319,23 @@ func resourceAwsTransferServerRead(d *schema.ResourceData, meta interface{}) err d.Set("domain", output.Domain) d.Set("endpoint", meta.(*AWSClient).RegionalHostname(fmt.Sprintf("%s.server.transfer", d.Id()))) if output.EndpointDetails != nil { - if err := d.Set("endpoint_details", []interface{}{flattenTransferEndpointDetails(output.EndpointDetails)}); err != nil { + securityGroupIDs := make([]*string, 0) + + // Security Group IDs are not returned for VPC endpoints. + if aws.StringValue(output.EndpointType) == transfer.EndpointTypeVpc && len(output.EndpointDetails.SecurityGroupIds) == 0 { + vpcEndpointID := aws.StringValue(output.EndpointDetails.VpcEndpointId) + output, err := ec2finder.VpcEndpointByID(meta.(*AWSClient).ec2conn, vpcEndpointID) + + if err != nil { + return fmt.Errorf("error reading Transfer Server (%s) VPC Endpoint (%s): %w", d.Id(), vpcEndpointID, err) + } + + for _, group := range output.Groups { + securityGroupIDs = append(securityGroupIDs, group.GroupId) + } + } + + if err := d.Set("endpoint_details", []interface{}{flattenTransferEndpointDetails(output.EndpointDetails, securityGroupIDs)}); err != nil { return fmt.Errorf("error setting endpoint_details: %w", err) } } else { @@ -538,7 +556,7 @@ func expandTransferEndpointDetails(tfMap map[string]interface{}) *transfer.Endpo return apiObject } -func flattenTransferEndpointDetails(apiObject *transfer.EndpointDetails) map[string]interface{} { +func flattenTransferEndpointDetails(apiObject *transfer.EndpointDetails, securityGroupIDs []*string) map[string]interface{} { if apiObject == nil { return nil } @@ -549,8 +567,10 @@ func flattenTransferEndpointDetails(apiObject *transfer.EndpointDetails) map[str tfMap["address_allocation_ids"] = aws.StringValueSlice(v) } - if v := apiObject.SecurityGroupIds; v != nil { + if v := apiObject.SecurityGroupIds; len(v) > 0 { tfMap["security_group_ids"] = aws.StringValueSlice(v) + } else if len(securityGroupIDs) > 0 { + tfMap["security_group_ids"] = aws.StringValueSlice(securityGroupIDs) } if v := apiObject.SubnetIds; v != nil { diff --git a/aws/resource_aws_transfer_server_test.go b/aws/resource_aws_transfer_server_test.go index efd9601cd4a..c023576ef25 100644 --- a/aws/resource_aws_transfer_server_test.go +++ b/aws/resource_aws_transfer_server_test.go @@ -227,10 +227,60 @@ func testAccAWSTransferServer_securityPolicy(t *testing.T) { } func testAccAWSTransferServer_vpc(t *testing.T) { + var conf transfer.DescribedServer + resourceName := "aws_transfer_server.test" + defaultSecurityGroupResourceName := "aws_default_security_group.test" + subnetResourceName := "aws_subnet.test" + vpcResourceName := "aws_vpc.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTransfer(t) }, + ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSTransferServerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSTransferServerVpcConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTransferServerExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", defaultSecurityGroupResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), + resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"force_destroy"}, + }, + { + Config: testAccAWSTransferServerVpcUpdateConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTransferServerExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", defaultSecurityGroupResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.subnet_ids.*", subnetResourceName, "id"), + resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + ), + }, + }, + }) +} + +func testAccAWSTransferServer_vpcAddressAllocationIds(t *testing.T) { var conf transfer.DescribedServer resourceName := "aws_transfer_server.test" eip1ResourceName := "aws_eip.test.0" eip2ResourceName := "aws_eip.test.1" + defaultSecurityGroupResourceName := "aws_default_security_group.test" subnetResourceName := "aws_subnet.test" vpcResourceName := "aws_vpc.test" rName := acctest.RandomWithPrefix("tf-acc-test") @@ -242,13 +292,14 @@ func testAccAWSTransferServer_vpc(t *testing.T) { CheckDestroy: testAccCheckAWSTransferServerDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSTransferServerVpcNoSecurityGroupsConfig(rName), + Config: testAccAWSTransferServerVpcAddressAllocationIdsNoSecurityGroupsConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.address_allocation_ids.*", eip1ResourceName, "id"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "0"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", defaultSecurityGroupResourceName, "id"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.subnet_ids.*", subnetResourceName, "id"), resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), @@ -261,13 +312,14 @@ func testAccAWSTransferServer_vpc(t *testing.T) { ImportStateVerifyIgnore: []string{"force_destroy"}, }, { - Config: testAccAWSTransferServerVpcNoSecurityGroupsUpdateConfig(rName), + Config: testAccAWSTransferServerVpcAddressAllocationIdsNoSecurityGroupsUpdateConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.address_allocation_ids.*", eip2ResourceName, "id"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "0"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", defaultSecurityGroupResourceName, "id"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.subnet_ids.*", subnetResourceName, "id"), resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), @@ -277,6 +329,54 @@ func testAccAWSTransferServer_vpc(t *testing.T) { }) } +func testAccAWSTransferServer_vpcSecurityGroupIds(t *testing.T) { + var conf transfer.DescribedServer + resourceName := "aws_transfer_server.test" + securityGroup1ResourceName := "aws_security_group.test" + securityGroup2ResourceName := "aws_security_group.tes2" + vpcResourceName := "aws_vpc.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTransfer(t) }, + ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSTransferServerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSTransferServerVpcSecurityGroupIdsConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTransferServerExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", securityGroup1ResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), + resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"force_destroy"}, + }, + { + Config: testAccAWSTransferServerVpcSecurityGroupIdsUpdateConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTransferServerExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", securityGroup2ResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), + resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + ), + }, + }, + }) +} + // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/16556 /* func testAccAWSTransferServer_updateEndpointType(t *testing.T) { @@ -649,6 +749,10 @@ resource "aws_security_group" "test" { Name = %[1]q } } + +resource "aws_default_security_group" "test" { + vpc_id = aws_vpc.test.id +} `, rName) } @@ -887,7 +991,36 @@ resource "aws_transfer_server" "test" { `, rName)) } -func testAccAWSTransferServerVpcNoSecurityGroupsConfig(rName string) string { +func testAccAWSTransferServerVpcConfig(rName string) string { + return composeConfig( + testAccAWSTransferServerConfigBaseVpc(rName), + ` +resource "aws_transfer_server" "test" { + endpoint_type = "VPC" + + endpoint_details { + vpc_id = aws_vpc.test.id + } +} +`) +} + +func testAccAWSTransferServerVpcUpdateConfig(rName string) string { + return composeConfig( + testAccAWSTransferServerConfigBaseVpc(rName), + ` +resource "aws_transfer_server" "test" { + endpoint_type = "VPC" + + endpoint_details { + subnet_ids = [aws_subnet.test.id] + vpc_id = aws_vpc.test.id + } +} +`) +} + +func testAccAWSTransferServerVpcAddressAllocationIdsNoSecurityGroupsConfig(rName string) string { return composeConfig( testAccAWSTransferServerConfigBaseVpc(rName), fmt.Sprintf(` @@ -913,7 +1046,7 @@ resource "aws_transfer_server" "test" { `, rName)) } -func testAccAWSTransferServerVpcNoSecurityGroupsUpdateConfig(rName string) string { +func testAccAWSTransferServerVpcAddressAllocationIdsNoSecurityGroupsUpdateConfig(rName string) string { return composeConfig( testAccAWSTransferServerConfigBaseVpc(rName), fmt.Sprintf(` @@ -939,6 +1072,45 @@ resource "aws_transfer_server" "test" { `, rName)) } +func testAccAWSTransferServerVpcSecurityGroupIdsConfig(rName string) string { + return composeConfig( + testAccAWSTransferServerConfigBaseVpc(rName), + ` +resource "aws_transfer_server" "test" { + endpoint_type = "VPC" + + endpoint_details { + security_group_ids = [aws_security_group.test.id] + vpc_id = aws_vpc.test.id + } +} +`) +} + +func testAccAWSTransferServerVpcSecurityGroupIdsUpdateConfig(rName string) string { + return composeConfig( + testAccAWSTransferServerConfigBaseVpc(rName), + fmt.Sprintf(` +resource "aws_security_group" "test2" { + name = "%[1]s-2" + vpc_id = aws_vpc.test.id + + tags = { + Name = "%[1]s-2" + } +} + +resource "aws_transfer_server" "test" { + endpoint_type = "VPC" + + endpoint_details { + security_group_ids = [aws_security_group.test2.id] + vpc_id = aws_vpc.test.id + } +} +`, rName)) +} + func testAccAWSTransferServerHostKeyConfig(hostKey string) string { return fmt.Sprintf(` resource "aws_transfer_server" "test" { diff --git a/aws/resource_aws_transfer_test.go b/aws/resource_aws_transfer_test.go index 49a5ef44dad..17e3adde8b7 100644 --- a/aws/resource_aws_transfer_test.go +++ b/aws/resource_aws_transfer_test.go @@ -7,17 +7,19 @@ import ( func TestAccAWSTransfer_serial(t *testing.T) { testCases := map[string]map[string]func(t *testing.T){ "Server": { - "basic": testAccAWSTransferServer_basic, - "disappears": testAccAWSTransferServer_disappears, - "APIGateway": testAccAWSTransferServer_apiGateway, - "APIGatewayForceDestroy": testAccAWSTransferServer_apiGateway_forceDestroy, - "Domain": testAccAWSTransferServer_domain, - "ForceDestroy": testAccAWSTransferServer_forceDestroy, - "HostKey": testAccAWSTransferServer_hostKey, - "Protocols": testAccAWSTransferServer_protocols, - "SecurityPolicy": testAccAWSTransferServer_securityPolicy, - "VPC": testAccAWSTransferServer_vpc, - "VPCEndpointID": testAccAWSTransferServer_vpcEndpointId, + "basic": testAccAWSTransferServer_basic, + "disappears": testAccAWSTransferServer_disappears, + "APIGateway": testAccAWSTransferServer_apiGateway, + "APIGatewayForceDestroy": testAccAWSTransferServer_apiGateway_forceDestroy, + "Domain": testAccAWSTransferServer_domain, + "ForceDestroy": testAccAWSTransferServer_forceDestroy, + "HostKey": testAccAWSTransferServer_hostKey, + "Protocols": testAccAWSTransferServer_protocols, + "SecurityPolicy": testAccAWSTransferServer_securityPolicy, + "VPC": testAccAWSTransferServer_vpc, + "VPCAddressAllocationIDs": testAccAWSTransferServer_vpcAddressAllocationIds, + "VPCEndpointID": testAccAWSTransferServer_vpcEndpointId, + "VPCSecurityGroupIDs": testAccAWSTransferServer_vpcSecurityGroupIds, }, "SSHKey": { "basic": testAccAWSTransferSshKey_basic, From fedf8cd136de3654d85341dbf3d6aff6b438e5cf Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 30 Jun 2021 12:17:38 -0400 Subject: [PATCH 095/398] r/aws_transfer_server: Additional tests. --- aws/resource_aws_transfer_server_test.go | 369 ++++++++++++++++++++++- aws/resource_aws_transfer_test.go | 28 +- 2 files changed, 377 insertions(+), 20 deletions(-) diff --git a/aws/resource_aws_transfer_server_test.go b/aws/resource_aws_transfer_server_test.go index c023576ef25..7067370b854 100644 --- a/aws/resource_aws_transfer_server_test.go +++ b/aws/resource_aws_transfer_server_test.go @@ -245,10 +245,12 @@ func testAccAWSTransferServer_vpc(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", defaultSecurityGroupResourceName, "id"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), + resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), ), }, @@ -263,11 +265,13 @@ func testAccAWSTransferServer_vpc(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", defaultSecurityGroupResourceName, "id"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.subnet_ids.*", subnetResourceName, "id"), + resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), ), }, @@ -292,16 +296,18 @@ func testAccAWSTransferServer_vpcAddressAllocationIds(t *testing.T) { CheckDestroy: testAccCheckAWSTransferServerDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSTransferServerVpcAddressAllocationIdsNoSecurityGroupsConfig(rName), + Config: testAccAWSTransferServerVpcAddressAllocationIdsConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.address_allocation_ids.*", eip1ResourceName, "id"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", defaultSecurityGroupResourceName, "id"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.subnet_ids.*", subnetResourceName, "id"), + resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), ), }, @@ -312,16 +318,18 @@ func testAccAWSTransferServer_vpcAddressAllocationIds(t *testing.T) { ImportStateVerifyIgnore: []string{"force_destroy"}, }, { - Config: testAccAWSTransferServerVpcAddressAllocationIdsNoSecurityGroupsUpdateConfig(rName), + Config: testAccAWSTransferServerVpcAddressAllocationIdsUpdateConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.address_allocation_ids.*", eip2ResourceName, "id"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", defaultSecurityGroupResourceName, "id"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.subnet_ids.*", subnetResourceName, "id"), + resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), ), }, @@ -333,7 +341,7 @@ func testAccAWSTransferServer_vpcSecurityGroupIds(t *testing.T) { var conf transfer.DescribedServer resourceName := "aws_transfer_server.test" securityGroup1ResourceName := "aws_security_group.test" - securityGroup2ResourceName := "aws_security_group.tes2" + securityGroup2ResourceName := "aws_security_group.test2" vpcResourceName := "aws_vpc.test" rName := acctest.RandomWithPrefix("tf-acc-test") @@ -348,10 +356,12 @@ func testAccAWSTransferServer_vpcSecurityGroupIds(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", securityGroup1ResourceName, "id"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), + resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), ), }, @@ -366,10 +376,12 @@ func testAccAWSTransferServer_vpcSecurityGroupIds(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", securityGroup2ResourceName, "id"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), + resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), ), }, @@ -377,11 +389,70 @@ func testAccAWSTransferServer_vpcSecurityGroupIds(t *testing.T) { }) } -// Reference: https://github.com/hashicorp/terraform-provider-aws/issues/16556 -/* -func testAccAWSTransferServer_updateEndpointType(t *testing.T) { +func testAccAWSTransferServer_vpcAddressAllocationIds_securityGroupIds(t *testing.T) { var conf transfer.DescribedServer resourceName := "aws_transfer_server.test" + eip1ResourceName := "aws_eip.test.0" + eip2ResourceName := "aws_eip.test.1" + securityGroup1ResourceName := "aws_security_group.test" + securityGroup2ResourceName := "aws_security_group.test2" + subnetResourceName := "aws_subnet.test" + vpcResourceName := "aws_vpc.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTransfer(t) }, + ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSTransferServerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSTransferServerVpcAddressAllocationIdsSecurityGroupIdsConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTransferServerExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.address_allocation_ids.*", eip1ResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", securityGroup1ResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.subnet_ids.*", subnetResourceName, "id"), + resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), + resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"force_destroy"}, + }, + { + Config: testAccAWSTransferServerVpcAddressAllocationIdsSecurityGroupIdsUpdateConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTransferServerExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.address_allocation_ids.*", eip2ResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", securityGroup2ResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.subnet_ids.*", subnetResourceName, "id"), + resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), + resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + ), + }, + }, + }) +} + +func testAccAWSTransferServer_updateEndpointType_publicToVpc(t *testing.T) { + var conf transfer.DescribedServer + resourceName := "aws_transfer_server.test" + defaultSecurityGroupResourceName := "aws_default_security_group.test" + vpcResourceName := "aws_vpc.test" rName := acctest.RandomWithPrefix("tf-acc-test") resource.Test(t, resource.TestCase{ @@ -403,8 +474,219 @@ func testAccAWSTransferServer_updateEndpointType(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", defaultSecurityGroupResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), + resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), + resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"force_destroy"}, + }, + }, + }) +} + +func testAccAWSTransferServer_updateEndpointType_publicToVpc_addressAllocationIds(t *testing.T) { + var conf transfer.DescribedServer + resourceName := "aws_transfer_server.test" + eipResourceName := "aws_eip.test.0" + defaultSecurityGroupResourceName := "aws_default_security_group.test" + subnetResourceName := "aws_subnet.test" + vpcResourceName := "aws_vpc.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTransfer(t) }, + ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSTransferServerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSTransferServerBasicConfig(), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTransferServerExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "0"), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "PUBLIC"), + ), + }, + { + Config: testAccAWSTransferServerVpcAddressAllocationIdsConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTransferServerExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.address_allocation_ids.*", eipResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", defaultSecurityGroupResourceName, "id"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.subnet_ids.*", subnetResourceName, "id"), + resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), + resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"force_destroy"}, + }, + }, + }) +} + +func testAccAWSTransferServer_updateEndpointType_vpcEndpointToVpc(t *testing.T) { + var conf transfer.DescribedServer + resourceName := "aws_transfer_server.test" + defaultSecurityGroupResourceName := "aws_default_security_group.test" + vpcEndpointResourceName := "aws_vpc_endpoint.test" + vpcResourceName := "aws_vpc.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTransfer(t) }, + ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSTransferServerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSTransferServerVpcEndpointConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTransferServerExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC_ENDPOINT"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "0"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), + resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_endpoint_id", vpcEndpointResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.vpc_id", ""), + ), + }, + { + Config: testAccAWSTransferServerVpcConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTransferServerExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", defaultSecurityGroupResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), + resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), + resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"force_destroy"}, + }, + }, + }) +} + +func testAccAWSTransferServer_updateEndpointType_vpcEndpointToVpc_addressAllocationIds(t *testing.T) { + var conf transfer.DescribedServer + resourceName := "aws_transfer_server.test" + eipResourceName := "aws_eip.test.0" + defaultSecurityGroupResourceName := "aws_default_security_group.test" + subnetResourceName := "aws_subnet.test" + vpcEndpointResourceName := "aws_vpc_endpoint.test" + vpcResourceName := "aws_vpc.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTransfer(t) }, + ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSTransferServerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSTransferServerVpcEndpointConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTransferServerExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC_ENDPOINT"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "0"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), + resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_endpoint_id", vpcEndpointResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.vpc_id", ""), + ), + }, + { + Config: testAccAWSTransferServerVpcAddressAllocationIdsConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTransferServerExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.address_allocation_ids.*", eipResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", defaultSecurityGroupResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.subnet_ids.*", subnetResourceName, "id"), + resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), + resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"force_destroy"}, + }, + }, + }) +} + +func testAccAWSTransferServer_updateEndpointType_vpcEndpointToVpc_securityGroupIds(t *testing.T) { + var conf transfer.DescribedServer + resourceName := "aws_transfer_server.test" + securityGroupResourceName := "aws_security_group.test" + vpcEndpointResourceName := "aws_vpc_endpoint.test" + vpcResourceName := "aws_vpc.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTransfer(t) }, + ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSTransferServerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSTransferServerVpcEndpointConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTransferServerExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC_ENDPOINT"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "0"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), + resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_endpoint_id", vpcEndpointResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.vpc_id", ""), + ), + }, + { + Config: testAccAWSTransferServerVpcSecurityGroupIdsConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTransferServerExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", securityGroupResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), + resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), + resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), ), }, { @@ -416,7 +698,6 @@ func testAccAWSTransferServer_updateEndpointType(t *testing.T) { }, }) } -*/ func testAccAWSTransferServer_protocols(t *testing.T) { var s transfer.DescribedServer @@ -604,6 +885,7 @@ func testAccAWSTransferServer_hostKey(t *testing.T) { func testAccAWSTransferServer_vpcEndpointId(t *testing.T) { var conf transfer.DescribedServer resourceName := "aws_transfer_server.test" + vpcEndpointResourceName := "aws_vpc_endpoint.test" rName := acctest.RandomWithPrefix("tf-acc-test") if testAccGetPartition() == "aws-us-gov" { @@ -621,6 +903,12 @@ func testAccAWSTransferServer_vpcEndpointId(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC_ENDPOINT"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "0"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), + resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_endpoint_id", vpcEndpointResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.vpc_id", ""), ), }, { @@ -1020,7 +1308,59 @@ resource "aws_transfer_server" "test" { `) } -func testAccAWSTransferServerVpcAddressAllocationIdsNoSecurityGroupsConfig(rName string) string { +func testAccAWSTransferServerVpcAddressAllocationIdsConfig(rName string) string { + return composeConfig( + testAccAWSTransferServerConfigBaseVpc(rName), + fmt.Sprintf(` +resource "aws_eip" "test" { + count = 2 + + vpc = true + + tags = { + Name = %[1]q + } +} + +resource "aws_transfer_server" "test" { + endpoint_type = "VPC" + + endpoint_details { + address_allocation_ids = [aws_eip.test[0].id] + subnet_ids = [aws_subnet.test.id] + vpc_id = aws_vpc.test.id + } +} +`, rName)) +} + +func testAccAWSTransferServerVpcAddressAllocationIdsUpdateConfig(rName string) string { + return composeConfig( + testAccAWSTransferServerConfigBaseVpc(rName), + fmt.Sprintf(` +resource "aws_eip" "test" { + count = 2 + + vpc = true + + tags = { + Name = %[1]q + } +} + +resource "aws_transfer_server" "test" { + endpoint_type = "VPC" + + endpoint_details { + address_allocation_ids = [aws_eip.test[1].id] + subnet_ids = [aws_subnet.test.id] + vpc_id = aws_vpc.test.id + } +} +`, rName)) +} + +func testAccAWSTransferServerVpcAddressAllocationIdsSecurityGroupIdsConfig(rName string) string { return composeConfig( testAccAWSTransferServerConfigBaseVpc(rName), fmt.Sprintf(` @@ -1039,6 +1379,7 @@ resource "aws_transfer_server" "test" { endpoint_details { address_allocation_ids = [aws_eip.test[0].id] + security_group_ids = [aws_security_group.test.id] subnet_ids = [aws_subnet.test.id] vpc_id = aws_vpc.test.id } @@ -1046,10 +1387,19 @@ resource "aws_transfer_server" "test" { `, rName)) } -func testAccAWSTransferServerVpcAddressAllocationIdsNoSecurityGroupsUpdateConfig(rName string) string { +func testAccAWSTransferServerVpcAddressAllocationIdsSecurityGroupIdsUpdateConfig(rName string) string { return composeConfig( testAccAWSTransferServerConfigBaseVpc(rName), fmt.Sprintf(` +resource "aws_security_group" "test2" { + name = "%[1]s-2" + vpc_id = aws_vpc.test.id + + tags = { + Name = "%[1]s-2" + } +} + resource "aws_eip" "test" { count = 2 @@ -1065,6 +1415,7 @@ resource "aws_transfer_server" "test" { endpoint_details { address_allocation_ids = [aws_eip.test[1].id] + security_group_ids = [aws_security_group.test2.id] subnet_ids = [aws_subnet.test.id] vpc_id = aws_vpc.test.id } diff --git a/aws/resource_aws_transfer_test.go b/aws/resource_aws_transfer_test.go index 17e3adde8b7..c076d6e4e19 100644 --- a/aws/resource_aws_transfer_test.go +++ b/aws/resource_aws_transfer_test.go @@ -7,19 +7,25 @@ import ( func TestAccAWSTransfer_serial(t *testing.T) { testCases := map[string]map[string]func(t *testing.T){ "Server": { - "basic": testAccAWSTransferServer_basic, - "disappears": testAccAWSTransferServer_disappears, - "APIGateway": testAccAWSTransferServer_apiGateway, - "APIGatewayForceDestroy": testAccAWSTransferServer_apiGateway_forceDestroy, - "Domain": testAccAWSTransferServer_domain, - "ForceDestroy": testAccAWSTransferServer_forceDestroy, - "HostKey": testAccAWSTransferServer_hostKey, - "Protocols": testAccAWSTransferServer_protocols, - "SecurityPolicy": testAccAWSTransferServer_securityPolicy, + "basic": testAccAWSTransferServer_basic, + "disappears": testAccAWSTransferServer_disappears, + "APIGateway": testAccAWSTransferServer_apiGateway, + "APIGatewayForceDestroy": testAccAWSTransferServer_apiGateway_forceDestroy, + "Domain": testAccAWSTransferServer_domain, + "ForceDestroy": testAccAWSTransferServer_forceDestroy, + "HostKey": testAccAWSTransferServer_hostKey, + "Protocols": testAccAWSTransferServer_protocols, + "SecurityPolicy": testAccAWSTransferServer_securityPolicy, + "UpdateEndpointTypePublicToVPC": testAccAWSTransferServer_updateEndpointType_publicToVpc, + "UpdateEndpointTypePublicToVPCAddressAllocationIDs": testAccAWSTransferServer_updateEndpointType_publicToVpc_addressAllocationIds, + "UpdateEndpointTypeVPCEndpointToVPC": testAccAWSTransferServer_updateEndpointType_vpcEndpointToVpc, + "UpdateEndpointTypeVPCEndpointToVPCAddressAllocationIDs": testAccAWSTransferServer_updateEndpointType_vpcEndpointToVpc_addressAllocationIds, + "UpdateEndpointTypeVPCEndpointToVPCSecurityGroupIDs": testAccAWSTransferServer_updateEndpointType_vpcEndpointToVpc_securityGroupIds, "VPC": testAccAWSTransferServer_vpc, "VPCAddressAllocationIDs": testAccAWSTransferServer_vpcAddressAllocationIds, - "VPCEndpointID": testAccAWSTransferServer_vpcEndpointId, - "VPCSecurityGroupIDs": testAccAWSTransferServer_vpcSecurityGroupIds, + "VPCAddressAllocationIDsSecurityGroupIDs": testAccAWSTransferServer_vpcAddressAllocationIds_securityGroupIds, + "VPCEndpointID": testAccAWSTransferServer_vpcEndpointId, + "VPCSecurityGroupIDs": testAccAWSTransferServer_vpcSecurityGroupIds, }, "SSHKey": { "basic": testAccAWSTransferSshKey_basic, From 5d26d2a81a5becf8ff722f9591c537e548124c9e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 30 Jun 2021 16:04:03 -0400 Subject: [PATCH 096/398] r/aws_transfer_server: Use Amazon EC2 ModifyVpcEndpoint API to modify security_group_ids. --- aws/resource_aws_transfer_server.go | 145 ++++++++++++++++------- aws/resource_aws_transfer_server_test.go | 45 +++++++ aws/resource_aws_transfer_test.go | 11 +- 3 files changed, 152 insertions(+), 49 deletions(-) diff --git a/aws/resource_aws_transfer_server.go b/aws/resource_aws_transfer_server.go index f249bd117d1..fc06e68e462 100644 --- a/aws/resource_aws_transfer_server.go +++ b/aws/resource_aws_transfer_server.go @@ -7,6 +7,7 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/transfer" "github.com/hashicorp/aws-sdk-go-base/tfawserr" multierror "github.com/hashicorp/go-multierror" @@ -16,6 +17,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" ec2finder "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2/finder" + ec2waiter "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2/waiter" tftransfer "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/transfer" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/transfer/finder" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/transfer/waiter" @@ -186,7 +188,6 @@ func resourceAwsTransferServer() *schema.Resource { } func resourceAwsTransferServerCreate(d *schema.ResourceData, meta interface{}) error { - updateAfterCreate := false conn := meta.(*AWSClient).transferconn defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig tags := defaultTagsConfig.MergeTags(keyvaluetags.New(d.Get("tags").(map[string]interface{}))) @@ -201,15 +202,15 @@ func resourceAwsTransferServerCreate(d *schema.ResourceData, meta interface{}) e input.Domain = aws.String(v.(string)) } + var addressAllocationIDs []*string + if v, ok := d.GetOk("endpoint_details"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { input.EndpointDetails = expandTransferEndpointDetails(v.([]interface{})[0].(map[string]interface{})) // Prevent the following error: InvalidRequestException: AddressAllocationIds cannot be set in CreateServer // Reference: https://docs.aws.amazon.com/transfer/latest/userguide/API_EndpointDetails.html#TransferFamily-Type-EndpointDetails-AddressAllocationIds - if input.EndpointDetails != nil && len(input.EndpointDetails.AddressAllocationIds) > 0 { - input.EndpointDetails.AddressAllocationIds = nil - updateAfterCreate = true - } + addressAllocationIDs = input.EndpointDetails.AddressAllocationIds + input.EndpointDetails.AddressAllocationIds = nil } if v, ok := d.GetOk("endpoint_type"); ok { @@ -271,18 +272,17 @@ func resourceAwsTransferServerCreate(d *schema.ResourceData, meta interface{}) e return fmt.Errorf("error waiting for Transfer Server (%s) to create: %w", d.Id(), err) } - if updateAfterCreate { + // AddressAllocationIds is only valid in the UpdateServer API. + if len(addressAllocationIDs) > 0 { if err := stopTransferServer(conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { return err } - // TODO - // TODO You can edit the SecurityGroupIds property in the UpdateServer API only if you are changing the EndpointType from PUBLIC or VPC_ENDPOINT to VPC. To change security groups associated with your server's VPC endpoint after creation, use the Amazon EC2 ModifyVpcEndpoint API. - // TODO - input := &transfer.UpdateServerInput{ - ServerId: aws.String(d.Id()), - EndpointDetails: expandTransferEndpointDetails(d.Get("endpoint_details").([]interface{})[0].(map[string]interface{})), + ServerId: aws.String(d.Id()), + EndpointDetails: &transfer.EndpointDetails{ + AddressAllocationIds: addressAllocationIDs, + }, } if err := updateTransferServer(conn, input); err != nil { @@ -376,18 +376,96 @@ func resourceAwsTransferServerUpdate(d *schema.ResourceData, meta interface{}) e conn := meta.(*AWSClient).transferconn if d.HasChangesExcept("tags", "tags_all") { - stopFlag := false + //TODO var addressAllocationIDs []*string + var offlineUpdate bool input := &transfer.UpdateServerInput{ ServerId: aws.String(d.Id()), } - if d.HasChange("logging_role") { - input.LoggingRole = aws.String(d.Get("logging_role").(string)) + if d.HasChange("certificate") { + input.Certificate = aws.String(d.Get("certificate").(string)) } - if d.HasChange("security_policy_name") { - input.SecurityPolicyName = aws.String(d.Get("security_policy_name").(string)) + if d.HasChange("endpoint_details") { + var newEndpointTypeVpc bool + var oldEndpointTypeVpc bool + + if v, ok := d.GetOk("endpoint_details"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.EndpointDetails = expandTransferEndpointDetails(v.([]interface{})[0].(map[string]interface{})) + + old, new := d.GetChange("endpoint_type") + + if old, new := old.(string), new.(string); new != old && new == transfer.EndpointTypeVpc { + newEndpointTypeVpc = true + } else if new == old && new == transfer.EndpointTypeVpc { + newEndpointTypeVpc = true + oldEndpointTypeVpc = true + } + + if newEndpointTypeVpc && !oldEndpointTypeVpc { + // TODO ???? + // Prevent the following error: InvalidRequestException: Cannot specify AddressAllocationids when updating server to EndpointType: VPC + // addressAllocationIDs = input.EndpointDetails.AddressAllocationIds + // input.EndpointDetails.AddressAllocationIds = nil + + // Prevent the following error: InvalidRequestException: VPC Endpoint ID unsupported for EndpointType: VPC + input.EndpointDetails.VpcEndpointId = nil + } else if newEndpointTypeVpc && oldEndpointTypeVpc { + // Prevent the following error: InvalidRequestException: Server must be OFFLINE to change AddressAllocationIds + if d.HasChange("endpoint_details.0.address_allocation_ids") { + offlineUpdate = true + } + + // Prevent the following error: InvalidRequestException: Changing Security Group is not supported + input.EndpointDetails.SecurityGroupIds = nil + } + } + + // You can edit the SecurityGroupIds property in the UpdateServer API only if you are changing the EndpointType from PUBLIC or VPC_ENDPOINT to VPC. + // To change security groups associated with your server's VPC endpoint after creation, use the Amazon EC2 ModifyVpcEndpoint API. + if d.HasChange("endpoint_details.0.security_group_ids") && newEndpointTypeVpc && oldEndpointTypeVpc { + conn := meta.(*AWSClient).ec2conn + + vpcEndpointID := d.Get("endpoint_details.0.vpc_endpoint_id").(string) + input := &ec2.ModifyVpcEndpointInput{ + VpcEndpointId: aws.String(vpcEndpointID), + } + + old, new := d.GetChange("endpoint_details.0.security_group_ids") + + if add := expandStringSet(new.(*schema.Set).Difference(old.(*schema.Set))); len(add) > 0 { + input.AddSecurityGroupIds = add + } + + if del := expandStringSet(old.(*schema.Set).Difference(new.(*schema.Set))); len(del) > 0 { + input.RemoveSecurityGroupIds = del + } + + log.Printf("[DEBUG] Updating VPC Endpoint: %s", input) + if _, err := conn.ModifyVpcEndpoint(input); err != nil { + return fmt.Errorf("error updating Transfer Server (%s) VPC Endpoint (%s): %w", d.Id(), vpcEndpointID, err) + } + + _, err := ec2waiter.VpcEndpointAvailable(conn, vpcEndpointID, d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return fmt.Errorf("error waiting for Transfer Server (%s) VPC Endpoint (%s) to become available: %w", d.Id(), vpcEndpointID, err) + } + } + } + + if d.HasChange("endpoint_type") { + input.EndpointType = aws.String(d.Get("endpoint_type").(string)) + + // Prevent the following error: InvalidRequestException: Server must be OFFLINE to change EndpointType + offlineUpdate = true + } + + if d.HasChange("host_key") { + if attr, ok := d.GetOk("host_key"); ok { + input.HostKey = aws.String(attr.(string)) + } } if d.HasChanges("invocation_role", "url") { @@ -404,40 +482,19 @@ func resourceAwsTransferServerUpdate(d *schema.ResourceData, meta interface{}) e input.IdentityProviderDetails = identityProviderDetails } - if d.HasChange("endpoint_type") { - input.EndpointType = aws.String(d.Get("endpoint_type").(string)) - } - - if d.HasChange("certificate") { - input.Certificate = aws.String(d.Get("certificate").(string)) + if d.HasChange("logging_role") { + input.LoggingRole = aws.String(d.Get("logging_role").(string)) } if d.HasChange("protocols") { input.Protocols = expandStringSet(d.Get("protocols").(*schema.Set)) } - if d.HasChange("endpoint_details") { - if v, ok := d.GetOk("endpoint_details"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.EndpointDetails = expandTransferEndpointDetails(v.([]interface{})[0].(map[string]interface{})) - } - - // Prevent the following error: InvalidRequestException: Server must be OFFLINE to change AddressAllocationIds - if d.HasChange("endpoint_details.0.address_allocation_ids") { - stopFlag = true - } - - // TODO - // TODO You can edit the SecurityGroupIds property in the UpdateServer API only if you are changing the EndpointType from PUBLIC or VPC_ENDPOINT to VPC. To change security groups associated with your server's VPC endpoint after creation, use the Amazon EC2 ModifyVpcEndpoint API. - // TODO - } - - if d.HasChange("host_key") { - if attr, ok := d.GetOk("host_key"); ok { - input.HostKey = aws.String(attr.(string)) - } + if d.HasChange("security_policy_name") { + input.SecurityPolicyName = aws.String(d.Get("security_policy_name").(string)) } - if stopFlag { + if offlineUpdate { if err := stopTransferServer(conn, d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil { return err } @@ -448,7 +505,7 @@ func resourceAwsTransferServerUpdate(d *schema.ResourceData, meta interface{}) e return err } - if stopFlag { + if offlineUpdate { if err := startTransferServer(conn, d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil { return err } diff --git a/aws/resource_aws_transfer_server_test.go b/aws/resource_aws_transfer_server_test.go index 7067370b854..c1226b6f0bc 100644 --- a/aws/resource_aws_transfer_server_test.go +++ b/aws/resource_aws_transfer_server_test.go @@ -699,6 +699,51 @@ func testAccAWSTransferServer_updateEndpointType_vpcEndpointToVpc_securityGroupI }) } +func testAccAWSTransferServer_updateEndpointType_vpcToPublic(t *testing.T) { + var conf transfer.DescribedServer + resourceName := "aws_transfer_server.test" + defaultSecurityGroupResourceName := "aws_default_security_group.test" + vpcResourceName := "aws_vpc.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTransfer(t) }, + ErrorCheck: testAccErrorCheck(t, transfer.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSTransferServerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSTransferServerVpcConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTransferServerExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", defaultSecurityGroupResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), + resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), + resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + ), + }, + { + Config: testAccAWSTransferServerBasicConfig(), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTransferServerExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "0"), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "PUBLIC"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"force_destroy"}, + }, + }, + }) +} + func testAccAWSTransferServer_protocols(t *testing.T) { var s transfer.DescribedServer var ca acmpca.CertificateAuthority diff --git a/aws/resource_aws_transfer_test.go b/aws/resource_aws_transfer_test.go index c076d6e4e19..a06c3fe0289 100644 --- a/aws/resource_aws_transfer_test.go +++ b/aws/resource_aws_transfer_test.go @@ -21,11 +21,12 @@ func TestAccAWSTransfer_serial(t *testing.T) { "UpdateEndpointTypeVPCEndpointToVPC": testAccAWSTransferServer_updateEndpointType_vpcEndpointToVpc, "UpdateEndpointTypeVPCEndpointToVPCAddressAllocationIDs": testAccAWSTransferServer_updateEndpointType_vpcEndpointToVpc_addressAllocationIds, "UpdateEndpointTypeVPCEndpointToVPCSecurityGroupIDs": testAccAWSTransferServer_updateEndpointType_vpcEndpointToVpc_securityGroupIds, - "VPC": testAccAWSTransferServer_vpc, - "VPCAddressAllocationIDs": testAccAWSTransferServer_vpcAddressAllocationIds, - "VPCAddressAllocationIDsSecurityGroupIDs": testAccAWSTransferServer_vpcAddressAllocationIds_securityGroupIds, - "VPCEndpointID": testAccAWSTransferServer_vpcEndpointId, - "VPCSecurityGroupIDs": testAccAWSTransferServer_vpcSecurityGroupIds, + "UpdateEndpointTypeVPCToPublic": testAccAWSTransferServer_updateEndpointType_vpcToPublic, + "VPC": testAccAWSTransferServer_vpc, + "VPCAddressAllocationIDs": testAccAWSTransferServer_vpcAddressAllocationIds, + "VPCAddressAllocationIDsSecurityGroupIDs": testAccAWSTransferServer_vpcAddressAllocationIds_securityGroupIds, + "VPCEndpointID": testAccAWSTransferServer_vpcEndpointId, + "VPCSecurityGroupIDs": testAccAWSTransferServer_vpcSecurityGroupIds, }, "SSHKey": { "basic": testAccAWSTransferSshKey_basic, From 41048b94230402c79f71b5e0abe785dd2755f15f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 30 Jun 2021 16:36:14 -0400 Subject: [PATCH 097/398] Prevent 'severity:warning rule:aws-sdk-go-multiple-service-imports: Resources should not implement multiple AWS service functionality'. --- .semgrep.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.semgrep.yml b/.semgrep.yml index c9a41d64c4a..575fc5949d2 100644 --- a/.semgrep.yml +++ b/.semgrep.yml @@ -22,6 +22,7 @@ rules: - aws/validators.go - aws/*wafregional*.go - aws/resource_aws_serverlessapplicationrepository_cloudformation_stack.go + - aws/resource_aws_transfer_server.go - aws/*_test.go - aws/internal/keyvaluetags/ - aws/internal/service/wafregional/ From d2d0ce509a3f0d097b5fb76893631fd90d6efd92 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 30 Jun 2021 17:01:01 -0400 Subject: [PATCH 098/398] Fix 'terrafmt' errors. --- aws/resource_aws_transfer_server_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_transfer_server_test.go b/aws/resource_aws_transfer_server_test.go index c1226b6f0bc..6400e56f1ab 100644 --- a/aws/resource_aws_transfer_server_test.go +++ b/aws/resource_aws_transfer_server_test.go @@ -1424,7 +1424,7 @@ resource "aws_transfer_server" "test" { endpoint_details { address_allocation_ids = [aws_eip.test[0].id] - security_group_ids = [aws_security_group.test.id] + security_group_ids = [aws_security_group.test.id] subnet_ids = [aws_subnet.test.id] vpc_id = aws_vpc.test.id } @@ -1460,7 +1460,7 @@ resource "aws_transfer_server" "test" { endpoint_details { address_allocation_ids = [aws_eip.test[1].id] - security_group_ids = [aws_security_group.test2.id] + security_group_ids = [aws_security_group.test2.id] subnet_ids = [aws_subnet.test.id] vpc_id = aws_vpc.test.id } From f22226de4cf529673e8e12cc17603d2de0bd53da Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 06:06:00 +0000 Subject: [PATCH 099/398] build(deps): bump integrations/github in /infrastructure/repository Bumps [integrations/github](https://github.com/integrations/terraform-provider-github) from 4.12.0 to 4.12.1. - [Release notes](https://github.com/integrations/terraform-provider-github/releases) - [Changelog](https://github.com/integrations/terraform-provider-github/blob/master/CHANGELOG.md) - [Commits](https://github.com/integrations/terraform-provider-github/compare/v4.12.0...v4.12.1) --- updated-dependencies: - dependency-name: integrations/github dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- infrastructure/repository/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/repository/main.tf b/infrastructure/repository/main.tf index fa2688e230b..d10b66d7471 100644 --- a/infrastructure/repository/main.tf +++ b/infrastructure/repository/main.tf @@ -10,7 +10,7 @@ terraform { required_providers { github = { source = "integrations/github" - version = "4.12.0" + version = "4.12.1" } } From 1c41f1270052d99bb5b48ab30458b8d1a23c535e Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Thu, 1 Jul 2021 10:26:50 +0300 Subject: [PATCH 100/398] add revoke and retry --- aws/resource_aws_cognito_user_pool_client.go | 21 +++++-- ...ource_aws_cognito_user_pool_client_test.go | 61 +++++++++++++++++++ 2 files changed, 78 insertions(+), 4 deletions(-) diff --git a/aws/resource_aws_cognito_user_pool_client.go b/aws/resource_aws_cognito_user_pool_client.go index 1a6bda9f636..45a45b3bd38 100644 --- a/aws/resource_aws_cognito_user_pool_client.go +++ b/aws/resource_aws_cognito_user_pool_client.go @@ -116,11 +116,16 @@ func resourceAwsCognitoUserPoolClient() *schema.Resource { Type: schema.TypeString, Optional: true, ValidateFunc: validation.All( - validation.StringLenBetween(1, 1024), + validation.StringLenBetween(0, 1024), validation.StringMatch(regexp.MustCompile(`[\p{L}\p{M}\p{S}\p{N}\p{P}]+`), "must satisfy regular expression pattern: [\\p{L}\\p{M}\\p{S}\\p{N}\\p{P}]+`"), ), }, + "enable_token_revocation": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + }, "explicit_auth_flows": { Type: schema.TypeSet, Optional: true, @@ -311,6 +316,10 @@ func resourceAwsCognitoUserPoolClientCreate(d *schema.ResourceData, meta interfa params.PreventUserExistenceErrors = aws.String(v.(string)) } + if v, ok := d.GetOk("enable_token_revocation"); ok { + params.EnableTokenRevocation = aws.Bool(v.(bool)) + } + log.Printf("[DEBUG] Creating Cognito User Pool Client: %s", params) resp, err := conn.CreateUserPoolClient(params) @@ -363,6 +372,7 @@ func resourceAwsCognitoUserPoolClientRead(d *schema.ResourceData, meta interface d.Set("logout_urls", flattenStringSet(userPoolClient.LogoutURLs)) d.Set("prevent_user_existence_errors", userPoolClient.PreventUserExistenceErrors) d.Set("supported_identity_providers", flattenStringSet(userPoolClient.SupportedIdentityProviders)) + d.Set("enable_token_revocation", userPoolClient.EnableTokenRevocation) if err := d.Set("analytics_configuration", flattenAwsCognitoUserPoolClientAnalyticsConfig(userPoolClient.AnalyticsConfiguration)); err != nil { return fmt.Errorf("error setting analytics_configuration: %w", err) @@ -379,8 +389,9 @@ func resourceAwsCognitoUserPoolClientUpdate(d *schema.ResourceData, meta interfa conn := meta.(*AWSClient).cognitoidpconn params := &cognitoidentityprovider.UpdateUserPoolClientInput{ - ClientId: aws.String(d.Id()), - UserPoolId: aws.String(d.Get("user_pool_id").(string)), + ClientId: aws.String(d.Id()), + UserPoolId: aws.String(d.Get("user_pool_id").(string)), + EnableTokenRevocation: aws.Bool(d.Get("enable_token_revocation").(bool)), } if v, ok := d.GetOk("name"); ok { @@ -453,7 +464,9 @@ func resourceAwsCognitoUserPoolClientUpdate(d *schema.ResourceData, meta interfa log.Printf("[DEBUG] Updating Cognito User Pool Client: %s", params) - _, err := conn.UpdateUserPoolClient(params) + _, err := retryOnAwsCode(cognitoidentityprovider.ErrCodeConcurrentModificationException, func() (interface{}, error) { + return conn.UpdateUserPoolClient(params) + }) if err != nil { return fmt.Errorf("error updating Cognito User Pool Client (%s): %w", d.Id(), err) } diff --git a/aws/resource_aws_cognito_user_pool_client_test.go b/aws/resource_aws_cognito_user_pool_client_test.go index d9d0c54319f..b5631516d34 100644 --- a/aws/resource_aws_cognito_user_pool_client_test.go +++ b/aws/resource_aws_cognito_user_pool_client_test.go @@ -45,6 +45,52 @@ func TestAccAWSCognitoUserPoolClient_basic(t *testing.T) { }) } +func TestAccAWSCognitoUserPoolClient_enableRevokation(t *testing.T) { + var client cognitoidentityprovider.UserPoolClientType + userPoolName := fmt.Sprintf("tf-acc-cognito-user-pool-%s", acctest.RandString(7)) + clientName := acctest.RandString(10) + resourceName := "aws_cognito_user_pool_client.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSCognitoIdentityProvider(t) }, + ErrorCheck: testAccErrorCheck(t, cognitoidentityprovider.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCognitoUserPoolClientDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCognitoUserPoolClientRevokationConfig(userPoolName, clientName, true), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), + resource.TestCheckResourceAttr(resourceName, "name", clientName), + resource.TestCheckResourceAttr(resourceName, "enable_token_revocation", "true"), + ), + }, + { + ResourceName: resourceName, + ImportStateIdFunc: testAccAWSCognitoUserPoolClientImportStateIDFunc(resourceName), + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSCognitoUserPoolClientRevokationConfig(userPoolName, clientName, false), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), + resource.TestCheckResourceAttr(resourceName, "name", clientName), + resource.TestCheckResourceAttr(resourceName, "enable_token_revocation", "false"), + ), + }, + { + Config: testAccAWSCognitoUserPoolClientRevokationConfig(userPoolName, clientName, true), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), + resource.TestCheckResourceAttr(resourceName, "name", clientName), + resource.TestCheckResourceAttr(resourceName, "enable_token_revocation", "true"), + ), + }, + }, + }) +} + func TestAccAWSCognitoUserPoolClient_refreshTokenValidity(t *testing.T) { var client cognitoidentityprovider.UserPoolClientType rName := acctest.RandomWithPrefix("tf-acc-test") @@ -623,6 +669,21 @@ resource "aws_cognito_user_pool_client" "test" { `, userPoolName, clientName) } +func testAccAWSCognitoUserPoolClientRevokationConfig(userPoolName, clientName string, revoke bool) string { + return fmt.Sprintf(` +resource "aws_cognito_user_pool" "test" { + name = %[1]q +} + +resource "aws_cognito_user_pool_client" "test" { + name = %[2]q + user_pool_id = aws_cognito_user_pool.test.id + explicit_auth_flows = ["ADMIN_NO_SRP_AUTH"] + enable_token_revocation = %[3]t +} +`, userPoolName, clientName, revoke) +} + func testAccAWSCognitoUserPoolClientConfig_RefreshTokenValidity(rName string, refreshTokenValidity int) string { return fmt.Sprintf(` resource "aws_cognito_user_pool" "test" { From 5c61440c4c37d62c6cd14f8129adb14a80584c43 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Thu, 1 Jul 2021 10:40:21 +0300 Subject: [PATCH 101/398] rename --- aws/resource_aws_cognito_user_pool_client_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/aws/resource_aws_cognito_user_pool_client_test.go b/aws/resource_aws_cognito_user_pool_client_test.go index b5631516d34..c8d87dfc3c4 100644 --- a/aws/resource_aws_cognito_user_pool_client_test.go +++ b/aws/resource_aws_cognito_user_pool_client_test.go @@ -45,7 +45,7 @@ func TestAccAWSCognitoUserPoolClient_basic(t *testing.T) { }) } -func TestAccAWSCognitoUserPoolClient_enableRevokation(t *testing.T) { +func TestAccAWSCognitoUserPoolClient_enableRevocation(t *testing.T) { var client cognitoidentityprovider.UserPoolClientType userPoolName := fmt.Sprintf("tf-acc-cognito-user-pool-%s", acctest.RandString(7)) clientName := acctest.RandString(10) @@ -58,7 +58,7 @@ func TestAccAWSCognitoUserPoolClient_enableRevokation(t *testing.T) { CheckDestroy: testAccCheckAWSCognitoUserPoolClientDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSCognitoUserPoolClientRevokationConfig(userPoolName, clientName, true), + Config: testAccAWSCognitoUserPoolClientRevocationConfig(userPoolName, clientName, true), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), resource.TestCheckResourceAttr(resourceName, "name", clientName), @@ -72,7 +72,7 @@ func TestAccAWSCognitoUserPoolClient_enableRevokation(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccAWSCognitoUserPoolClientRevokationConfig(userPoolName, clientName, false), + Config: testAccAWSCognitoUserPoolClientRevocationConfig(userPoolName, clientName, false), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), resource.TestCheckResourceAttr(resourceName, "name", clientName), @@ -80,7 +80,7 @@ func TestAccAWSCognitoUserPoolClient_enableRevokation(t *testing.T) { ), }, { - Config: testAccAWSCognitoUserPoolClientRevokationConfig(userPoolName, clientName, true), + Config: testAccAWSCognitoUserPoolClientRevocationConfig(userPoolName, clientName, true), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), resource.TestCheckResourceAttr(resourceName, "name", clientName), @@ -669,7 +669,7 @@ resource "aws_cognito_user_pool_client" "test" { `, userPoolName, clientName) } -func testAccAWSCognitoUserPoolClientRevokationConfig(userPoolName, clientName string, revoke bool) string { +func testAccAWSCognitoUserPoolClientRevocationConfig(userPoolName, clientName string, revoke bool) string { return fmt.Sprintf(` resource "aws_cognito_user_pool" "test" { name = %[1]q From 6e24253a793be6f499ca1347581606afdf583580 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Thu, 1 Jul 2021 12:16:08 +0300 Subject: [PATCH 102/398] refactor tests --- ...ource_aws_cognito_user_pool_client_test.go | 199 +++++++----------- 1 file changed, 80 insertions(+), 119 deletions(-) diff --git a/aws/resource_aws_cognito_user_pool_client_test.go b/aws/resource_aws_cognito_user_pool_client_test.go index c8d87dfc3c4..d6f0bfb681c 100644 --- a/aws/resource_aws_cognito_user_pool_client_test.go +++ b/aws/resource_aws_cognito_user_pool_client_test.go @@ -14,8 +14,7 @@ import ( func TestAccAWSCognitoUserPoolClient_basic(t *testing.T) { var client cognitoidentityprovider.UserPoolClientType - userPoolName := fmt.Sprintf("tf-acc-cognito-user-pool-%s", acctest.RandString(7)) - clientName := acctest.RandString(10) + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_cognito_user_pool_client.test" resource.ParallelTest(t, resource.TestCase{ @@ -25,10 +24,10 @@ func TestAccAWSCognitoUserPoolClient_basic(t *testing.T) { CheckDestroy: testAccCheckAWSCognitoUserPoolClientDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSCognitoUserPoolClientConfig_basic(userPoolName, clientName), + Config: testAccAWSCognitoUserPoolClientConfig_basic(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), - resource.TestCheckResourceAttr(resourceName, "name", clientName), + resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "explicit_auth_flows.#", "1"), resource.TestCheckTypeSetElemAttr(resourceName, "explicit_auth_flows.*", "ADMIN_NO_SRP_AUTH"), resource.TestCheckResourceAttr(resourceName, "token_validity_units.#", "0"), @@ -47,8 +46,8 @@ func TestAccAWSCognitoUserPoolClient_basic(t *testing.T) { func TestAccAWSCognitoUserPoolClient_enableRevocation(t *testing.T) { var client cognitoidentityprovider.UserPoolClientType - userPoolName := fmt.Sprintf("tf-acc-cognito-user-pool-%s", acctest.RandString(7)) - clientName := acctest.RandString(10) + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_cognito_user_pool_client.test" resource.ParallelTest(t, resource.TestCase{ @@ -58,10 +57,10 @@ func TestAccAWSCognitoUserPoolClient_enableRevocation(t *testing.T) { CheckDestroy: testAccCheckAWSCognitoUserPoolClientDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSCognitoUserPoolClientRevocationConfig(userPoolName, clientName, true), + Config: testAccAWSCognitoUserPoolClientRevocationConfig(rName, true), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), - resource.TestCheckResourceAttr(resourceName, "name", clientName), + resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "enable_token_revocation", "true"), ), }, @@ -72,18 +71,18 @@ func TestAccAWSCognitoUserPoolClient_enableRevocation(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccAWSCognitoUserPoolClientRevocationConfig(userPoolName, clientName, false), + Config: testAccAWSCognitoUserPoolClientRevocationConfig(rName, false), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), - resource.TestCheckResourceAttr(resourceName, "name", clientName), + resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "enable_token_revocation", "false"), ), }, { - Config: testAccAWSCognitoUserPoolClientRevocationConfig(userPoolName, clientName, true), + Config: testAccAWSCognitoUserPoolClientRevocationConfig(rName, true), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), - resource.TestCheckResourceAttr(resourceName, "name", clientName), + resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "enable_token_revocation", "true"), ), }, @@ -317,8 +316,7 @@ func TestAccAWSCognitoUserPoolClient_Name(t *testing.T) { func TestAccAWSCognitoUserPoolClient_allFields(t *testing.T) { var client cognitoidentityprovider.UserPoolClientType - userPoolName := fmt.Sprintf("tf-acc-cognito-user-pool-%s", acctest.RandString(7)) - clientName := acctest.RandString(10) + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_cognito_user_pool_client.test" resource.ParallelTest(t, resource.TestCase{ @@ -328,10 +326,10 @@ func TestAccAWSCognitoUserPoolClient_allFields(t *testing.T) { CheckDestroy: testAccCheckAWSCognitoUserPoolClientDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSCognitoUserPoolClientConfig_allFields(userPoolName, clientName, 300), + Config: testAccAWSCognitoUserPoolClientConfig_allFields(rName, 300), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), - resource.TestCheckResourceAttr(resourceName, "name", clientName), + resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "explicit_auth_flows.#", "3"), resource.TestCheckTypeSetElemAttr(resourceName, "explicit_auth_flows.*", "CUSTOM_AUTH_FLOW_ONLY"), resource.TestCheckTypeSetElemAttr(resourceName, "explicit_auth_flows.*", "USER_PASSWORD_AUTH"), @@ -374,8 +372,7 @@ func TestAccAWSCognitoUserPoolClient_allFields(t *testing.T) { func TestAccAWSCognitoUserPoolClient_allFieldsUpdatingOneField(t *testing.T) { var client cognitoidentityprovider.UserPoolClientType - userPoolName := fmt.Sprintf("tf-acc-cognito-user-pool-%s", acctest.RandString(7)) - clientName := acctest.RandString(10) + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_cognito_user_pool_client.test" resource.ParallelTest(t, resource.TestCase{ @@ -385,13 +382,13 @@ func TestAccAWSCognitoUserPoolClient_allFieldsUpdatingOneField(t *testing.T) { CheckDestroy: testAccCheckAWSCognitoUserPoolClientDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSCognitoUserPoolClientConfig_allFields(userPoolName, clientName, 300), + Config: testAccAWSCognitoUserPoolClientConfig_allFields(rName, 300), }, { - Config: testAccAWSCognitoUserPoolClientConfig_allFields(userPoolName, clientName, 299), + Config: testAccAWSCognitoUserPoolClientConfig_allFields(rName, 299), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), - resource.TestCheckResourceAttr(resourceName, "name", clientName), + resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "explicit_auth_flows.#", "3"), resource.TestCheckTypeSetElemAttr(resourceName, "explicit_auth_flows.*", "CUSTOM_AUTH_FLOW_ONLY"), resource.TestCheckTypeSetElemAttr(resourceName, "explicit_auth_flows.*", "USER_PASSWORD_AUTH"), @@ -434,8 +431,7 @@ func TestAccAWSCognitoUserPoolClient_allFieldsUpdatingOneField(t *testing.T) { func TestAccAWSCognitoUserPoolClient_analyticsConfig(t *testing.T) { var client cognitoidentityprovider.UserPoolClientType - userPoolName := fmt.Sprintf("tf-acc-cognito-user-pool-%s", acctest.RandString(7)) - clientName := acctest.RandString(10) + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_cognito_user_pool_client.test" pinpointResourceName := "aws_pinpoint_app.test" @@ -450,12 +446,12 @@ func TestAccAWSCognitoUserPoolClient_analyticsConfig(t *testing.T) { CheckDestroy: testAccCheckAWSCognitoUserPoolClientDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSCognitoUserPoolClientConfigAnalyticsConfig(userPoolName, clientName), + Config: testAccAWSCognitoUserPoolClientConfigAnalyticsConfig(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), resource.TestCheckResourceAttr(resourceName, "analytics_configuration.#", "1"), resource.TestCheckResourceAttrPair(resourceName, "analytics_configuration.0.application_id", pinpointResourceName, "id"), - resource.TestCheckResourceAttr(resourceName, "analytics_configuration.0.external_id", clientName), + resource.TestCheckResourceAttr(resourceName, "analytics_configuration.0.external_id", rName), resource.TestCheckResourceAttr(resourceName, "analytics_configuration.0.user_data_shared", "false"), ), }, @@ -466,19 +462,19 @@ func TestAccAWSCognitoUserPoolClient_analyticsConfig(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccAWSCognitoUserPoolClientConfig_basic(userPoolName, clientName), + Config: testAccAWSCognitoUserPoolClientConfig_basic(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), resource.TestCheckResourceAttr(resourceName, "analytics_configuration.#", "0"), ), }, { - Config: testAccAWSCognitoUserPoolClientConfigAnalyticsConfigShareUserData(userPoolName, clientName), + Config: testAccAWSCognitoUserPoolClientConfigAnalyticsConfigShareUserData(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), resource.TestCheckResourceAttr(resourceName, "analytics_configuration.#", "1"), resource.TestCheckResourceAttrPair(resourceName, "analytics_configuration.0.application_id", pinpointResourceName, "id"), - resource.TestCheckResourceAttr(resourceName, "analytics_configuration.0.external_id", clientName), + resource.TestCheckResourceAttr(resourceName, "analytics_configuration.0.external_id", rName), resource.TestCheckResourceAttr(resourceName, "analytics_configuration.0.user_data_shared", "true"), ), }, @@ -488,8 +484,7 @@ func TestAccAWSCognitoUserPoolClient_analyticsConfig(t *testing.T) { func TestAccAWSCognitoUserPoolClient_analyticsConfigWithArn(t *testing.T) { var client cognitoidentityprovider.UserPoolClientType - userPoolName := acctest.RandString(10) - clientName := acctest.RandString(10) + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_cognito_user_pool_client.test" resource.ParallelTest(t, resource.TestCase{ @@ -503,7 +498,7 @@ func TestAccAWSCognitoUserPoolClient_analyticsConfigWithArn(t *testing.T) { CheckDestroy: testAccCheckAWSCognitoUserPoolClientDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSCognitoUserPoolClientConfigAnalyticsWithArnConfig(userPoolName, clientName), + Config: testAccAWSCognitoUserPoolClientConfigAnalyticsWithArnConfig(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), resource.TestCheckResourceAttr(resourceName, "analytics_configuration.#", "1"), @@ -524,8 +519,7 @@ func TestAccAWSCognitoUserPoolClient_analyticsConfigWithArn(t *testing.T) { func TestAccAWSCognitoUserPoolClient_disappears(t *testing.T) { var client cognitoidentityprovider.UserPoolClientType - userPoolName := fmt.Sprintf("tf-acc-cognito-user-pool-%s", acctest.RandString(7)) - clientName := acctest.RandString(10) + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_cognito_user_pool_client.test" resource.ParallelTest(t, resource.TestCase{ @@ -535,7 +529,7 @@ func TestAccAWSCognitoUserPoolClient_disappears(t *testing.T) { CheckDestroy: testAccCheckAWSCognitoUserPoolClientDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSCognitoUserPoolClientConfig_basic(userPoolName, clientName), + Config: testAccAWSCognitoUserPoolClientConfig_basic(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), testAccCheckResourceDisappears(testAccProvider, resourceAwsCognitoUserPoolClient(), resourceName), @@ -548,8 +542,7 @@ func TestAccAWSCognitoUserPoolClient_disappears(t *testing.T) { func TestAccAWSCognitoUserPoolClient_disappears_userPool(t *testing.T) { var client cognitoidentityprovider.UserPoolClientType - userPoolName := fmt.Sprintf("tf-acc-cognito-user-pool-%s", acctest.RandString(7)) - clientName := acctest.RandString(10) + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_cognito_user_pool_client.test" resource.ParallelTest(t, resource.TestCase{ @@ -559,7 +552,7 @@ func TestAccAWSCognitoUserPoolClient_disappears_userPool(t *testing.T) { CheckDestroy: testAccCheckAWSCognitoUserPoolClientDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSCognitoUserPoolClientConfig_basic(userPoolName, clientName), + Config: testAccAWSCognitoUserPoolClientConfig_basic(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), testAccCheckResourceDisappears(testAccProvider, resourceAwsCognitoUserPool(), "aws_cognito_user_pool.test"), @@ -655,83 +648,67 @@ func testAccCheckAWSCognitoUserPoolClientExists(name string, client *cognitoiden } } -func testAccAWSCognitoUserPoolClientConfig_basic(userPoolName, clientName string) string { +func testAccAWSCognitoUserPoolClientConfigBase(rName string) string { return fmt.Sprintf(` resource "aws_cognito_user_pool" "test" { - name = "%s" + name = %[1]q +} +`, rName) } +func testAccAWSCognitoUserPoolClientConfig_basic(rName string) string { + return testAccAWSCognitoUserPoolClientConfigBase(rName) + fmt.Sprintf(` resource "aws_cognito_user_pool_client" "test" { - name = "%s" + name = %[1]q user_pool_id = aws_cognito_user_pool.test.id explicit_auth_flows = ["ADMIN_NO_SRP_AUTH"] } -`, userPoolName, clientName) -} - -func testAccAWSCognitoUserPoolClientRevocationConfig(userPoolName, clientName string, revoke bool) string { - return fmt.Sprintf(` -resource "aws_cognito_user_pool" "test" { - name = %[1]q +`, rName) } +func testAccAWSCognitoUserPoolClientRevocationConfig(rName string, revoke bool) string { + return testAccAWSCognitoUserPoolClientConfigBase(rName) + fmt.Sprintf(` resource "aws_cognito_user_pool_client" "test" { - name = %[2]q + name = %[1]q user_pool_id = aws_cognito_user_pool.test.id explicit_auth_flows = ["ADMIN_NO_SRP_AUTH"] - enable_token_revocation = %[3]t + enable_token_revocation = %[2]t } -`, userPoolName, clientName, revoke) +`, rName, revoke) } func testAccAWSCognitoUserPoolClientConfig_RefreshTokenValidity(rName string, refreshTokenValidity int) string { - return fmt.Sprintf(` -resource "aws_cognito_user_pool" "test" { - name = "%s" -} - + return testAccAWSCognitoUserPoolClientConfigBase(rName) + fmt.Sprintf(` resource "aws_cognito_user_pool_client" "test" { - name = "%s" - refresh_token_validity = %d + name = %[1]q + refresh_token_validity = %[2]d user_pool_id = aws_cognito_user_pool.test.id } -`, rName, rName, refreshTokenValidity) +`, rName, refreshTokenValidity) } func testAccAWSCognitoUserPoolClientConfigAccessTokenValidity(rName string, validity int) string { - return fmt.Sprintf(` -resource "aws_cognito_user_pool" "test" { - name = "%s" -} - + return testAccAWSCognitoUserPoolClientConfigBase(rName) + fmt.Sprintf(` resource "aws_cognito_user_pool_client" "test" { - name = "%s" - access_token_validity = %d + name = %[1]q + access_token_validity = %[2]d user_pool_id = aws_cognito_user_pool.test.id } -`, rName, rName, validity) +`, rName, validity) } func testAccAWSCognitoUserPoolClientConfigIDTokenValidity(rName string, validity int) string { - return fmt.Sprintf(` -resource "aws_cognito_user_pool" "test" { - name = "%s" -} - + return testAccAWSCognitoUserPoolClientConfigBase(rName) + fmt.Sprintf(` resource "aws_cognito_user_pool_client" "test" { - name = "%s" - id_token_validity = %d + name = %[1]q + id_token_validity = %[2]d user_pool_id = aws_cognito_user_pool.test.id } -`, rName, rName, validity) +`, rName, validity) } func testAccAWSCognitoUserPoolClientConfigTokenValidityUnits(rName, units string) string { - return fmt.Sprintf(` -resource "aws_cognito_user_pool" "test" { - name = %[1]q -} - + return testAccAWSCognitoUserPoolClientConfigBase(rName) + fmt.Sprintf(` resource "aws_cognito_user_pool_client" "test" { name = %[1]q user_pool_id = aws_cognito_user_pool.test.id @@ -746,11 +723,7 @@ resource "aws_cognito_user_pool_client" "test" { } func testAccAWSCognitoUserPoolClientConfigTokenValidityUnitsWithTokenValidity(rName, units string) string { - return fmt.Sprintf(` -resource "aws_cognito_user_pool" "test" { - name = %[1]q -} - + return testAccAWSCognitoUserPoolClientConfigBase(rName) + fmt.Sprintf(` resource "aws_cognito_user_pool_client" "test" { name = %[1]q user_pool_id = aws_cognito_user_pool.test.id @@ -766,26 +739,18 @@ resource "aws_cognito_user_pool_client" "test" { } func testAccAWSCognitoUserPoolClientConfig_Name(rName, name string) string { - return fmt.Sprintf(` -resource "aws_cognito_user_pool" "test" { - name = %[1]q -} - + return testAccAWSCognitoUserPoolClientConfigBase(rName) + fmt.Sprintf(` resource "aws_cognito_user_pool_client" "test" { - name = %[2]q + name = %[1]q user_pool_id = aws_cognito_user_pool.test.id } -`, rName, name) -} - -func testAccAWSCognitoUserPoolClientConfig_allFields(userPoolName, clientName string, refreshTokenValidity int) string { - return fmt.Sprintf(` -resource "aws_cognito_user_pool" "test" { - name = "%s" +`, name) } +func testAccAWSCognitoUserPoolClientConfig_allFields(rName string, refreshTokenValidity int) string { + return testAccAWSCognitoUserPoolClientConfigBase(rName) + fmt.Sprintf(` resource "aws_cognito_user_pool_client" "test" { - name = "%s" + name = %[1]q user_pool_id = aws_cognito_user_pool.test.id explicit_auth_flows = ["ADMIN_NO_SRP_AUTH", "CUSTOM_AUTH_FLOW_ONLY", "USER_PASSWORD_AUTH"] @@ -795,7 +760,7 @@ resource "aws_cognito_user_pool_client" "test" { read_attributes = ["email"] write_attributes = ["email"] - refresh_token_validity = %d + refresh_token_validity = %[2]d prevent_user_existence_errors = "LEGACY" allowed_oauth_flows = ["code", "implicit"] @@ -806,25 +771,21 @@ resource "aws_cognito_user_pool_client" "test" { default_redirect_uri = "https://www.example.com/redirect" logout_urls = ["https://www.example.com/login"] } -`, userPoolName, clientName, refreshTokenValidity) +`, rName, refreshTokenValidity) } -func testAccAWSCognitoUserPoolClientConfigAnalyticsConfigBase(userPoolName, clientName string) string { - return fmt.Sprintf(` +func testAccAWSCognitoUserPoolClientConfigAnalyticsConfigBase(rName string) string { + return testAccAWSCognitoUserPoolClientConfigBase(rName) + fmt.Sprintf(` data "aws_caller_identity" "current" {} data "aws_partition" "current" {} -resource "aws_cognito_user_pool" "test" { - name = %[1]q -} - resource "aws_pinpoint_app" "test" { - name = %[2]q + name = %[1]q } resource "aws_iam_role" "test" { - name = %[2]q + name = %[1]q assume_role_policy = < Date: Thu, 1 Jul 2021 08:56:11 -0400 Subject: [PATCH 103/398] r/aws_transfer_server: Add tests to set no VPC subnet IDs or security group IDs. --- aws/resource_aws_transfer_server.go | 21 +++++++++--- aws/resource_aws_transfer_server_test.go | 43 ++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/aws/resource_aws_transfer_server.go b/aws/resource_aws_transfer_server.go index fc06e68e462..162533614f9 100644 --- a/aws/resource_aws_transfer_server.go +++ b/aws/resource_aws_transfer_server.go @@ -376,7 +376,7 @@ func resourceAwsTransferServerUpdate(d *schema.ResourceData, meta interface{}) e conn := meta.(*AWSClient).transferconn if d.HasChangesExcept("tags", "tags_all") { - //TODO var addressAllocationIDs []*string + var addressAllocationIDs []*string var offlineUpdate bool input := &transfer.UpdateServerInput{ @@ -404,10 +404,9 @@ func resourceAwsTransferServerUpdate(d *schema.ResourceData, meta interface{}) e } if newEndpointTypeVpc && !oldEndpointTypeVpc { - // TODO ???? // Prevent the following error: InvalidRequestException: Cannot specify AddressAllocationids when updating server to EndpointType: VPC - // addressAllocationIDs = input.EndpointDetails.AddressAllocationIds - // input.EndpointDetails.AddressAllocationIds = nil + addressAllocationIDs = input.EndpointDetails.AddressAllocationIds + input.EndpointDetails.AddressAllocationIds = nil // Prevent the following error: InvalidRequestException: VPC Endpoint ID unsupported for EndpointType: VPC input.EndpointDetails.VpcEndpointId = nil @@ -505,6 +504,20 @@ func resourceAwsTransferServerUpdate(d *schema.ResourceData, meta interface{}) e return err } + // Set any AddressAllocationIds if the server has updated endpoint type to VPC. + if len(addressAllocationIDs) > 0 { + input := &transfer.UpdateServerInput{ + ServerId: aws.String(d.Id()), + EndpointDetails: &transfer.EndpointDetails{ + AddressAllocationIds: addressAllocationIDs, + }, + } + + if err := updateTransferServer(conn, input); err != nil { + return err + } + } + if offlineUpdate { if err := startTransferServer(conn, d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil { return err diff --git a/aws/resource_aws_transfer_server_test.go b/aws/resource_aws_transfer_server_test.go index 6400e56f1ab..be2668959b3 100644 --- a/aws/resource_aws_transfer_server_test.go +++ b/aws/resource_aws_transfer_server_test.go @@ -275,6 +275,20 @@ func testAccAWSTransferServer_vpc(t *testing.T) { resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), ), }, + { + Config: testAccAWSTransferServerVpcConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTransferServerExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", defaultSecurityGroupResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), + resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), + resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + ), + }, }, }) } @@ -333,6 +347,20 @@ func testAccAWSTransferServer_vpcAddressAllocationIds(t *testing.T) { resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), ), }, + { + Config: testAccAWSTransferServerVpcConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTransferServerExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", defaultSecurityGroupResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), + resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), + resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + ), + }, }, }) } @@ -342,6 +370,7 @@ func testAccAWSTransferServer_vpcSecurityGroupIds(t *testing.T) { resourceName := "aws_transfer_server.test" securityGroup1ResourceName := "aws_security_group.test" securityGroup2ResourceName := "aws_security_group.test2" + defaultSecurityGroupResourceName := "aws_default_security_group.test" vpcResourceName := "aws_vpc.test" rName := acctest.RandomWithPrefix("tf-acc-test") @@ -385,6 +414,20 @@ func testAccAWSTransferServer_vpcSecurityGroupIds(t *testing.T) { resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), ), }, + { + Config: testAccAWSTransferServerVpcConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTransferServerExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", defaultSecurityGroupResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), + resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), + resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + ), + }, }, }) } From d635b87ee9b04e94637eabd955ed2f9cce40eed4 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 1 Jul 2021 10:05:18 -0400 Subject: [PATCH 104/398] r/aws_vpc_endpoint: Ignore errors such as Error: error deleting EC2 VPC Endpoint (vpce-09a9ae6b78f2b0571): 1 error occurred: * vpce-09a9ae6b78f2b0571: InvalidVpcEndpoint.NotFound: The Vpc Endpoint Id 'vpce-09a9ae6b78f2b0571' does not exist --- aws/internal/service/ec2/errors.go | 4 +- aws/internal/service/ec2/errors_test.go | 133 ++++++++++++++++++++++++ aws/resource_aws_vpc_endpoint.go | 14 +-- 3 files changed, 141 insertions(+), 10 deletions(-) create mode 100644 aws/internal/service/ec2/errors_test.go diff --git a/aws/internal/service/ec2/errors.go b/aws/internal/service/ec2/errors.go index cbcac12e097..e4c94a55408 100644 --- a/aws/internal/service/ec2/errors.go +++ b/aws/internal/service/ec2/errors.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/ec2" multierror "github.com/hashicorp/go-multierror" ) @@ -69,6 +70,7 @@ const ( const ( ErrCodeInvalidVpcEndpointIdNotFound = "InvalidVpcEndpointId.NotFound" + ErrCodeInvalidVpcEndpointNotFound = "InvalidVpcEndpoint.NotFound" ErrCodeInvalidVpcEndpointServiceIdNotFound = "InvalidVpcEndpointServiceId.NotFound" ) @@ -86,7 +88,7 @@ func UnsuccessfulItemError(apiObject *ec2.UnsuccessfulItemError) error { return nil } - return fmt.Errorf("%s: %s", aws.StringValue(apiObject.Code), aws.StringValue(apiObject.Message)) + return awserr.New(aws.StringValue(apiObject.Code), aws.StringValue(apiObject.Message), nil) } func UnsuccessfulItemsError(apiObjects []*ec2.UnsuccessfulItem) error { diff --git a/aws/internal/service/ec2/errors_test.go b/aws/internal/service/ec2/errors_test.go new file mode 100644 index 00000000000..7bdc90fa31b --- /dev/null +++ b/aws/internal/service/ec2/errors_test.go @@ -0,0 +1,133 @@ +package ec2_test + +import ( + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + tfec2 "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2" +) + +func TestUnsuccessfulItemError(t *testing.T) { + unsuccessfulItemError := &ec2.UnsuccessfulItemError{ + Code: aws.String("test code"), + Message: aws.String("test message"), + } + + err := tfec2.UnsuccessfulItemError(unsuccessfulItemError) + + if !tfawserr.ErrCodeEquals(err, "test code") { + t.Errorf("tfawserr.ErrCodeEquals failed: %s", err) + } + + if !tfawserr.ErrMessageContains(err, "test code", "est mess") { + t.Errorf("tfawserr.ErrMessageContains failed: %s", err) + } +} + +func TestUnsuccessfulItemsError(t *testing.T) { + testCases := []struct { + Name string + Items []*ec2.UnsuccessfulItem + Expected bool + }{ + { + Name: "no items", + }, + { + Name: "one item no error", + Items: []*ec2.UnsuccessfulItem{ + { + ResourceId: aws.String("test resource"), + }, + }, + }, + { + Name: "one item", + Items: []*ec2.UnsuccessfulItem{ + { + Error: &ec2.UnsuccessfulItemError{ + Code: aws.String("test code"), + Message: aws.String("test message"), + }, + ResourceId: aws.String("test resource"), + }, + }, + Expected: true, + }, + { + Name: "two items, first no error", + Items: []*ec2.UnsuccessfulItem{ + { + ResourceId: aws.String("test resource 1"), + }, + { + Error: &ec2.UnsuccessfulItemError{ + Code: aws.String("test code"), + Message: aws.String("test message"), + }, + ResourceId: aws.String("test resource 2"), + }, + }, + Expected: true, + }, + { + Name: "two items, first not as expected", + Items: []*ec2.UnsuccessfulItem{ + { + Error: &ec2.UnsuccessfulItemError{ + Code: aws.String("not what is required"), + Message: aws.String("not what is wanted"), + }, + ResourceId: aws.String("test resource 1"), + }, + { + Error: &ec2.UnsuccessfulItemError{ + Code: aws.String("test code"), + Message: aws.String("test message"), + }, + ResourceId: aws.String("test resource 2"), + }, + }, + }, + { + Name: "two items, first as expected", + Items: []*ec2.UnsuccessfulItem{ + { + Error: &ec2.UnsuccessfulItemError{ + Code: aws.String("test code"), + Message: aws.String("test message"), + }, + ResourceId: aws.String("test resource 1"), + }, + { + Error: &ec2.UnsuccessfulItemError{ + Code: aws.String("not what is required"), + Message: aws.String("not what is wanted"), + }, + ResourceId: aws.String("test resource 2"), + }, + }, + Expected: true, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.Name, func(t *testing.T) { + err := tfec2.UnsuccessfulItemsError(testCase.Items) + + got := tfawserr.ErrCodeEquals(err, "test code") + + if got != testCase.Expected { + t.Errorf("ErrCodeEquals got %t, expected %t", got, testCase.Expected) + } + + got = tfawserr.ErrMessageContains(err, "test code", "est mess") + + if got != testCase.Expected { + t.Errorf("ErrMessageContains got %t, expected %t", got, testCase.Expected) + } + }) + } +} diff --git a/aws/resource_aws_vpc_endpoint.go b/aws/resource_aws_vpc_endpoint.go index 0f372c3acf0..8520c36a88a 100644 --- a/aws/resource_aws_vpc_endpoint.go +++ b/aws/resource_aws_vpc_endpoint.go @@ -377,7 +377,11 @@ func resourceAwsVpcEndpointDelete(d *schema.ResourceData, meta interface{}) erro output, err := conn.DeleteVpcEndpoints(input) - if tfawserr.ErrCodeEquals(err, tfec2.ErrCodeInvalidVpcEndpointIdNotFound) { + if err == nil && output != nil { + err = tfec2.UnsuccessfulItemsError(output.Unsuccessful) + } + + if tfawserr.ErrCodeEquals(err, tfec2.ErrCodeInvalidVpcEndpointNotFound) { return nil } @@ -385,14 +389,6 @@ func resourceAwsVpcEndpointDelete(d *schema.ResourceData, meta interface{}) erro return fmt.Errorf("error deleting EC2 VPC Endpoint (%s): %w", d.Id(), err) } - if output != nil && len(output.Unsuccessful) > 0 { - err := tfec2.UnsuccessfulItemsError(output.Unsuccessful) - - if err != nil { - return fmt.Errorf("error deleting EC2 VPC Endpoint (%s): %w", d.Id(), err) - } - } - _, err = waiter.VpcEndpointDeleted(conn, d.Id(), d.Timeout(schema.TimeoutDelete)) if err != nil { From 163c41745c837c26d74bff0d2771db3bd7d727bf Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Thu, 1 Jul 2021 17:25:46 +0300 Subject: [PATCH 105/398] docs --- website/docs/r/cognito_user_pool_client.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/website/docs/r/cognito_user_pool_client.markdown b/website/docs/r/cognito_user_pool_client.markdown index a4fed4fc222..a3e6d692063 100644 --- a/website/docs/r/cognito_user_pool_client.markdown +++ b/website/docs/r/cognito_user_pool_client.markdown @@ -126,6 +126,7 @@ The following arguments are optional: * `analytics_configuration` - (Optional) Configuration block for Amazon Pinpoint analytics for collecting metrics for this user pool. [Detailed below](#analytics_configuration). * `callback_urls` - (Optional) List of allowed callback URLs for the identity providers. * `default_redirect_uri` - (Optional) Default redirect URI. Must be in the list of callback URLs. +* `enable_token_revocation` - (Optional) Enables or disables token revocation. * `explicit_auth_flows` - (Optional) List of authentication flows (ADMIN_NO_SRP_AUTH, CUSTOM_AUTH_FLOW_ONLY, USER_PASSWORD_AUTH, ALLOW_ADMIN_USER_PASSWORD_AUTH, ALLOW_CUSTOM_AUTH, ALLOW_USER_PASSWORD_AUTH, ALLOW_USER_SRP_AUTH, ALLOW_REFRESH_TOKEN_AUTH). * `generate_secret` - (Optional) Should an application secret be generated. * `id_token_validity` - (Optional) Time limit, between 5 minutes and 1 day, after which the ID token is no longer valid and cannot be used. This value will be overridden if you have entered a value in `token_validity_units`. From e6c3deaa516092a474ab8d510762be242e9ec94d Mon Sep 17 00:00:00 2001 From: Deku-shrub Date: Thu, 1 Jul 2021 15:37:35 +0100 Subject: [PATCH 106/398] correct value --- aws/data_source_aws_wafv2_regex_pattern_set_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/data_source_aws_wafv2_regex_pattern_set_test.go b/aws/data_source_aws_wafv2_regex_pattern_set_test.go index 0d2fb68921d..a1a4c962124 100644 --- a/aws/data_source_aws_wafv2_regex_pattern_set_test.go +++ b/aws/data_source_aws_wafv2_regex_pattern_set_test.go @@ -32,7 +32,7 @@ func TestAccDataSourceAwsWafv2RegexPatternSet_basic(t *testing.T) { resource.TestCheckResourceAttrPair(datasourceName, "description", resourceName, "description"), resource.TestCheckResourceAttrPair(datasourceName, "id", resourceName, "id"), resource.TestCheckResourceAttrPair(datasourceName, "name", resourceName, "name"), - resource.TestCheckResourceAttrPair(datasourceName, "regular_expression_list", resourceName, "regular_expression_list"), + resource.TestCheckResourceAttrPair(datasourceName, "regular_expression", resourceName, "regular_expression"), resource.TestCheckResourceAttrPair(datasourceName, "scope", resourceName, "scope"), ), }, From dc51a726fdafcd5624979a16007520cfff1eec5a Mon Sep 17 00:00:00 2001 From: Deku-shrub Date: Thu, 1 Jul 2021 15:38:57 +0100 Subject: [PATCH 107/398] correct value --- website/docs/r/wafv2_rule_group.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/wafv2_rule_group.html.markdown b/website/docs/r/wafv2_rule_group.html.markdown index a8432218bf1..276bcb306d8 100644 --- a/website/docs/r/wafv2_rule_group.html.markdown +++ b/website/docs/r/wafv2_rule_group.html.markdown @@ -64,7 +64,7 @@ resource "aws_wafv2_regex_pattern_set" "test" { name = "test" scope = "REGIONAL" - regular_expression_list { + regular_expression { regex_string = "one" } } From 62593d79cb062f7e09244442f8f248e1b5a6eddb Mon Sep 17 00:00:00 2001 From: Jeremy Ciak Date: Wed, 9 Dec 2020 22:37:15 -0500 Subject: [PATCH 108/398] Adding support for aliases with FSx for Windows --- aws/resource_aws_fsx_windows_file_system.go | 105 ++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/aws/resource_aws_fsx_windows_file_system.go b/aws/resource_aws_fsx_windows_file_system.go index 1dbf1f7dae0..8f1f11ca8a7 100644 --- a/aws/resource_aws_fsx_windows_file_system.go +++ b/aws/resource_aws_fsx_windows_file_system.go @@ -43,6 +43,18 @@ func resourceAwsFsxWindowsFileSystem() *schema.Resource { ForceNew: true, ConflictsWith: []string{"self_managed_active_directory"}, }, + "aliases": { + Type: schema.TypeSet, + Optional: true, + MaxItems: 50, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.All( + validation.StringLenBetween(4, 253), + validation.StringMatch(regexp.MustCompile(`^[A-Za-z0-9]([.][A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9])+$`), "must be in the fqdn format hostname.domain"), + ), + }, + }, "arn": { Type: schema.TypeString, Computed: true, @@ -265,6 +277,10 @@ func resourceAwsFsxWindowsFileSystemCreate(d *schema.ResourceData, meta interfac input.WindowsConfiguration.ActiveDirectoryId = aws.String(v.(string)) } + if v, ok := d.GetOk("aliases"); ok { + input.WindowsConfiguration.Aliases = expandStringSet(v.(*schema.Set)) + } + if v, ok := d.GetOk("deployment_type"); ok { input.WindowsConfiguration.DeploymentType = aws.String(v.(string)) } @@ -339,6 +355,14 @@ func resourceAwsFsxWindowsFileSystemUpdate(d *schema.ResourceData, meta interfac WindowsConfiguration: &fsx.UpdateFileSystemWindowsConfiguration{}, } + if d.HasChange("aliases") { + o, n := d.GetChange("aliases") + + if err := updateFsxAliases(conn, d.Get("arn").(string), o.([]*fsx.Alias), n.([]*fsx.Alias)); err != nil { + return fmt.Errorf("error updating FSx Windows File System (%s) aliases: %s", d.Get("arn").(string), err) + } + } + if d.HasChange("automatic_backup_retention_days") { input.WindowsConfiguration.AutomaticBackupRetentionDays = aws.Int64(int64(d.Get("automatic_backup_retention_days").(int))) } @@ -425,6 +449,10 @@ func resourceAwsFsxWindowsFileSystemRead(d *schema.ResourceData, meta interface{ d.Set("kms_key_id", filesystem.KmsKeyId) d.Set("storage_type", filesystem.StorageType) + if err := d.Set("aliases", aws.StringValueSlice(expandFsxAliasValues(filesystem.WindowsConfiguration.Aliases))); err != nil { + return fmt.Errorf("error setting aliases: %s", err) + } + if err := d.Set("network_interface_ids", aws.StringValueSlice(filesystem.NetworkInterfaceIds)); err != nil { return fmt.Errorf("error setting network_interface_ids: %w", err) } @@ -493,6 +521,83 @@ func resourceAwsFsxWindowsFileSystemDelete(d *schema.ResourceData, meta interfac return nil } +func expandFsxAliasValues(aliases []*fsx.Alias) []*string { + var alternateDNSNames []*string + + for i := 0; i < len(aliases); i++ { + alternateDNSNames[i] = aliases[i].Name + } + + return alternateDNSNames +} + +func updateFsxAliases(conn *fsx.FSx, identifier string, oldAliases []*fsx.Alias, newAliases []*fsx.Alias) error { + oldAliasValues := expandFsxAliasValues(oldAliases) + newAliasValues := expandFsxAliasValues(newAliases) + + var removedAliases []*string + + for _, oldAliasValue := range oldAliasValues { + exists := false + + for _, newAliasValue := range newAliasValues { + if newAliasValue == oldAliasValue { + exists = true + break + } + } + + if !exists { + removedAliases = append(removedAliases, oldAliasValue) + } + } + + if len(removedAliases) > 0 { + input := &fsx.DisassociateFileSystemAliasesInput{ + FileSystemId: aws.String(identifier), + Aliases: removedAliases, + } + + _, err := conn.DisassociateFileSystemAliases(input) + + if err != nil { + return fmt.Errorf("error disassociating aliases from FSx file system (%s): %w", identifier, err) + } + } + + var addedAliases []*string + + for _, newAliasValue := range newAliasValues { + exists := false + + for _, oldAliasValue := range oldAliasValues { + if oldAliasValue == newAliasValue { + exists = true + break + } + } + + if !exists { + addedAliases = append(addedAliases, newAliasValue) + } + } + + if len(addedAliases) > 0 { + input := &fsx.AssociateFileSystemAliasesInput{ + FileSystemId: aws.String(identifier), + Aliases: addedAliases, + } + + _, err := conn.AssociateFileSystemAliases(input) + + if err != nil { + return fmt.Errorf("error associating aliases to FSx file system (%s): %w", identifier, err) + } + } + + return nil +} + func expandFsxSelfManagedActiveDirectoryConfigurationCreate(l []interface{}) *fsx.SelfManagedActiveDirectoryConfiguration { if len(l) == 0 || l[0] == nil { return nil From d9b8d0715718f461a94d692881dce673ec36c9d7 Mon Sep 17 00:00:00 2001 From: Jeremy Ciak Date: Wed, 9 Dec 2020 22:37:29 -0500 Subject: [PATCH 109/398] Adding tests to account for the new support for aliases --- ...source_aws_fsx_windows_file_system_test.go | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/aws/resource_aws_fsx_windows_file_system_test.go b/aws/resource_aws_fsx_windows_file_system_test.go index 20b4b679c8e..9feae003698 100644 --- a/aws/resource_aws_fsx_windows_file_system_test.go +++ b/aws/resource_aws_fsx_windows_file_system_test.go @@ -85,6 +85,7 @@ func TestAccAWSFsxWindowsFileSystem_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "fsx", regexp.MustCompile(`file-system/fs-.+`)), + resource.TestCheckResourceAttr(resourceName, "aliases.#", "0"), resource.TestCheckResourceAttr(resourceName, "automatic_backup_retention_days", "7"), resource.TestCheckResourceAttr(resourceName, "copy_tags_to_backups", "false"), resource.TestMatchResourceAttr(resourceName, "daily_automatic_backup_start_time", regexp.MustCompile(`^\d\d:\d\d$`)), @@ -140,6 +141,7 @@ func TestAccAWSFsxWindowsFileSystem_singleAz2(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "fsx", regexp.MustCompile(`file-system/fs-.+`)), + resource.TestCheckResourceAttr(resourceName, "aliases.#", "0"), resource.TestCheckResourceAttr(resourceName, "automatic_backup_retention_days", "7"), resource.TestCheckResourceAttr(resourceName, "copy_tags_to_backups", "false"), resource.TestMatchResourceAttr(resourceName, "daily_automatic_backup_start_time", regexp.MustCompile(`^\d\d:\d\d$`)), @@ -219,6 +221,7 @@ func TestAccAWSFsxWindowsFileSystem_multiAz(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "fsx", regexp.MustCompile(`file-system/fs-.+`)), + resource.TestCheckResourceAttr(resourceName, "aliases.#", "0"), resource.TestCheckResourceAttr(resourceName, "automatic_backup_retention_days", "7"), resource.TestCheckResourceAttr(resourceName, "copy_tags_to_backups", "false"), resource.TestMatchResourceAttr(resourceName, "daily_automatic_backup_start_time", regexp.MustCompile(`^\d\d:\d\d$`)), @@ -273,6 +276,55 @@ func TestAccAWSFsxWindowsFileSystem_disappears(t *testing.T) { }) } +func TestAccAWSFsxWindowsFileSystem_Aliases(t *testing.T) { + var filesystem1, filesystem2, filesystem3 fsx.FileSystem + resourceName := "aws_fsx_windows_file_system.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(fsx.EndpointsID, t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckFsxWindowsFileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsFsxWindowsFileSystemConfigAliases1("filesystem1.domain.name.com"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem1), + resource.TestCheckResourceAttr(resourceName, "aliases.#", "1"), + resource.TestCheckResourceAttr(resourceName, "aliases.0", "filesystem1.domain.name.com"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "security_group_ids", + "skip_final_backup", + }, + }, + { + Config: testAccAwsFsxWindowsFileSystemConfigAliases2("filesystem1.domain.name.com", "filesystem2.domain.name.com"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem2), + testAccCheckFsxWindowsFileSystemNotRecreated(&filesystem1, &filesystem2), + resource.TestCheckResourceAttr(resourceName, "aliases.#", "2"), + resource.TestCheckResourceAttr(resourceName, "aliases.0", "filesystem1.domain.name.com"), + resource.TestCheckResourceAttr(resourceName, "aliases.1", "filesystem2.domain.name.com"), + ), + }, + { + Config: testAccAwsFsxWindowsFileSystemConfigAliases1("filesystem2.domain.name.com"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem3), + testAccCheckFsxWindowsFileSystemNotRecreated(&filesystem2, &filesystem3), + resource.TestCheckResourceAttr(resourceName, "aliases.#", "1"), + resource.TestCheckResourceAttr(resourceName, "aliases.0", "filesystem2.domain.name.com"), + ), + }, + }, + }) +} + func TestAccAWSFsxWindowsFileSystem_AutomaticBackupRetentionDays(t *testing.T) { var filesystem1, filesystem2, filesystem3 fsx.FileSystem resourceName := "aws_fsx_windows_file_system.test" @@ -861,6 +913,39 @@ resource "aws_directory_service_directory" "test" { ` } +func testAccAwsFsxWindowsFileSystemConfigAliases1(alias1 string) string { + return testAccAwsFsxWindowsFileSystemConfigBase() + fmt.Sprintf(` +resource "aws_fsx_windows_file_system" "test" { + active_directory_id = aws_directory_service_directory.test.id + skip_final_backup = true + storage_capacity = 32 + subnet_ids = [aws_subnet.test1.id] + throughput_capacity = 8 + + aliases = [ + %[1]q + ] +} +`, alias1) +} + +func testAccAwsFsxWindowsFileSystemConfigAliases2(alias1, alias2 string) string { + return testAccAwsFsxWindowsFileSystemConfigBase() + fmt.Sprintf(` +resource "aws_fsx_windows_file_system" "test" { + active_directory_id = aws_directory_service_directory.test.id + skip_final_backup = true + storage_capacity = 32 + subnet_ids = [aws_subnet.test1.id] + throughput_capacity = 8 + + aliases = [ + %[1]q + %[2]q + ] +} +`, alias1, alias2) +} + func testAccAwsFsxWindowsFileSystemConfigAutomaticBackupRetentionDays(automaticBackupRetentionDays int) string { return testAccAwsFsxWindowsFileSystemConfigBase() + fmt.Sprintf(` resource "aws_fsx_windows_file_system" "test" { From 43d31c78ff1fa962d5f08c878af2efe511d60086 Mon Sep 17 00:00:00 2001 From: Jeremy Ciak Date: Wed, 9 Dec 2020 22:37:45 -0500 Subject: [PATCH 110/398] Updating tools --- tools/go.mod | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/go.mod b/tools/go.mod index 02ed3cf722a..f6e63fbb44f 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -5,12 +5,19 @@ go 1.15 require ( github.com/bflad/tfproviderdocs v0.9.1 github.com/client9/misspell v0.3.4 +<<<<<<< HEAD github.com/golangci/golangci-lint v1.39.0 github.com/hashicorp/go-changelog v0.0.0-20201005170154-56335215ce3a github.com/hashicorp/go-getter v1.5.2 // indirect github.com/katbyte/terrafmt v0.3.0 github.com/pavius/impi v0.0.3 github.com/terraform-linters/tflint v0.28.1 +======= + github.com/golangci/golangci-lint v1.33.0 + github.com/katbyte/terrafmt v0.2.1-0.20200913185704-5ff4421407b4 + github.com/pavius/impi v0.0.3 // indirect + github.com/terraform-linters/tflint v0.20.3 +>>>>>>> 9c419a56f (Updating tools) ) replace github.com/katbyte/terrafmt => github.com/gdavison/terrafmt v0.3.1-0.20210204054728-84242796be99 From 3394f1479e61dbf2074126078058fba2678f6db0 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Thu, 1 Jul 2021 20:58:52 +0300 Subject: [PATCH 111/398] revert --- tools/go.mod | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tools/go.mod b/tools/go.mod index f6e63fbb44f..02ed3cf722a 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -5,19 +5,12 @@ go 1.15 require ( github.com/bflad/tfproviderdocs v0.9.1 github.com/client9/misspell v0.3.4 -<<<<<<< HEAD github.com/golangci/golangci-lint v1.39.0 github.com/hashicorp/go-changelog v0.0.0-20201005170154-56335215ce3a github.com/hashicorp/go-getter v1.5.2 // indirect github.com/katbyte/terrafmt v0.3.0 github.com/pavius/impi v0.0.3 github.com/terraform-linters/tflint v0.28.1 -======= - github.com/golangci/golangci-lint v1.33.0 - github.com/katbyte/terrafmt v0.2.1-0.20200913185704-5ff4421407b4 - github.com/pavius/impi v0.0.3 // indirect - github.com/terraform-linters/tflint v0.20.3 ->>>>>>> 9c419a56f (Updating tools) ) replace github.com/katbyte/terrafmt => github.com/gdavison/terrafmt v0.3.1-0.20210204054728-84242796be99 From 8fd8ce797e6c2c76fce26f2390cea326e149dde1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Jul 2021 06:11:49 +0000 Subject: [PATCH 112/398] build(deps): bump github.com/aws/aws-sdk-go in /awsproviderlint Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.38.68 to 1.38.71. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Changelog](https://github.com/aws/aws-sdk-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.38.68...v1.38.71) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- awsproviderlint/go.mod | 2 +- awsproviderlint/go.sum | 4 ++-- .../github.com/aws/aws-sdk-go/aws/endpoints/defaults.go | 3 +++ .../vendor/github.com/aws/aws-sdk-go/aws/version.go | 2 +- awsproviderlint/vendor/modules.txt | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/awsproviderlint/go.mod b/awsproviderlint/go.mod index e5c3fcf0a8e..a1f0079246c 100644 --- a/awsproviderlint/go.mod +++ b/awsproviderlint/go.mod @@ -3,7 +3,7 @@ module github.com/terraform-providers/terraform-provider-aws/awsproviderlint go 1.16 require ( - github.com/aws/aws-sdk-go v1.38.68 + github.com/aws/aws-sdk-go v1.38.71 github.com/bflad/tfproviderlint v0.26.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.7.0 golang.org/x/tools v0.0.0-20201028111035-eafbe7b904eb diff --git a/awsproviderlint/go.sum b/awsproviderlint/go.sum index 744b783008c..54c82c3d98e 100644 --- a/awsproviderlint/go.sum +++ b/awsproviderlint/go.sum @@ -70,8 +70,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= github.com/aws/aws-sdk-go v1.25.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.38.68 h1:aOG8geU4SohNp659eKBHRBgbqSrZ6jNZlfimIuJAwL8= -github.com/aws/aws-sdk-go v1.38.68/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go v1.38.71 h1:aWhtgoOiDhBCfaAj9XbxzcyvjEAKovbtv7d5mCVBZXw= +github.com/aws/aws-sdk-go v1.38.71/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/bflad/gopaniccheck v0.1.0 h1:tJftp+bv42ouERmUMWLoUn/5bi/iQZjHPznM00cP/bU= github.com/bflad/gopaniccheck v0.1.0/go.mod h1:ZCj2vSr7EqVeDaqVsWN4n2MwdROx1YL+LFo47TSWtsA= github.com/bflad/tfproviderlint v0.26.0 h1:Xd+hbVlSQhKlXifpqmHPvlcnOK1lRS4IZf+cXBAUpCs= diff --git a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go index 887c21dcb1c..27797a72e98 100644 --- a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go +++ b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go @@ -982,6 +982,7 @@ var awsPartition = partition{ "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, + "ap-northeast-3": endpoint{}, "ap-south-1": endpoint{}, "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, @@ -3047,6 +3048,7 @@ var awsPartition = partition{ "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, + "ap-northeast-3": endpoint{}, "ap-south-1": endpoint{}, "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, @@ -6737,6 +6739,7 @@ var awsPartition = partition{ "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, + "ap-northeast-3": endpoint{}, "ap-south-1": endpoint{}, "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, diff --git a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/version.go b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/version.go index db652204980..95a41a03b2b 100644 --- a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/version.go +++ b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/version.go @@ -5,4 +5,4 @@ package aws const SDKName = "aws-sdk-go" // SDKVersion is the version of this SDK -const SDKVersion = "1.38.68" +const SDKVersion = "1.38.71" diff --git a/awsproviderlint/vendor/modules.txt b/awsproviderlint/vendor/modules.txt index 656e13681f3..76a11a4180f 100644 --- a/awsproviderlint/vendor/modules.txt +++ b/awsproviderlint/vendor/modules.txt @@ -14,7 +14,7 @@ github.com/agext/levenshtein github.com/apparentlymart/go-textseg/v12/textseg # github.com/apparentlymart/go-textseg/v13 v13.0.0 github.com/apparentlymart/go-textseg/v13/textseg -# github.com/aws/aws-sdk-go v1.38.68 +# github.com/aws/aws-sdk-go v1.38.71 ## explicit github.com/aws/aws-sdk-go/aws github.com/aws/aws-sdk-go/aws/arn From b86fb6ce10dd8fcf52672fb5ef56b2a7c3a64a5d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Jul 2021 06:12:46 +0000 Subject: [PATCH 113/398] build(deps): bump github.com/aws/aws-sdk-go from 1.38.68 to 1.38.71 Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.38.68 to 1.38.71. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Changelog](https://github.com/aws/aws-sdk-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.38.68...v1.38.71) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0a0a9587467..5a3199e20f3 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.16 require ( github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect - github.com/aws/aws-sdk-go v1.38.68 + github.com/aws/aws-sdk-go v1.38.71 github.com/beevik/etree v1.1.0 github.com/fatih/color v1.9.0 // indirect github.com/hashicorp/aws-sdk-go-base v0.7.1 diff --git a/go.sum b/go.sum index e9f95cdd37d..64815f1736e 100644 --- a/go.sum +++ b/go.sum @@ -66,8 +66,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= github.com/aws/aws-sdk-go v1.25.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.31.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/aws/aws-sdk-go v1.38.68 h1:aOG8geU4SohNp659eKBHRBgbqSrZ6jNZlfimIuJAwL8= -github.com/aws/aws-sdk-go v1.38.68/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go v1.38.71 h1:aWhtgoOiDhBCfaAj9XbxzcyvjEAKovbtv7d5mCVBZXw= +github.com/aws/aws-sdk-go v1.38.71/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= From c6345fab946f2d0cfa957dc1ec30080f51e59124 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Fri, 2 Jul 2021 13:40:24 +0300 Subject: [PATCH 114/398] refactor PR --- aws/fsx.go | 72 ++++++++++ aws/resource_aws_fsx_windows_file_system.go | 130 ++++++++++-------- ...source_aws_fsx_windows_file_system_test.go | 30 ++-- 3 files changed, 161 insertions(+), 71 deletions(-) diff --git a/aws/fsx.go b/aws/fsx.go index 73afe902a41..faf6dcce2e8 100644 --- a/aws/fsx.go +++ b/aws/fsx.go @@ -8,6 +8,11 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) +const ( + fsxWindowsFileSystemAliasAvailable = 10 * time.Minute + fsxWindowsFileSystemAliasDeleted = 10 * time.Minute +) + func describeFsxFileSystem(conn *fsx.FSx, id string) (*fsx.FileSystem, error) { input := &fsx.DescribeFileSystemsInput{ FileSystemIds: []*string{aws.String(id)}, @@ -48,6 +53,45 @@ func refreshFsxFileSystemLifecycle(conn *fsx.FSx, id string) resource.StateRefre } } +func refreshFsxWindowsFileSystemAliasLifecycle(conn *fsx.FSx, id, aliasName string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + filesystem, err := describeFsxFileSystem(conn, id) + + if isAWSErr(err, fsx.ErrCodeFileSystemNotFound, "") { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + if filesystem == nil { + return nil, "", nil + } + + if filesystem.WindowsConfiguration == nil { + return nil, "", nil + } + + aliases := filesystem.WindowsConfiguration.Aliases + if aliases == nil { + return nil, "", nil + } + + for _, alias := range aliases { + if alias == nil { + continue + } + + if aws.StringValue(alias.Name) == aliasName { + return filesystem, aws.StringValue(alias.Lifecycle), nil + } + } + + return filesystem, "", nil + } +} + func refreshFsxFileSystemAdministrativeActionsStatusFileSystemUpdate(conn *fsx.FSx, id string) resource.StateRefreshFunc { return func() (interface{}, string, error) { filesystem, err := describeFsxFileSystem(conn, id) @@ -78,6 +122,34 @@ func refreshFsxFileSystemAdministrativeActionsStatusFileSystemUpdate(conn *fsx.F } } +func waitForFsxWindowsFileSystemAliasAvailable(conn *fsx.FSx, id, alias string) error { + stateConf := &resource.StateChangeConf{ + Pending: []string{fsx.AliasLifecycleCreating}, + Target: []string{fsx.AliasLifecycleAvailable}, + Refresh: refreshFsxWindowsFileSystemAliasLifecycle(conn, id, alias), + Timeout: fsxWindowsFileSystemAliasAvailable, + Delay: 30 * time.Second, + } + + _, err := stateConf.WaitForState() + + return err +} + +func waitForFsxWindowsFileSystemAliasDeleted(conn *fsx.FSx, id, alias string) error { + stateConf := &resource.StateChangeConf{ + Pending: []string{fsx.AliasLifecycleAvailable, fsx.AliasLifecycleDeleting}, + Target: []string{""}, + Refresh: refreshFsxWindowsFileSystemAliasLifecycle(conn, id, alias), + Timeout: fsxWindowsFileSystemAliasDeleted, + Delay: 30 * time.Second, + } + + _, err := stateConf.WaitForState() + + return err +} + func waitForFsxFileSystemCreation(conn *fsx.FSx, id string, timeout time.Duration) error { stateConf := &resource.StateChangeConf{ Pending: []string{fsx.FileSystemLifecycleCreating}, diff --git a/aws/resource_aws_fsx_windows_file_system.go b/aws/resource_aws_fsx_windows_file_system.go index 8f1f11ca8a7..ced17dc4c90 100644 --- a/aws/resource_aws_fsx_windows_file_system.go +++ b/aws/resource_aws_fsx_windows_file_system.go @@ -51,7 +51,7 @@ func resourceAwsFsxWindowsFileSystem() *schema.Resource { Type: schema.TypeString, ValidateFunc: validation.All( validation.StringLenBetween(4, 253), - validation.StringMatch(regexp.MustCompile(`^[A-Za-z0-9]([.][A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9])+$`), "must be in the fqdn format hostname.domain"), + // validation.StringMatch(regexp.MustCompile(`^[A-Za-z0-9]([.][A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9])+$`), "must be in the fqdn format hostname.domain"), ), }, }, @@ -348,21 +348,21 @@ func resourceAwsFsxWindowsFileSystemUpdate(d *schema.ResourceData, meta interfac } } - if d.HasChangeExcept("tags_all") { + if d.HasChange("aliases") { + o, n := d.GetChange("aliases") + + if err := updateFsxAliases(conn, d.Id(), o.(*schema.Set), n.(*schema.Set)); err != nil { + return fmt.Errorf("error updating FSx Windows File System (%s) aliases: %w", d.Id(), err) + } + } + + if d.HasChangesExcept("tags_all", "aliases") { input := &fsx.UpdateFileSystemInput{ ClientRequestToken: aws.String(resource.UniqueId()), FileSystemId: aws.String(d.Id()), WindowsConfiguration: &fsx.UpdateFileSystemWindowsConfiguration{}, } - if d.HasChange("aliases") { - o, n := d.GetChange("aliases") - - if err := updateFsxAliases(conn, d.Get("arn").(string), o.([]*fsx.Alias), n.([]*fsx.Alias)); err != nil { - return fmt.Errorf("error updating FSx Windows File System (%s) aliases: %s", d.Get("arn").(string), err) - } - } - if d.HasChange("automatic_backup_retention_days") { input.WindowsConfiguration.AutomaticBackupRetentionDays = aws.Int64(int64(d.Get("automatic_backup_retention_days").(int))) } @@ -524,76 +524,94 @@ func resourceAwsFsxWindowsFileSystemDelete(d *schema.ResourceData, meta interfac func expandFsxAliasValues(aliases []*fsx.Alias) []*string { var alternateDNSNames []*string - for i := 0; i < len(aliases); i++ { - alternateDNSNames[i] = aliases[i].Name + for _, alias := range aliases { + aName := alias.Name + alternateDNSNames = append(alternateDNSNames, aName) } return alternateDNSNames } -func updateFsxAliases(conn *fsx.FSx, identifier string, oldAliases []*fsx.Alias, newAliases []*fsx.Alias) error { - oldAliasValues := expandFsxAliasValues(oldAliases) - newAliasValues := expandFsxAliasValues(newAliases) +func updateFsxAliases(conn *fsx.FSx, identifier string, newSet *schema.Set, oldSet *schema.Set) error { + // oldAliasValues := expandFsxAliasValues(oldAliases) + // newAliasValues := expandFsxAliasValues(newAliases) - var removedAliases []*string + // var removedAliases []*string - for _, oldAliasValue := range oldAliasValues { - exists := false + // for _, oldAliasValue := range oldAliasValues { + // exists := false - for _, newAliasValue := range newAliasValues { - if newAliasValue == oldAliasValue { - exists = true - break - } - } + // for _, newAliasValue := range newAliasValues { + // if newAliasValue == oldAliasValue { + // exists = true + // break + // } + // } - if !exists { - removedAliases = append(removedAliases, oldAliasValue) - } - } + // if !exists { + // removedAliases = append(removedAliases, oldAliasValue) + // } + // } - if len(removedAliases) > 0 { - input := &fsx.DisassociateFileSystemAliasesInput{ - FileSystemId: aws.String(identifier), - Aliases: removedAliases, - } + if newSet.Len() > 0 { + if newAliases := newSet.Difference(oldSet); newAliases.Len() > 0 { - _, err := conn.DisassociateFileSystemAliases(input) + input := &fsx.AssociateFileSystemAliasesInput{ + FileSystemId: aws.String(identifier), + Aliases: expandStringSet(newAliases), + } - if err != nil { - return fmt.Errorf("error disassociating aliases from FSx file system (%s): %w", identifier, err) + _, err := conn.AssociateFileSystemAliases(input) + + if err != nil { + return fmt.Errorf("error associating aliases to FSx file system (%s): %w", identifier, err) + } + + for _, alias := range newAliases.List() { + if err := waitForFsxWindowsFileSystemAliasAvailable(conn, identifier, alias.(string)); err != nil { + return fmt.Errorf("Error waiting for FSX Windows filesystem alias (%s) to be available: %w", identifier, err) + } + } } } - var addedAliases []*string + if oldSet.Len() > 0 { + if oldAliases := oldSet.Difference(newSet); oldAliases.Len() > 0 { + input := &fsx.DisassociateFileSystemAliasesInput{ + FileSystemId: aws.String(identifier), + Aliases: expandStringSet(oldAliases), + } - for _, newAliasValue := range newAliasValues { - exists := false + _, err := conn.DisassociateFileSystemAliases(input) - for _, oldAliasValue := range oldAliasValues { - if oldAliasValue == newAliasValue { - exists = true - break + if err != nil { + return fmt.Errorf("error disassociating aliases from FSx file system (%s): %w", identifier, err) } - } - if !exists { - addedAliases = append(addedAliases, newAliasValue) + for _, alias := range oldAliases.List() { + if err := waitForFsxWindowsFileSystemAliasDeleted(conn, identifier, alias.(string)); err != nil { + return fmt.Errorf("Error waiting for FSX Windows filesystem alias (%s) to delete: %w", identifier, err) + } + } } } - if len(addedAliases) > 0 { - input := &fsx.AssociateFileSystemAliasesInput{ - FileSystemId: aws.String(identifier), - Aliases: addedAliases, - } + // var addedAliases []*string - _, err := conn.AssociateFileSystemAliases(input) + // for _, newAliasValue := range newAliasValues { + // exists := false - if err != nil { - return fmt.Errorf("error associating aliases to FSx file system (%s): %w", identifier, err) - } - } + // for _, oldAliasValue := range oldAliasValues { + // if oldAliasValue == newAliasValue { + // exists = true + // break + // } + // } + + // if !exists { + // addedAliases = append(addedAliases, newAliasValue) + // } + // } return nil } diff --git a/aws/resource_aws_fsx_windows_file_system_test.go b/aws/resource_aws_fsx_windows_file_system_test.go index 9feae003698..44493c61138 100644 --- a/aws/resource_aws_fsx_windows_file_system_test.go +++ b/aws/resource_aws_fsx_windows_file_system_test.go @@ -276,7 +276,7 @@ func TestAccAWSFsxWindowsFileSystem_disappears(t *testing.T) { }) } -func TestAccAWSFsxWindowsFileSystem_Aliases(t *testing.T) { +func TestAccAWSFsxWindowsFileSystem_aliases(t *testing.T) { var filesystem1, filesystem2, filesystem3 fsx.FileSystem resourceName := "aws_fsx_windows_file_system.test" @@ -286,11 +286,11 @@ func TestAccAWSFsxWindowsFileSystem_Aliases(t *testing.T) { CheckDestroy: testAccCheckFsxWindowsFileSystemDestroy, Steps: []resource.TestStep{ { - Config: testAccAwsFsxWindowsFileSystemConfigAliases1("filesystem1.domain.name.com"), + Config: testAccAwsFsxWindowsFileSystemConfigAliases1("filesystem1.example.com"), Check: resource.ComposeTestCheckFunc( testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem1), resource.TestCheckResourceAttr(resourceName, "aliases.#", "1"), - resource.TestCheckResourceAttr(resourceName, "aliases.0", "filesystem1.domain.name.com"), + resource.TestCheckResourceAttr(resourceName, "aliases.0", "filesystem1.example.com"), ), }, { @@ -303,22 +303,22 @@ func TestAccAWSFsxWindowsFileSystem_Aliases(t *testing.T) { }, }, { - Config: testAccAwsFsxWindowsFileSystemConfigAliases2("filesystem1.domain.name.com", "filesystem2.domain.name.com"), + Config: testAccAwsFsxWindowsFileSystemConfigAliases2("filesystem2.example.com", "filesystem3.example.com"), Check: resource.ComposeTestCheckFunc( testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem2), testAccCheckFsxWindowsFileSystemNotRecreated(&filesystem1, &filesystem2), resource.TestCheckResourceAttr(resourceName, "aliases.#", "2"), - resource.TestCheckResourceAttr(resourceName, "aliases.0", "filesystem1.domain.name.com"), - resource.TestCheckResourceAttr(resourceName, "aliases.1", "filesystem2.domain.name.com"), + resource.TestCheckResourceAttr(resourceName, "aliases.0", "filesystem2.example.com"), + resource.TestCheckResourceAttr(resourceName, "aliases.1", "filesystem3.example.com"), ), }, { - Config: testAccAwsFsxWindowsFileSystemConfigAliases1("filesystem2.domain.name.com"), + Config: testAccAwsFsxWindowsFileSystemConfigAliases1("filesystem3.example.com"), Check: resource.ComposeTestCheckFunc( testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem3), testAccCheckFsxWindowsFileSystemNotRecreated(&filesystem2, &filesystem3), resource.TestCheckResourceAttr(resourceName, "aliases.#", "1"), - resource.TestCheckResourceAttr(resourceName, "aliases.0", "filesystem2.domain.name.com"), + resource.TestCheckResourceAttr(resourceName, "aliases.0", "filesystem3.example.com"), ), }, }, @@ -914,12 +914,12 @@ resource "aws_directory_service_directory" "test" { } func testAccAwsFsxWindowsFileSystemConfigAliases1(alias1 string) string { - return testAccAwsFsxWindowsFileSystemConfigBase() + fmt.Sprintf(` + return fmt.Sprintf(` resource "aws_fsx_windows_file_system" "test" { - active_directory_id = aws_directory_service_directory.test.id + active_directory_id = "d-92677185a7" skip_final_backup = true storage_capacity = 32 - subnet_ids = [aws_subnet.test1.id] + subnet_ids = ["subnet-060ab259c291fa28c"] throughput_capacity = 8 aliases = [ @@ -930,16 +930,16 @@ resource "aws_fsx_windows_file_system" "test" { } func testAccAwsFsxWindowsFileSystemConfigAliases2(alias1, alias2 string) string { - return testAccAwsFsxWindowsFileSystemConfigBase() + fmt.Sprintf(` + return fmt.Sprintf(` resource "aws_fsx_windows_file_system" "test" { - active_directory_id = aws_directory_service_directory.test.id + active_directory_id = "d-92677185a7" skip_final_backup = true storage_capacity = 32 - subnet_ids = [aws_subnet.test1.id] + subnet_ids = ["subnet-060ab259c291fa28c"] throughput_capacity = 8 aliases = [ - %[1]q + %[1]q, %[2]q ] } From 15fe05ff273c21d5ce4caa8f52c1391c283473f6 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Fri, 2 Jul 2021 13:43:30 +0300 Subject: [PATCH 115/398] changelog --- .changelog/20054.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/20054.txt diff --git a/.changelog/20054.txt b/.changelog/20054.txt new file mode 100644 index 00000000000..17613fbece0 --- /dev/null +++ b/.changelog/20054.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/fsx_windows_file_system: Add `aliases` argument. +``` From b19fd347424ccb3db1a6a67630e7bc1172f1643e Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Fri, 2 Jul 2021 13:46:16 +0300 Subject: [PATCH 116/398] docs --- website/docs/r/fsx_windows_file_system.html.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/website/docs/r/fsx_windows_file_system.html.markdown b/website/docs/r/fsx_windows_file_system.html.markdown index 4378b897041..8498466b874 100644 --- a/website/docs/r/fsx_windows_file_system.html.markdown +++ b/website/docs/r/fsx_windows_file_system.html.markdown @@ -56,6 +56,7 @@ The following arguments are supported: * `subnet_ids` - (Required) A list of IDs for the subnets that the file system will be accessible from. To specify more than a single subnet set `deployment_type` to `MULTI_AZ_1`. * `throughput_capacity` - (Required) Throughput (megabytes per second) of the file system in power of 2 increments. Minimum of `8` and maximum of `2048`. * `active_directory_id` - (Optional) The ID for an existing Microsoft Active Directory instance that the file system should join when it's created. Cannot be specified with `self_managed_active_directory`. +* `aliases` - (Optional) An array DNS alias names that you want to associate with the Amazon FSx file system. For more information, see [Working with DNS Aliases](https://docs.aws.amazon.com/fsx/latest/WindowsGuide/managing-dns-aliases.html) * `automatic_backup_retention_days` - (Optional) The number of days to retain automatic backups. Minimum of `0` and maximum of `90`. Defaults to `7`. Set to `0` to disable. * `copy_tags_to_backups` - (Optional) A boolean flag indicating whether tags on the file system should be copied to backups. Defaults to `false`. * `daily_automatic_backup_start_time` - (Optional) The preferred time (in `HH:MM` format) to take daily automatic backups, in the UTC time zone. From 321819037b8db314d4dae84e4ee4c5d6b101cd64 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Fri, 2 Jul 2021 13:47:56 +0300 Subject: [PATCH 117/398] fmt --- aws/resource_aws_fsx_windows_file_system_test.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/aws/resource_aws_fsx_windows_file_system_test.go b/aws/resource_aws_fsx_windows_file_system_test.go index 44493c61138..87f3b1fac64 100644 --- a/aws/resource_aws_fsx_windows_file_system_test.go +++ b/aws/resource_aws_fsx_windows_file_system_test.go @@ -922,9 +922,7 @@ resource "aws_fsx_windows_file_system" "test" { subnet_ids = ["subnet-060ab259c291fa28c"] throughput_capacity = 8 - aliases = [ - %[1]q - ] + aliases = [%[1]q] } `, alias1) } @@ -938,10 +936,7 @@ resource "aws_fsx_windows_file_system" "test" { subnet_ids = ["subnet-060ab259c291fa28c"] throughput_capacity = 8 - aliases = [ - %[1]q, - %[2]q - ] + aliases = [%[1]q, %[2]q] } `, alias1, alias2) } From d3ecd65b58533856e88f2b222184ffed762af20c Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Fri, 2 Jul 2021 14:08:20 +0300 Subject: [PATCH 118/398] fix diff --- aws/resource_aws_fsx_windows_file_system.go | 43 ++----------------- ...source_aws_fsx_windows_file_system_test.go | 1 + 2 files changed, 4 insertions(+), 40 deletions(-) diff --git a/aws/resource_aws_fsx_windows_file_system.go b/aws/resource_aws_fsx_windows_file_system.go index ced17dc4c90..eb7eda3011f 100644 --- a/aws/resource_aws_fsx_windows_file_system.go +++ b/aws/resource_aws_fsx_windows_file_system.go @@ -532,27 +532,7 @@ func expandFsxAliasValues(aliases []*fsx.Alias) []*string { return alternateDNSNames } -func updateFsxAliases(conn *fsx.FSx, identifier string, newSet *schema.Set, oldSet *schema.Set) error { - // oldAliasValues := expandFsxAliasValues(oldAliases) - // newAliasValues := expandFsxAliasValues(newAliases) - - // var removedAliases []*string - - // for _, oldAliasValue := range oldAliasValues { - // exists := false - - // for _, newAliasValue := range newAliasValues { - // if newAliasValue == oldAliasValue { - // exists = true - // break - // } - // } - - // if !exists { - // removedAliases = append(removedAliases, oldAliasValue) - // } - // } - +func updateFsxAliases(conn *fsx.FSx, identifier string, oldSet *schema.Set, newSet *schema.Set) error { if newSet.Len() > 0 { if newAliases := newSet.Difference(oldSet); newAliases.Len() > 0 { @@ -569,7 +549,7 @@ func updateFsxAliases(conn *fsx.FSx, identifier string, newSet *schema.Set, oldS for _, alias := range newAliases.List() { if err := waitForFsxWindowsFileSystemAliasAvailable(conn, identifier, alias.(string)); err != nil { - return fmt.Errorf("Error waiting for FSX Windows filesystem alias (%s) to be available: %w", identifier, err) + return fmt.Errorf("Error waiting for FSX Windows filesystem (%s) alias (%s) to be available: %w", identifier, alias.(string), err) } } } @@ -590,29 +570,12 @@ func updateFsxAliases(conn *fsx.FSx, identifier string, newSet *schema.Set, oldS for _, alias := range oldAliases.List() { if err := waitForFsxWindowsFileSystemAliasDeleted(conn, identifier, alias.(string)); err != nil { - return fmt.Errorf("Error waiting for FSX Windows filesystem alias (%s) to delete: %w", identifier, err) + return fmt.Errorf("Error waiting for FSX Windows filesystem (%s) alias (%s) to delete: %w", identifier, alias.(string), err) } } } } - // var addedAliases []*string - - // for _, newAliasValue := range newAliasValues { - // exists := false - - // for _, oldAliasValue := range oldAliasValues { - // if oldAliasValue == newAliasValue { - // exists = true - // break - // } - // } - - // if !exists { - // addedAliases = append(addedAliases, newAliasValue) - // } - // } - return nil } diff --git a/aws/resource_aws_fsx_windows_file_system_test.go b/aws/resource_aws_fsx_windows_file_system_test.go index 87f3b1fac64..99e8bbd9863 100644 --- a/aws/resource_aws_fsx_windows_file_system_test.go +++ b/aws/resource_aws_fsx_windows_file_system_test.go @@ -282,6 +282,7 @@ func TestAccAWSFsxWindowsFileSystem_aliases(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(fsx.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, fsx.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckFsxWindowsFileSystemDestroy, Steps: []resource.TestStep{ From 1d20f54c376f026aa7f44661790ec06807ea5d98 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Fri, 2 Jul 2021 15:16:34 +0300 Subject: [PATCH 119/398] use admin action waiter --- aws/fsx.go | 80 ++------------------- aws/resource_aws_fsx_windows_file_system.go | 18 ++--- 2 files changed, 11 insertions(+), 87 deletions(-) diff --git a/aws/fsx.go b/aws/fsx.go index faf6dcce2e8..7aa5d989698 100644 --- a/aws/fsx.go +++ b/aws/fsx.go @@ -8,11 +8,6 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) -const ( - fsxWindowsFileSystemAliasAvailable = 10 * time.Minute - fsxWindowsFileSystemAliasDeleted = 10 * time.Minute -) - func describeFsxFileSystem(conn *fsx.FSx, id string) (*fsx.FileSystem, error) { input := &fsx.DescribeFileSystemsInput{ FileSystemIds: []*string{aws.String(id)}, @@ -53,46 +48,7 @@ func refreshFsxFileSystemLifecycle(conn *fsx.FSx, id string) resource.StateRefre } } -func refreshFsxWindowsFileSystemAliasLifecycle(conn *fsx.FSx, id, aliasName string) resource.StateRefreshFunc { - return func() (interface{}, string, error) { - filesystem, err := describeFsxFileSystem(conn, id) - - if isAWSErr(err, fsx.ErrCodeFileSystemNotFound, "") { - return nil, "", nil - } - - if err != nil { - return nil, "", err - } - - if filesystem == nil { - return nil, "", nil - } - - if filesystem.WindowsConfiguration == nil { - return nil, "", nil - } - - aliases := filesystem.WindowsConfiguration.Aliases - if aliases == nil { - return nil, "", nil - } - - for _, alias := range aliases { - if alias == nil { - continue - } - - if aws.StringValue(alias.Name) == aliasName { - return filesystem, aws.StringValue(alias.Lifecycle), nil - } - } - - return filesystem, "", nil - } -} - -func refreshFsxFileSystemAdministrativeActionsStatusFileSystemUpdate(conn *fsx.FSx, id string) resource.StateRefreshFunc { +func refreshFsxFileSystemAdministrativeActionsStatusFileSystemUpdate(conn *fsx.FSx, id, action string) resource.StateRefreshFunc { return func() (interface{}, string, error) { filesystem, err := describeFsxFileSystem(conn, id) @@ -113,7 +69,7 @@ func refreshFsxFileSystemAdministrativeActionsStatusFileSystemUpdate(conn *fsx.F continue } - if aws.StringValue(administrativeAction.AdministrativeActionType) == fsx.AdministrativeActionTypeFileSystemUpdate { + if aws.StringValue(administrativeAction.AdministrativeActionType) == action { return filesystem, aws.StringValue(administrativeAction.Status), nil } } @@ -122,34 +78,6 @@ func refreshFsxFileSystemAdministrativeActionsStatusFileSystemUpdate(conn *fsx.F } } -func waitForFsxWindowsFileSystemAliasAvailable(conn *fsx.FSx, id, alias string) error { - stateConf := &resource.StateChangeConf{ - Pending: []string{fsx.AliasLifecycleCreating}, - Target: []string{fsx.AliasLifecycleAvailable}, - Refresh: refreshFsxWindowsFileSystemAliasLifecycle(conn, id, alias), - Timeout: fsxWindowsFileSystemAliasAvailable, - Delay: 30 * time.Second, - } - - _, err := stateConf.WaitForState() - - return err -} - -func waitForFsxWindowsFileSystemAliasDeleted(conn *fsx.FSx, id, alias string) error { - stateConf := &resource.StateChangeConf{ - Pending: []string{fsx.AliasLifecycleAvailable, fsx.AliasLifecycleDeleting}, - Target: []string{""}, - Refresh: refreshFsxWindowsFileSystemAliasLifecycle(conn, id, alias), - Timeout: fsxWindowsFileSystemAliasDeleted, - Delay: 30 * time.Second, - } - - _, err := stateConf.WaitForState() - - return err -} - func waitForFsxFileSystemCreation(conn *fsx.FSx, id string, timeout time.Duration) error { stateConf := &resource.StateChangeConf{ Pending: []string{fsx.FileSystemLifecycleCreating}, @@ -192,7 +120,7 @@ func waitForFsxFileSystemUpdate(conn *fsx.FSx, id string, timeout time.Duration) return err } -func waitForFsxFileSystemUpdateAdministrativeActionsStatusFileSystemUpdate(conn *fsx.FSx, id string, timeout time.Duration) error { +func waitForFsxFileSystemUpdateAdministrativeActionsStatusFileSystemUpdate(conn *fsx.FSx, id, action string, timeout time.Duration) error { stateConf := &resource.StateChangeConf{ Pending: []string{ fsx.StatusInProgress, @@ -202,7 +130,7 @@ func waitForFsxFileSystemUpdateAdministrativeActionsStatusFileSystemUpdate(conn fsx.StatusCompleted, fsx.StatusUpdatedOptimizing, }, - Refresh: refreshFsxFileSystemAdministrativeActionsStatusFileSystemUpdate(conn, id), + Refresh: refreshFsxFileSystemAdministrativeActionsStatusFileSystemUpdate(conn, id, action), Timeout: timeout, Delay: 30 * time.Second, } diff --git a/aws/resource_aws_fsx_windows_file_system.go b/aws/resource_aws_fsx_windows_file_system.go index eb7eda3011f..fb9df280df4 100644 --- a/aws/resource_aws_fsx_windows_file_system.go +++ b/aws/resource_aws_fsx_windows_file_system.go @@ -351,7 +351,7 @@ func resourceAwsFsxWindowsFileSystemUpdate(d *schema.ResourceData, meta interfac if d.HasChange("aliases") { o, n := d.GetChange("aliases") - if err := updateFsxAliases(conn, d.Id(), o.(*schema.Set), n.(*schema.Set)); err != nil { + if err := updateFsxAliases(conn, d.Id(), o.(*schema.Set), n.(*schema.Set), d.Timeout(schema.TimeoutUpdate)); err != nil { return fmt.Errorf("error updating FSx Windows File System (%s) aliases: %w", d.Id(), err) } } @@ -397,7 +397,7 @@ func resourceAwsFsxWindowsFileSystemUpdate(d *schema.ResourceData, meta interfac return fmt.Errorf("error updating FSx Windows File System (%s): %w", d.Id(), err) } - if err := waitForFsxFileSystemUpdateAdministrativeActionsStatusFileSystemUpdate(conn, d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil { + if err := waitForFsxFileSystemUpdateAdministrativeActionsStatusFileSystemUpdate(conn, d.Id(), fsx.AdministrativeActionTypeFileSystemUpdate, d.Timeout(schema.TimeoutUpdate)); err != nil { return fmt.Errorf("error waiting for FSx Windows File System (%s) update: %w", d.Id(), err) } } @@ -532,7 +532,7 @@ func expandFsxAliasValues(aliases []*fsx.Alias) []*string { return alternateDNSNames } -func updateFsxAliases(conn *fsx.FSx, identifier string, oldSet *schema.Set, newSet *schema.Set) error { +func updateFsxAliases(conn *fsx.FSx, identifier string, oldSet *schema.Set, newSet *schema.Set, timeout time.Duration) error { if newSet.Len() > 0 { if newAliases := newSet.Difference(oldSet); newAliases.Len() > 0 { @@ -547,10 +547,8 @@ func updateFsxAliases(conn *fsx.FSx, identifier string, oldSet *schema.Set, newS return fmt.Errorf("error associating aliases to FSx file system (%s): %w", identifier, err) } - for _, alias := range newAliases.List() { - if err := waitForFsxWindowsFileSystemAliasAvailable(conn, identifier, alias.(string)); err != nil { - return fmt.Errorf("Error waiting for FSX Windows filesystem (%s) alias (%s) to be available: %w", identifier, alias.(string), err) - } + if err := waitForFsxFileSystemUpdateAdministrativeActionsStatusFileSystemUpdate(conn, identifier, fsx.AdministrativeActionTypeFileSystemAliasAssociation, timeout); err != nil { + return fmt.Errorf("Error waiting for FSX Windows filesystem (%s) alias to be associated: %w", identifier, err) } } } @@ -568,10 +566,8 @@ func updateFsxAliases(conn *fsx.FSx, identifier string, oldSet *schema.Set, newS return fmt.Errorf("error disassociating aliases from FSx file system (%s): %w", identifier, err) } - for _, alias := range oldAliases.List() { - if err := waitForFsxWindowsFileSystemAliasDeleted(conn, identifier, alias.(string)); err != nil { - return fmt.Errorf("Error waiting for FSX Windows filesystem (%s) alias (%s) to delete: %w", identifier, alias.(string), err) - } + if err := waitForFsxFileSystemUpdateAdministrativeActionsStatusFileSystemUpdate(conn, identifier, fsx.AdministrativeActionTypeFileSystemAliasDisassociation, timeout); err != nil { + return fmt.Errorf("Error waiting for FSX Windows filesystem (%s) alias to disassociated: %w", identifier, err) } } } From 8ff6dfe816cf526d722af6668bd825e03a66d1b5 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Fri, 2 Jul 2021 15:17:58 +0300 Subject: [PATCH 120/398] revert local changes --- aws/resource_aws_fsx_windows_file_system_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/aws/resource_aws_fsx_windows_file_system_test.go b/aws/resource_aws_fsx_windows_file_system_test.go index 99e8bbd9863..a6d83d327a7 100644 --- a/aws/resource_aws_fsx_windows_file_system_test.go +++ b/aws/resource_aws_fsx_windows_file_system_test.go @@ -915,12 +915,12 @@ resource "aws_directory_service_directory" "test" { } func testAccAwsFsxWindowsFileSystemConfigAliases1(alias1 string) string { - return fmt.Sprintf(` + return testAccAwsFsxWindowsFileSystemConfigBase() + fmt.Sprintf(` resource "aws_fsx_windows_file_system" "test" { - active_directory_id = "d-92677185a7" + active_directory_id = aws_directory_service_directory.test.id skip_final_backup = true storage_capacity = 32 - subnet_ids = ["subnet-060ab259c291fa28c"] + subnet_ids = [aws_subnet.test1.id] throughput_capacity = 8 aliases = [%[1]q] @@ -929,12 +929,12 @@ resource "aws_fsx_windows_file_system" "test" { } func testAccAwsFsxWindowsFileSystemConfigAliases2(alias1, alias2 string) string { - return fmt.Sprintf(` + return testAccAwsFsxWindowsFileSystemConfigBase() + fmt.Sprintf(` resource "aws_fsx_windows_file_system" "test" { - active_directory_id = "d-92677185a7" + active_directory_id = aws_directory_service_directory.test.id skip_final_backup = true storage_capacity = 32 - subnet_ids = ["subnet-060ab259c291fa28c"] + subnet_ids = [aws_subnet.test1.id] throughput_capacity = 8 aliases = [%[1]q, %[2]q] From e8dc87fa890058af2adbad3267f3caf25f3eafe1 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Fri, 2 Jul 2021 15:25:46 +0300 Subject: [PATCH 121/398] fmt --- aws/resource_aws_fsx_windows_file_system_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_fsx_windows_file_system_test.go b/aws/resource_aws_fsx_windows_file_system_test.go index a6d83d327a7..f64f2852c64 100644 --- a/aws/resource_aws_fsx_windows_file_system_test.go +++ b/aws/resource_aws_fsx_windows_file_system_test.go @@ -917,7 +917,7 @@ resource "aws_directory_service_directory" "test" { func testAccAwsFsxWindowsFileSystemConfigAliases1(alias1 string) string { return testAccAwsFsxWindowsFileSystemConfigBase() + fmt.Sprintf(` resource "aws_fsx_windows_file_system" "test" { - active_directory_id = aws_directory_service_directory.test.id + active_directory_id = aws_directory_service_directory.test.id skip_final_backup = true storage_capacity = 32 subnet_ids = [aws_subnet.test1.id] From 725e40df6a2cbeeaf822f5398057acdc946a105a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Jul 2021 08:53:14 -0400 Subject: [PATCH 122/398] r/aws_transfer_server: Correct update to 0 subnet_ids. --- aws/resource_aws_transfer_server.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/aws/resource_aws_transfer_server.go b/aws/resource_aws_transfer_server.go index 162533614f9..201c2cf3fbb 100644 --- a/aws/resource_aws_transfer_server.go +++ b/aws/resource_aws_transfer_server.go @@ -418,6 +418,11 @@ func resourceAwsTransferServerUpdate(d *schema.ResourceData, meta interface{}) e // Prevent the following error: InvalidRequestException: Changing Security Group is not supported input.EndpointDetails.SecurityGroupIds = nil + + // Update to 0 SubnetIds. + if input.EndpointDetails.SubnetIds == nil { + input.EndpointDetails.SubnetIds = []*string{} + } } } From 549d814f47357d45b49f90183b80ce908308a18f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Jul 2021 08:54:00 -0400 Subject: [PATCH 123/398] r/aws_transfer_server: Cannot update to 0 security_group_ids (VPC Endpoint always requires at least 1). --- aws/resource_aws_transfer_server_test.go | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/aws/resource_aws_transfer_server_test.go b/aws/resource_aws_transfer_server_test.go index be2668959b3..1313d31210b 100644 --- a/aws/resource_aws_transfer_server_test.go +++ b/aws/resource_aws_transfer_server_test.go @@ -414,20 +414,6 @@ func testAccAWSTransferServer_vpcSecurityGroupIds(t *testing.T) { resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), ), }, - { - Config: testAccAWSTransferServerVpcConfig(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSTransferServerExists(resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), - resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", defaultSecurityGroupResourceName, "id"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), - resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), - resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), - ), - }, }, }) } From fe8cda6d26fd9d73e6d98b7386f4bf708385e316 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Jul 2021 09:40:05 -0400 Subject: [PATCH 124/398] r/aws_transfer_server: When updating endpoint_type to VPC, wait for newly provisioned VPC Endpoint to become available. --- aws/resource_aws_transfer_server.go | 41 ++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/aws/resource_aws_transfer_server.go b/aws/resource_aws_transfer_server.go index 201c2cf3fbb..972412b495b 100644 --- a/aws/resource_aws_transfer_server.go +++ b/aws/resource_aws_transfer_server.go @@ -376,6 +376,18 @@ func resourceAwsTransferServerUpdate(d *schema.ResourceData, meta interface{}) e conn := meta.(*AWSClient).transferconn if d.HasChangesExcept("tags", "tags_all") { + var newEndpointTypeVpc bool + var oldEndpointTypeVpc bool + + old, new := d.GetChange("endpoint_type") + + if old, new := old.(string), new.(string); new != old && new == transfer.EndpointTypeVpc { + newEndpointTypeVpc = true + } else if new == old && new == transfer.EndpointTypeVpc { + newEndpointTypeVpc = true + oldEndpointTypeVpc = true + } + var addressAllocationIDs []*string var offlineUpdate bool @@ -388,21 +400,9 @@ func resourceAwsTransferServerUpdate(d *schema.ResourceData, meta interface{}) e } if d.HasChange("endpoint_details") { - var newEndpointTypeVpc bool - var oldEndpointTypeVpc bool - if v, ok := d.GetOk("endpoint_details"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { input.EndpointDetails = expandTransferEndpointDetails(v.([]interface{})[0].(map[string]interface{})) - old, new := d.GetChange("endpoint_type") - - if old, new := old.(string), new.(string); new != old && new == transfer.EndpointTypeVpc { - newEndpointTypeVpc = true - } else if new == old && new == transfer.EndpointTypeVpc { - newEndpointTypeVpc = true - oldEndpointTypeVpc = true - } - if newEndpointTypeVpc && !oldEndpointTypeVpc { // Prevent the following error: InvalidRequestException: Cannot specify AddressAllocationids when updating server to EndpointType: VPC addressAllocationIDs = input.EndpointDetails.AddressAllocationIds @@ -509,6 +509,23 @@ func resourceAwsTransferServerUpdate(d *schema.ResourceData, meta interface{}) e return err } + if newEndpointTypeVpc && !oldEndpointTypeVpc { + // Wait for newly provisioned VPC Endpoint to become available. + output, err := finder.ServerByID(conn, d.Id()) + + if err != nil { + return fmt.Errorf("error reading Transfer Server (%s): %w", d.Id(), err) + } + + vpcEndpointID := aws.StringValue(output.EndpointDetails.VpcEndpointId) + + _, err = ec2waiter.VpcEndpointAvailable(meta.(*AWSClient).ec2conn, vpcEndpointID, d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return fmt.Errorf("error waiting for Transfer Server (%s) VPC Endpoint (%s) to become available: %w", d.Id(), vpcEndpointID, err) + } + } + // Set any AddressAllocationIds if the server has updated endpoint type to VPC. if len(addressAllocationIDs) > 0 { input := &transfer.UpdateServerInput{ From 7c0c8d049da5ea8b6071bbf53b2be1e1b07ccb08 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Jul 2021 10:12:16 -0400 Subject: [PATCH 125/398] Fix rebase merge conflicts. --- .github/workflows/release.yml | 54 ----------------------------------- .goreleaser.yml | 2 +- 2 files changed, 1 insertion(+), 55 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5ed1b05a502..90969c0596b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,3 @@ -<<<<<<< HEAD name: Post Publish on: release: @@ -53,56 +52,3 @@ jobs: git add CHANGELOG.md git commit -m "Update CHANGELOG.md after ${{ github.event.release.tag_name }}" git push -======= -# This GitHub action can publish assets for release when a tag is created. -# Currently its setup to run on any tag that matches the pattern "v*" (ie. v0.1.0). -# -# This uses an action (paultyng/ghaction-import-gpg) that assumes you set your -# private key in the `GPG_PRIVATE_KEY` secret and passphrase in the `PASSPHRASE` -# secret. If you would rather own your own GPG handling, please fork this action -# or use an alternative one for key handling. -# -# You will need to pass the `--batch` flag to `gpg` in your signing step -# in `goreleaser` to indicate this is being used in a non-interactive mode. -# -name: release -on: - push: - tags: - - 'v*' -jobs: - goreleaser: - runs-on: ubuntu-latest - steps: - - - name: Checkout - uses: actions/checkout@v2 - - - name: Unshallow - run: git fetch --prune --unshallow - - - name: Set up Go - uses: actions/setup-go@v2 - with: - go-version: 1.14 - - - name: Import GPG key - id: import_gpg - # TODO: move this to HashiCorp namespace or find alternative that is just simple gpg commands - # see https://github.com/hashicorp/terraform-provider-scaffolding/issues/22 - uses: paultyng/ghaction-import-gpg@v2.1.0 - env: - # These secrets will need to be configured for the repository: - GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} - PASSPHRASE: ${{ secrets.PASSPHRASE }} - - - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v2 - with: - version: latest - args: release --rm-dist - env: - GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} - # GitHub sets this automatically - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ->>>>>>> 19bf7eb9c (Change) diff --git a/.goreleaser.yml b/.goreleaser.yml index 8d1a78155dd..fe1eb97fe8c 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -24,7 +24,7 @@ builds: - goarch: '386' goos: darwin ldflags: - - -s -w -X aws/version.ProviderVersion={{.Version}} + - -s -w -X version.ProviderVersion={{.Version}} mod_timestamp: '{{ .CommitTimestamp }}' changelog: skip: true From baffc397d24e7e132930046350a8b0548da685ae Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Jul 2021 10:19:19 -0400 Subject: [PATCH 126/398] Call out use of EC2 DescribeVpcEndpoints/ModifyVpcEndpoint actions. --- website/docs/r/transfer_server.html.markdown | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/website/docs/r/transfer_server.html.markdown b/website/docs/r/transfer_server.html.markdown index 257f4703b11..2b754ab8544 100644 --- a/website/docs/r/transfer_server.html.markdown +++ b/website/docs/r/transfer_server.html.markdown @@ -10,6 +10,8 @@ description: |- Provides a AWS Transfer Server resource. +~> **NOTE on AWS IAM permissions:** If the `endpoint_type` is set to `VPC`, the `ec2:DescribeVpcEndpoints` and `ec2:ModifyVpcEndpoint` [actions](https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazonec2.html#amazonec2-actions-as-permissions) are used. + ## Example Usage ### Basic @@ -124,7 +126,7 @@ In addition to all arguments above, the following attributes are exported: Transfer Servers can be imported using the `server id`, e.g. ``` -$ terraform import aws_transfer_server.bar s-12345678 +$ terraform import aws_transfer_server.example s-12345678 ``` Certain resource arguments, such as `host_key`, cannot be read via the API and imported into Terraform. Terraform will display a difference for these arguments the first run after import if declared in the Terraform configuration for an imported resource. From d6a8e5383b6d376448cba49cbc6bd2faeee750e2 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Jul 2021 10:34:07 -0400 Subject: [PATCH 127/398] r/aws_transfer_server: Handle 'InvalidRequestException: AddressAllocationIds must be removed before SubnetIds can be modified'. --- aws/resource_aws_transfer_server.go | 37 +++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_transfer_server.go b/aws/resource_aws_transfer_server.go index 972412b495b..c04b235b31b 100644 --- a/aws/resource_aws_transfer_server.go +++ b/aws/resource_aws_transfer_server.go @@ -390,6 +390,7 @@ func resourceAwsTransferServerUpdate(d *schema.ResourceData, meta interface{}) e var addressAllocationIDs []*string var offlineUpdate bool + var removeAddressAllocationIDs bool input := &transfer.UpdateServerInput{ ServerId: aws.String(d.Id()), @@ -416,6 +417,24 @@ func resourceAwsTransferServerUpdate(d *schema.ResourceData, meta interface{}) e offlineUpdate = true } + // Update to 0 AddressAllocationIds. + if input.EndpointDetails.AddressAllocationIds == nil { + input.EndpointDetails.AddressAllocationIds = []*string{} + } + + // Prevent the following error: InvalidRequestException: AddressAllocationIds must be removed before SubnetIds can be modified + if d.HasChange("endpoint_details.0.subnet_ids") { + old, _ := d.GetChange("endpoint_details.0.address_allocation_ids") + + if old := old.(*schema.Set); old.Len() > 0 { + offlineUpdate = true + removeAddressAllocationIDs = true + + addressAllocationIDs = input.EndpointDetails.AddressAllocationIds + input.EndpointDetails.AddressAllocationIds = nil + } + } + // Prevent the following error: InvalidRequestException: Changing Security Group is not supported input.EndpointDetails.SecurityGroupIds = nil @@ -451,7 +470,7 @@ func resourceAwsTransferServerUpdate(d *schema.ResourceData, meta interface{}) e return fmt.Errorf("error updating Transfer Server (%s) VPC Endpoint (%s): %w", d.Id(), vpcEndpointID, err) } - _, err := ec2waiter.VpcEndpointAvailable(conn, vpcEndpointID, d.Timeout(schema.TimeoutUpdate)) + _, err := ec2waiter.VpcEndpointAvailable(conn, vpcEndpointID, Ec2VpcEndpointCreationTimeout) if err != nil { return fmt.Errorf("error waiting for Transfer Server (%s) VPC Endpoint (%s) to become available: %w", d.Id(), vpcEndpointID, err) @@ -504,6 +523,20 @@ func resourceAwsTransferServerUpdate(d *schema.ResourceData, meta interface{}) e } } + if removeAddressAllocationIDs { + input := &transfer.UpdateServerInput{ + ServerId: aws.String(d.Id()), + EndpointDetails: &transfer.EndpointDetails{ + AddressAllocationIds: []*string{}, + }, + } + + log.Printf("[DEBUG] Removing Transfer Server Address Allocation IDs: %s", input) + if err := updateTransferServer(conn, input); err != nil { + return err + } + } + log.Printf("[DEBUG] Updating Transfer Server: %s", input) if err := updateTransferServer(conn, input); err != nil { return err @@ -526,7 +559,6 @@ func resourceAwsTransferServerUpdate(d *schema.ResourceData, meta interface{}) e } } - // Set any AddressAllocationIds if the server has updated endpoint type to VPC. if len(addressAllocationIDs) > 0 { input := &transfer.UpdateServerInput{ ServerId: aws.String(d.Id()), @@ -535,6 +567,7 @@ func resourceAwsTransferServerUpdate(d *schema.ResourceData, meta interface{}) e }, } + log.Printf("[DEBUG] Adding Transfer Server Address Allocation IDs: %s", input) if err := updateTransferServer(conn, input); err != nil { return err } From f3cf2ce769dbfe49a939730b32d2e9f45abb3dd0 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Jul 2021 12:32:56 -0400 Subject: [PATCH 128/398] r/aws_transfer_server: Simplify some acceptance tests. --- aws/resource_aws_transfer_server_test.go | 111 ++++------------------- 1 file changed, 18 insertions(+), 93 deletions(-) diff --git a/aws/resource_aws_transfer_server_test.go b/aws/resource_aws_transfer_server_test.go index 1313d31210b..c1274b96ed9 100644 --- a/aws/resource_aws_transfer_server_test.go +++ b/aws/resource_aws_transfer_server_test.go @@ -244,7 +244,6 @@ func testAccAWSTransferServer_vpc(t *testing.T) { Config: testAccAWSTransferServerVpcConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), @@ -252,6 +251,7 @@ func testAccAWSTransferServer_vpc(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), ), }, { @@ -264,7 +264,6 @@ func testAccAWSTransferServer_vpc(t *testing.T) { Config: testAccAWSTransferServerVpcUpdateConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), @@ -273,13 +272,13 @@ func testAccAWSTransferServer_vpc(t *testing.T) { resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.subnet_ids.*", subnetResourceName, "id"), resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), ), }, { Config: testAccAWSTransferServerVpcConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), @@ -287,6 +286,7 @@ func testAccAWSTransferServer_vpc(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), ), }, }, @@ -313,7 +313,6 @@ func testAccAWSTransferServer_vpcAddressAllocationIds(t *testing.T) { Config: testAccAWSTransferServerVpcAddressAllocationIdsConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.address_allocation_ids.*", eip1ResourceName, "id"), @@ -323,6 +322,7 @@ func testAccAWSTransferServer_vpcAddressAllocationIds(t *testing.T) { resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.subnet_ids.*", subnetResourceName, "id"), resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), ), }, { @@ -335,7 +335,6 @@ func testAccAWSTransferServer_vpcAddressAllocationIds(t *testing.T) { Config: testAccAWSTransferServerVpcAddressAllocationIdsUpdateConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.address_allocation_ids.*", eip2ResourceName, "id"), @@ -345,13 +344,13 @@ func testAccAWSTransferServer_vpcAddressAllocationIds(t *testing.T) { resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.subnet_ids.*", subnetResourceName, "id"), resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), ), }, { Config: testAccAWSTransferServerVpcConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), @@ -359,6 +358,7 @@ func testAccAWSTransferServer_vpcAddressAllocationIds(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), ), }, }, @@ -370,7 +370,6 @@ func testAccAWSTransferServer_vpcSecurityGroupIds(t *testing.T) { resourceName := "aws_transfer_server.test" securityGroup1ResourceName := "aws_security_group.test" securityGroup2ResourceName := "aws_security_group.test2" - defaultSecurityGroupResourceName := "aws_default_security_group.test" vpcResourceName := "aws_vpc.test" rName := acctest.RandomWithPrefix("tf-acc-test") @@ -384,7 +383,6 @@ func testAccAWSTransferServer_vpcSecurityGroupIds(t *testing.T) { Config: testAccAWSTransferServerVpcSecurityGroupIdsConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), @@ -392,6 +390,7 @@ func testAccAWSTransferServer_vpcSecurityGroupIds(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), ), }, { @@ -404,7 +403,6 @@ func testAccAWSTransferServer_vpcSecurityGroupIds(t *testing.T) { Config: testAccAWSTransferServerVpcSecurityGroupIdsUpdateConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), @@ -412,6 +410,7 @@ func testAccAWSTransferServer_vpcSecurityGroupIds(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), ), }, }, @@ -439,7 +438,6 @@ func testAccAWSTransferServer_vpcAddressAllocationIds_securityGroupIds(t *testin Config: testAccAWSTransferServerVpcAddressAllocationIdsSecurityGroupIdsConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.address_allocation_ids.*", eip1ResourceName, "id"), @@ -449,6 +447,7 @@ func testAccAWSTransferServer_vpcAddressAllocationIds_securityGroupIds(t *testin resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.subnet_ids.*", subnetResourceName, "id"), resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), ), }, { @@ -461,7 +460,6 @@ func testAccAWSTransferServer_vpcAddressAllocationIds_securityGroupIds(t *testin Config: testAccAWSTransferServerVpcAddressAllocationIdsSecurityGroupIdsUpdateConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.address_allocation_ids.*", eip2ResourceName, "id"), @@ -471,6 +469,7 @@ func testAccAWSTransferServer_vpcAddressAllocationIds_securityGroupIds(t *testin resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.subnet_ids.*", subnetResourceName, "id"), resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), ), }, }, @@ -480,8 +479,6 @@ func testAccAWSTransferServer_vpcAddressAllocationIds_securityGroupIds(t *testin func testAccAWSTransferServer_updateEndpointType_publicToVpc(t *testing.T) { var conf transfer.DescribedServer resourceName := "aws_transfer_server.test" - defaultSecurityGroupResourceName := "aws_default_security_group.test" - vpcResourceName := "aws_vpc.test" rName := acctest.RandomWithPrefix("tf-acc-test") resource.Test(t, resource.TestCase{ @@ -502,14 +499,8 @@ func testAccAWSTransferServer_updateEndpointType_publicToVpc(t *testing.T) { Config: testAccAWSTransferServerVpcConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), - resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", defaultSecurityGroupResourceName, "id"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), - resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), - resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), ), }, { @@ -525,10 +516,6 @@ func testAccAWSTransferServer_updateEndpointType_publicToVpc(t *testing.T) { func testAccAWSTransferServer_updateEndpointType_publicToVpc_addressAllocationIds(t *testing.T) { var conf transfer.DescribedServer resourceName := "aws_transfer_server.test" - eipResourceName := "aws_eip.test.0" - defaultSecurityGroupResourceName := "aws_default_security_group.test" - subnetResourceName := "aws_subnet.test" - vpcResourceName := "aws_vpc.test" rName := acctest.RandomWithPrefix("tf-acc-test") resource.Test(t, resource.TestCase{ @@ -549,16 +536,8 @@ func testAccAWSTransferServer_updateEndpointType_publicToVpc_addressAllocationId Config: testAccAWSTransferServerVpcAddressAllocationIdsConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "1"), - resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.address_allocation_ids.*", eipResourceName, "id"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), - resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", defaultSecurityGroupResourceName, "id"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "1"), - resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.subnet_ids.*", subnetResourceName, "id"), - resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), - resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), ), }, { @@ -574,9 +553,6 @@ func testAccAWSTransferServer_updateEndpointType_publicToVpc_addressAllocationId func testAccAWSTransferServer_updateEndpointType_vpcEndpointToVpc(t *testing.T) { var conf transfer.DescribedServer resourceName := "aws_transfer_server.test" - defaultSecurityGroupResourceName := "aws_default_security_group.test" - vpcEndpointResourceName := "aws_vpc_endpoint.test" - vpcResourceName := "aws_vpc.test" rName := acctest.RandomWithPrefix("tf-acc-test") resource.Test(t, resource.TestCase{ @@ -589,27 +565,16 @@ func testAccAWSTransferServer_updateEndpointType_vpcEndpointToVpc(t *testing.T) Config: testAccAWSTransferServerVpcEndpointConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC_ENDPOINT"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "0"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), - resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_endpoint_id", vpcEndpointResourceName, "id"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.vpc_id", ""), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC_ENDPOINT"), ), }, { Config: testAccAWSTransferServerVpcConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), - resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", defaultSecurityGroupResourceName, "id"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), - resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), - resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), ), }, { @@ -625,11 +590,6 @@ func testAccAWSTransferServer_updateEndpointType_vpcEndpointToVpc(t *testing.T) func testAccAWSTransferServer_updateEndpointType_vpcEndpointToVpc_addressAllocationIds(t *testing.T) { var conf transfer.DescribedServer resourceName := "aws_transfer_server.test" - eipResourceName := "aws_eip.test.0" - defaultSecurityGroupResourceName := "aws_default_security_group.test" - subnetResourceName := "aws_subnet.test" - vpcEndpointResourceName := "aws_vpc_endpoint.test" - vpcResourceName := "aws_vpc.test" rName := acctest.RandomWithPrefix("tf-acc-test") resource.Test(t, resource.TestCase{ @@ -644,11 +604,6 @@ func testAccAWSTransferServer_updateEndpointType_vpcEndpointToVpc_addressAllocat testAccCheckAWSTransferServerExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC_ENDPOINT"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "0"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), - resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_endpoint_id", vpcEndpointResourceName, "id"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.vpc_id", ""), ), }, { @@ -657,14 +612,6 @@ func testAccAWSTransferServer_updateEndpointType_vpcEndpointToVpc_addressAllocat testAccCheckAWSTransferServerExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "1"), - resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.address_allocation_ids.*", eipResourceName, "id"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), - resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", defaultSecurityGroupResourceName, "id"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "1"), - resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.subnet_ids.*", subnetResourceName, "id"), - resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), - resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), ), }, { @@ -680,9 +627,6 @@ func testAccAWSTransferServer_updateEndpointType_vpcEndpointToVpc_addressAllocat func testAccAWSTransferServer_updateEndpointType_vpcEndpointToVpc_securityGroupIds(t *testing.T) { var conf transfer.DescribedServer resourceName := "aws_transfer_server.test" - securityGroupResourceName := "aws_security_group.test" - vpcEndpointResourceName := "aws_vpc_endpoint.test" - vpcResourceName := "aws_vpc.test" rName := acctest.RandomWithPrefix("tf-acc-test") resource.Test(t, resource.TestCase{ @@ -695,27 +639,16 @@ func testAccAWSTransferServer_updateEndpointType_vpcEndpointToVpc_securityGroupI Config: testAccAWSTransferServerVpcEndpointConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC_ENDPOINT"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "0"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), - resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_endpoint_id", vpcEndpointResourceName, "id"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.vpc_id", ""), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC_ENDPOINT"), ), }, { Config: testAccAWSTransferServerVpcSecurityGroupIdsConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), - resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", securityGroupResourceName, "id"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), - resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), - resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), ), }, { @@ -731,8 +664,6 @@ func testAccAWSTransferServer_updateEndpointType_vpcEndpointToVpc_securityGroupI func testAccAWSTransferServer_updateEndpointType_vpcToPublic(t *testing.T) { var conf transfer.DescribedServer resourceName := "aws_transfer_server.test" - defaultSecurityGroupResourceName := "aws_default_security_group.test" - vpcResourceName := "aws_vpc.test" rName := acctest.RandomWithPrefix("tf-acc-test") resource.Test(t, resource.TestCase{ @@ -745,14 +676,8 @@ func testAccAWSTransferServer_updateEndpointType_vpcToPublic(t *testing.T) { Config: testAccAWSTransferServerVpcConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "1"), - resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_details.0.security_group_ids.*", defaultSecurityGroupResourceName, "id"), - resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), - resource.TestCheckResourceAttrSet(resourceName, "endpoint_details.0.vpc_endpoint_id"), - resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_id", vpcResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC"), ), }, { @@ -976,13 +901,13 @@ func testAccAWSTransferServer_vpcEndpointId(t *testing.T) { Config: testAccAWSTransferServerVpcEndpointConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSTransferServerExists(resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC_ENDPOINT"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.#", "1"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.address_allocation_ids.#", "0"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.security_group_ids.#", "0"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.subnet_ids.#", "0"), resource.TestCheckResourceAttrPair(resourceName, "endpoint_details.0.vpc_endpoint_id", vpcEndpointResourceName, "id"), resource.TestCheckResourceAttr(resourceName, "endpoint_details.0.vpc_id", ""), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "VPC_ENDPOINT"), ), }, { From 2b06a38688ce41f72da1bb1c78174ccc27b20e8f Mon Sep 17 00:00:00 2001 From: changelogbot Date: Fri, 2 Jul 2021 17:11:33 +0000 Subject: [PATCH 129/398] Update CHANGELOG.md after v3.48.0 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53ea0bf22fe..4b7731a01b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +## 3.49.0 (Unreleased) ## 3.48.0 (July 02, 2021) FEATURES: From c008160e6c9b51b9fe5fbd7873a5c5bc697f49e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Jul 2021 18:44:30 +0000 Subject: [PATCH 130/398] build(deps): bump github.com/bflad/tfproviderlint in /awsproviderlint Bumps [github.com/bflad/tfproviderlint](https://github.com/bflad/tfproviderlint) from 0.26.0 to 0.27.0. - [Release notes](https://github.com/bflad/tfproviderlint/releases) - [Changelog](https://github.com/bflad/tfproviderlint/blob/main/CHANGELOG.md) - [Commits](https://github.com/bflad/tfproviderlint/compare/v0.26.0...v0.27.0) --- updated-dependencies: - dependency-name: github.com/bflad/tfproviderlint dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- awsproviderlint/go.mod | 2 +- awsproviderlint/go.sum | 4 +- .../tfproviderlint/helper/astutils/package.go | 4 ++ .../bflad/tfproviderlint/passes/R006/R006.go | 42 +++++++++++++++++-- .../tfproviderlint/passes/R006/README.md | 4 ++ awsproviderlint/vendor/modules.txt | 2 +- 6 files changed, 51 insertions(+), 7 deletions(-) diff --git a/awsproviderlint/go.mod b/awsproviderlint/go.mod index a1f0079246c..8c64df62cb8 100644 --- a/awsproviderlint/go.mod +++ b/awsproviderlint/go.mod @@ -4,7 +4,7 @@ go 1.16 require ( github.com/aws/aws-sdk-go v1.38.71 - github.com/bflad/tfproviderlint v0.26.0 + github.com/bflad/tfproviderlint v0.27.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.7.0 golang.org/x/tools v0.0.0-20201028111035-eafbe7b904eb ) diff --git a/awsproviderlint/go.sum b/awsproviderlint/go.sum index 54c82c3d98e..26160262f8c 100644 --- a/awsproviderlint/go.sum +++ b/awsproviderlint/go.sum @@ -74,8 +74,8 @@ github.com/aws/aws-sdk-go v1.38.71 h1:aWhtgoOiDhBCfaAj9XbxzcyvjEAKovbtv7d5mCVBZX github.com/aws/aws-sdk-go v1.38.71/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/bflad/gopaniccheck v0.1.0 h1:tJftp+bv42ouERmUMWLoUn/5bi/iQZjHPznM00cP/bU= github.com/bflad/gopaniccheck v0.1.0/go.mod h1:ZCj2vSr7EqVeDaqVsWN4n2MwdROx1YL+LFo47TSWtsA= -github.com/bflad/tfproviderlint v0.26.0 h1:Xd+hbVlSQhKlXifpqmHPvlcnOK1lRS4IZf+cXBAUpCs= -github.com/bflad/tfproviderlint v0.26.0/go.mod h1:7Z9Pyl1Z1UWJcPBuyjN89D2NaJGpjReQb5NoaaQCthQ= +github.com/bflad/tfproviderlint v0.27.0 h1:KXF+dYaWJ/OSVyWIrk2NIYgQBMDDSOC4VQB/P+P5nhI= +github.com/bflad/tfproviderlint v0.27.0/go.mod h1:7Z9Pyl1Z1UWJcPBuyjN89D2NaJGpjReQb5NoaaQCthQ= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= diff --git a/awsproviderlint/vendor/github.com/bflad/tfproviderlint/helper/astutils/package.go b/awsproviderlint/vendor/github.com/bflad/tfproviderlint/helper/astutils/package.go index 1ce1d7f2c8d..91aae9414ee 100644 --- a/awsproviderlint/vendor/github.com/bflad/tfproviderlint/helper/astutils/package.go +++ b/awsproviderlint/vendor/github.com/bflad/tfproviderlint/helper/astutils/package.go @@ -293,6 +293,10 @@ func IsStdlibPackageType(t types.Type, packagePath string, typeName string) bool func isModulePackagePath(module string, packageSuffix string, path string) bool { // Only check end of path due to vendoring + if packageSuffix == "" { + return strings.HasSuffix(path, module) + } + r := regexp.MustCompile(fmt.Sprintf("%s(/v[1-9][0-9]*)?/%s$", module, packageSuffix)) return r.MatchString(path) } diff --git a/awsproviderlint/vendor/github.com/bflad/tfproviderlint/passes/R006/R006.go b/awsproviderlint/vendor/github.com/bflad/tfproviderlint/passes/R006/R006.go index da27a3e6a25..b66b59d58d3 100644 --- a/awsproviderlint/vendor/github.com/bflad/tfproviderlint/passes/R006/R006.go +++ b/awsproviderlint/vendor/github.com/bflad/tfproviderlint/passes/R006/R006.go @@ -3,8 +3,12 @@ package R006 import ( + "flag" "go/ast" + "go/types" + "strings" + "github.com/bflad/tfproviderlint/helper/astutils" "github.com/bflad/tfproviderlint/helper/terraformtype/helper/resource" "github.com/bflad/tfproviderlint/passes/commentignore" "github.com/bflad/tfproviderlint/passes/helper/resource/retryfuncinfo" @@ -14,13 +18,28 @@ import ( const Doc = `check for RetryFunc that omit retryable errors The R006 analyzer reports when RetryFunc declarations are missing -retryable errors and should not be used as RetryFunc.` +retryable errors and should not be used as RetryFunc. + +Optional parameters: + - package-aliases Comma-separated list of additional Go import paths to consider as aliases for helper/resource, defaults to none. +` const analyzerName = "R006" +var ( + packageAliases string +) + +func parseFlags() flag.FlagSet { + var flags = flag.NewFlagSet(analyzerName, flag.ExitOnError) + flags.StringVar(&packageAliases, "package-aliases", "", "Comma-separated list of additional Go import paths to consider as aliases for helper/resource") + return *flags +} + var Analyzer = &analysis.Analyzer{ - Name: analyzerName, - Doc: Doc, + Name: analyzerName, + Doc: Doc, + Flags: parseFlags(), Requires: []*analysis.Analyzer{ commentignore.Analyzer, retryfuncinfo.Analyzer, @@ -28,6 +47,18 @@ var Analyzer = &analysis.Analyzer{ Run: run, } +func isPackageAliasIgnored(e ast.Expr, info *types.Info, packageAliasesList string) bool { + packageAliases := strings.Split(packageAliasesList, ",") + + for _, packageAlias := range packageAliases { + if astutils.IsModulePackageFunc(e, info, packageAlias, "", resource.FuncNameRetryableError) { + return true + } + } + + return false +} + func run(pass *analysis.Pass) (interface{}, error) { ignorer := pass.ResultOf[commentignore.Analyzer].(*commentignore.Ignorer) retryFuncs := pass.ResultOf[retryfuncinfo.Analyzer].([]*resource.RetryFuncInfo) @@ -51,6 +82,11 @@ func run(pass *analysis.Pass) (interface{}, error) { return false } + if packageAliases != "" && isPackageAliasIgnored(callExpr.Fun, pass.TypesInfo, packageAliases) { + retryableErrorFound = true + return false + } + return true }) diff --git a/awsproviderlint/vendor/github.com/bflad/tfproviderlint/passes/R006/README.md b/awsproviderlint/vendor/github.com/bflad/tfproviderlint/passes/R006/README.md index 4afb2997335..af7441563a8 100644 --- a/awsproviderlint/vendor/github.com/bflad/tfproviderlint/passes/R006/README.md +++ b/awsproviderlint/vendor/github.com/bflad/tfproviderlint/passes/R006/README.md @@ -2,6 +2,10 @@ The R006 analyzer reports when `RetryFunc` declarations are missing retryable errors (e.g. `RetryableError()` calls) and should not be used as `RetryFunc`. +Optional parameters: + +- `-package-aliases` Comma-separated list of additional Go import paths to consider as aliases for helper/resource, defaults to none. + ## Flagged Code ```go diff --git a/awsproviderlint/vendor/modules.txt b/awsproviderlint/vendor/modules.txt index 76a11a4180f..868fd1b3cb0 100644 --- a/awsproviderlint/vendor/modules.txt +++ b/awsproviderlint/vendor/modules.txt @@ -70,7 +70,7 @@ github.com/bflad/gopaniccheck/passes/logpaniccallexpr github.com/bflad/gopaniccheck/passes/logpanicfcallexpr github.com/bflad/gopaniccheck/passes/logpaniclncallexpr github.com/bflad/gopaniccheck/passes/paniccallexpr -# github.com/bflad/tfproviderlint v0.26.0 +# github.com/bflad/tfproviderlint v0.27.0 ## explicit github.com/bflad/tfproviderlint/helper/analysisutils github.com/bflad/tfproviderlint/helper/astutils From ac93cbe89a22490d0d6b015ac1ab32c5ce735629 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Jul 2021 18:44:42 +0000 Subject: [PATCH 131/398] build(deps): bump github.com/aws/aws-sdk-go from 1.38.71 to 1.39.0 Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.38.71 to 1.39.0. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Changelog](https://github.com/aws/aws-sdk-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.38.71...v1.39.0) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 5a3199e20f3..0bdea89223d 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.16 require ( github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect - github.com/aws/aws-sdk-go v1.38.71 + github.com/aws/aws-sdk-go v1.39.0 github.com/beevik/etree v1.1.0 github.com/fatih/color v1.9.0 // indirect github.com/hashicorp/aws-sdk-go-base v0.7.1 diff --git a/go.sum b/go.sum index 64815f1736e..009b6a2c58d 100644 --- a/go.sum +++ b/go.sum @@ -66,8 +66,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= github.com/aws/aws-sdk-go v1.25.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.31.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/aws/aws-sdk-go v1.38.71 h1:aWhtgoOiDhBCfaAj9XbxzcyvjEAKovbtv7d5mCVBZXw= -github.com/aws/aws-sdk-go v1.38.71/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go v1.39.0 h1:74BBwkEmiqBbi2CGflEh34l0YNtIibTjZsibGarkNjo= +github.com/aws/aws-sdk-go v1.39.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= From e5dd508992c895215fd8cb86f4459d9d03e9114f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Jul 2021 18:44:54 +0000 Subject: [PATCH 132/398] build(deps): bump github.com/aws/aws-sdk-go in /awsproviderlint Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.38.71 to 1.39.0. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Changelog](https://github.com/aws/aws-sdk-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.38.71...v1.39.0) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- awsproviderlint/go.mod | 2 +- awsproviderlint/go.sum | 4 +- .../github.com/aws/aws-sdk-go/aws/version.go | 2 +- .../aws/aws-sdk-go/internal/ini/doc.go | 33 ++++++++---- .../aws/aws-sdk-go/internal/ini/ini_parser.go | 51 ++++++++----------- .../aws/aws-sdk-go/internal/ini/visitor.go | 5 +- awsproviderlint/vendor/modules.txt | 2 +- 7 files changed, 54 insertions(+), 45 deletions(-) diff --git a/awsproviderlint/go.mod b/awsproviderlint/go.mod index a1f0079246c..4b3289a4fd4 100644 --- a/awsproviderlint/go.mod +++ b/awsproviderlint/go.mod @@ -3,7 +3,7 @@ module github.com/terraform-providers/terraform-provider-aws/awsproviderlint go 1.16 require ( - github.com/aws/aws-sdk-go v1.38.71 + github.com/aws/aws-sdk-go v1.39.0 github.com/bflad/tfproviderlint v0.26.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.7.0 golang.org/x/tools v0.0.0-20201028111035-eafbe7b904eb diff --git a/awsproviderlint/go.sum b/awsproviderlint/go.sum index 54c82c3d98e..6dad7baea21 100644 --- a/awsproviderlint/go.sum +++ b/awsproviderlint/go.sum @@ -70,8 +70,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= github.com/aws/aws-sdk-go v1.25.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.38.71 h1:aWhtgoOiDhBCfaAj9XbxzcyvjEAKovbtv7d5mCVBZXw= -github.com/aws/aws-sdk-go v1.38.71/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go v1.39.0 h1:74BBwkEmiqBbi2CGflEh34l0YNtIibTjZsibGarkNjo= +github.com/aws/aws-sdk-go v1.39.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/bflad/gopaniccheck v0.1.0 h1:tJftp+bv42ouERmUMWLoUn/5bi/iQZjHPznM00cP/bU= github.com/bflad/gopaniccheck v0.1.0/go.mod h1:ZCj2vSr7EqVeDaqVsWN4n2MwdROx1YL+LFo47TSWtsA= github.com/bflad/tfproviderlint v0.26.0 h1:Xd+hbVlSQhKlXifpqmHPvlcnOK1lRS4IZf+cXBAUpCs= diff --git a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/version.go b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/version.go index 95a41a03b2b..62fbbf77404 100644 --- a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/version.go +++ b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/version.go @@ -5,4 +5,4 @@ package aws const SDKName = "aws-sdk-go" // SDKVersion is the version of this SDK -const SDKVersion = "1.38.71" +const SDKVersion = "1.39.0" diff --git a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/internal/ini/doc.go b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/internal/ini/doc.go index 25ce0fe134d..1e55bbd07b9 100644 --- a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/internal/ini/doc.go +++ b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/internal/ini/doc.go @@ -13,17 +13,30 @@ // } // // Below is the BNF that describes this parser -// Grammar: -// stmt -> value stmt' -// stmt' -> epsilon | op stmt -// value -> number | string | boolean | quoted_string +// Grammar: +// stmt -> section | stmt' +// stmt' -> epsilon | expr +// expr -> value (stmt)* | equal_expr (stmt)* +// equal_expr -> value ( ':' | '=' ) equal_expr' +// equal_expr' -> number | string | quoted_string +// quoted_string -> " quoted_string' +// quoted_string' -> string quoted_string_end +// quoted_string_end -> " // -// section -> [ section' -// section' -> value section_close -// section_close -> ] +// section -> [ section' +// section' -> section_value section_close +// section_value -> number | string_subset | boolean | quoted_string_subset +// quoted_string_subset -> " quoted_string_subset' +// quoted_string_subset' -> string_subset quoted_string_end +// quoted_string_subset -> " +// section_close -> ] // -// SkipState will skip (NL WS)+ +// value -> number | string_subset | boolean +// string -> ? UTF-8 Code-Points except '\n' (U+000A) and '\r\n' (U+000D U+000A) ? +// string_subset -> ? Code-points excepted by grammar except ':' (U+003A), '=' (U+003D), '[' (U+005B), and ']' (U+005D) ? // -// comment -> # comment' | ; comment' -// comment' -> epsilon | value +// SkipState will skip (NL WS)+ +// +// comment -> # comment' | ; comment' +// comment' -> epsilon | value package ini diff --git a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/internal/ini/ini_parser.go b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/internal/ini/ini_parser.go index 55fa73ebcf2..0ba319491c0 100644 --- a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/internal/ini/ini_parser.go +++ b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/internal/ini/ini_parser.go @@ -5,9 +5,12 @@ import ( "io" ) +// ParseState represents the current state of the parser. +type ParseState uint + // State enums for the parse table const ( - InvalidState = iota + InvalidState ParseState = iota // stmt -> value stmt' StatementState // stmt' -> MarkComplete | op stmt @@ -36,8 +39,8 @@ const ( ) // parseTable is a state machine to dictate the grammar above. -var parseTable = map[ASTKind]map[TokenType]int{ - ASTKindStart: map[TokenType]int{ +var parseTable = map[ASTKind]map[TokenType]ParseState{ + ASTKindStart: { TokenLit: StatementState, TokenSep: OpenScopeState, TokenWS: SkipTokenState, @@ -45,7 +48,7 @@ var parseTable = map[ASTKind]map[TokenType]int{ TokenComment: CommentState, TokenNone: TerminalState, }, - ASTKindCommentStatement: map[TokenType]int{ + ASTKindCommentStatement: { TokenLit: StatementState, TokenSep: OpenScopeState, TokenWS: SkipTokenState, @@ -53,7 +56,7 @@ var parseTable = map[ASTKind]map[TokenType]int{ TokenComment: CommentState, TokenNone: MarkCompleteState, }, - ASTKindExpr: map[TokenType]int{ + ASTKindExpr: { TokenOp: StatementPrimeState, TokenLit: ValueState, TokenSep: OpenScopeState, @@ -62,13 +65,15 @@ var parseTable = map[ASTKind]map[TokenType]int{ TokenComment: CommentState, TokenNone: MarkCompleteState, }, - ASTKindEqualExpr: map[TokenType]int{ - TokenLit: ValueState, - TokenWS: SkipTokenState, - TokenNL: SkipState, - TokenNone: SkipState, + ASTKindEqualExpr: { + TokenLit: ValueState, + TokenSep: ValueState, + TokenOp: ValueState, + TokenWS: SkipTokenState, + TokenNL: SkipState, + TokenNone: SkipState, }, - ASTKindStatement: map[TokenType]int{ + ASTKindStatement: { TokenLit: SectionState, TokenSep: CloseScopeState, TokenWS: SkipTokenState, @@ -76,9 +81,9 @@ var parseTable = map[ASTKind]map[TokenType]int{ TokenComment: CommentState, TokenNone: MarkCompleteState, }, - ASTKindExprStatement: map[TokenType]int{ + ASTKindExprStatement: { TokenLit: ValueState, - TokenSep: OpenScopeState, + TokenSep: ValueState, TokenOp: ValueState, TokenWS: ValueState, TokenNL: MarkCompleteState, @@ -86,14 +91,14 @@ var parseTable = map[ASTKind]map[TokenType]int{ TokenNone: TerminalState, TokenComma: SkipState, }, - ASTKindSectionStatement: map[TokenType]int{ + ASTKindSectionStatement: { TokenLit: SectionState, TokenOp: SectionState, TokenSep: CloseScopeState, TokenWS: SectionState, TokenNL: SkipTokenState, }, - ASTKindCompletedSectionStatement: map[TokenType]int{ + ASTKindCompletedSectionStatement: { TokenWS: SkipTokenState, TokenNL: SkipTokenState, TokenLit: StatementState, @@ -101,7 +106,7 @@ var parseTable = map[ASTKind]map[TokenType]int{ TokenComment: CommentState, TokenNone: MarkCompleteState, }, - ASTKindSkipStatement: map[TokenType]int{ + ASTKindSkipStatement: { TokenLit: StatementState, TokenSep: OpenScopeState, TokenWS: SkipTokenState, @@ -205,18 +210,6 @@ loop: case ValueState: // ValueState requires the previous state to either be an equal expression // or an expression statement. - // - // This grammar occurs when the RHS is a number, word, or quoted string. - // equal_expr -> lit op equal_expr' - // equal_expr' -> number | string | quoted_string - // quoted_string -> " quoted_string' - // quoted_string' -> string quoted_string_end - // quoted_string_end -> " - // - // otherwise - // expr_stmt -> equal_expr (expr_stmt')* - // expr_stmt' -> ws S | op S | MarkComplete - // S -> equal_expr' expr_stmt' switch k.Kind { case ASTKindEqualExpr: // assigning a value to some key @@ -243,7 +236,7 @@ loop: } children[len(children)-1] = rhs - k.SetChildren(children) + root.SetChildren(children) stack.Push(k) } diff --git a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/internal/ini/visitor.go b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/internal/ini/visitor.go index 94841c32443..081cf433424 100644 --- a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/internal/ini/visitor.go +++ b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/internal/ini/visitor.go @@ -50,7 +50,10 @@ func (v *DefaultVisitor) VisitExpr(expr AST) error { rhs := children[1] - if rhs.Root.Type() != TokenLit { + // The right-hand value side the equality expression is allowed to contain '[', ']', ':', '=' in the values. + // If the token is not either a literal or one of the token types that identifies those four additional + // tokens then error. + if !(rhs.Root.Type() == TokenLit || rhs.Root.Type() == TokenOp || rhs.Root.Type() == TokenSep) { return NewParseError("unexpected token type") } diff --git a/awsproviderlint/vendor/modules.txt b/awsproviderlint/vendor/modules.txt index 76a11a4180f..7bffdfcc982 100644 --- a/awsproviderlint/vendor/modules.txt +++ b/awsproviderlint/vendor/modules.txt @@ -14,7 +14,7 @@ github.com/agext/levenshtein github.com/apparentlymart/go-textseg/v12/textseg # github.com/apparentlymart/go-textseg/v13 v13.0.0 github.com/apparentlymart/go-textseg/v13/textseg -# github.com/aws/aws-sdk-go v1.38.71 +# github.com/aws/aws-sdk-go v1.39.0 ## explicit github.com/aws/aws-sdk-go/aws github.com/aws/aws-sdk-go/aws/arn From 38971b4f186d1d0367cc3a5cc92cf790c59a3a8f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Jul 2021 15:09:19 -0400 Subject: [PATCH 133/398] r/aws_eks_cluster: Remove extra 'ForceNew's. --- aws/resource_aws_eks_cluster.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/aws/resource_aws_eks_cluster.go b/aws/resource_aws_eks_cluster.go index c13ed4eb7a9..3c8e1000a47 100644 --- a/aws/resource_aws_eks_cluster.go +++ b/aws/resource_aws_eks_cluster.go @@ -91,7 +91,6 @@ func resourceAwsEksCluster() *schema.Resource { "key_arn": { Type: schema.TypeString, Required: true, - ForceNew: true, }, }, }, @@ -99,7 +98,6 @@ func resourceAwsEksCluster() *schema.Resource { "resources": { Type: schema.TypeSet, Required: true, - ForceNew: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice(tfeks.Resources_Values(), false), From 678491c778219987b09c555373d3a8809466aaa9 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Jul 2021 15:32:32 -0400 Subject: [PATCH 134/398] Simplify acceptance test configurations. --- aws/resource_aws_transfer_server.go | 17 ------- aws/resource_aws_transfer_server_test.go | 63 ++++++------------------ 2 files changed, 16 insertions(+), 64 deletions(-) diff --git a/aws/resource_aws_transfer_server.go b/aws/resource_aws_transfer_server.go index c04b235b31b..d388f43f578 100644 --- a/aws/resource_aws_transfer_server.go +++ b/aws/resource_aws_transfer_server.go @@ -542,23 +542,6 @@ func resourceAwsTransferServerUpdate(d *schema.ResourceData, meta interface{}) e return err } - if newEndpointTypeVpc && !oldEndpointTypeVpc { - // Wait for newly provisioned VPC Endpoint to become available. - output, err := finder.ServerByID(conn, d.Id()) - - if err != nil { - return fmt.Errorf("error reading Transfer Server (%s): %w", d.Id(), err) - } - - vpcEndpointID := aws.StringValue(output.EndpointDetails.VpcEndpointId) - - _, err = ec2waiter.VpcEndpointAvailable(meta.(*AWSClient).ec2conn, vpcEndpointID, d.Timeout(schema.TimeoutUpdate)) - - if err != nil { - return fmt.Errorf("error waiting for Transfer Server (%s) VPC Endpoint (%s) to become available: %w", d.Id(), vpcEndpointID, err) - } - } - if len(addressAllocationIDs) > 0 { input := &transfer.UpdateServerInput{ ServerId: aws.String(d.Id()), diff --git a/aws/resource_aws_transfer_server_test.go b/aws/resource_aws_transfer_server_test.go index c1274b96ed9..1bb9ab0d79a 100644 --- a/aws/resource_aws_transfer_server_test.go +++ b/aws/resource_aws_transfer_server_test.go @@ -1040,6 +1040,16 @@ resource "aws_security_group" "test" { resource "aws_default_security_group" "test" { vpc_id = aws_vpc.test.id } + +resource "aws_eip" "test" { + count = 2 + + vpc = true + + tags = { + Name = %[1]q + } +} `, rName) } @@ -1310,17 +1320,7 @@ resource "aws_transfer_server" "test" { func testAccAWSTransferServerVpcAddressAllocationIdsConfig(rName string) string { return composeConfig( testAccAWSTransferServerConfigBaseVpc(rName), - fmt.Sprintf(` -resource "aws_eip" "test" { - count = 2 - - vpc = true - - tags = { - Name = %[1]q - } -} - + ` resource "aws_transfer_server" "test" { endpoint_type = "VPC" @@ -1330,23 +1330,13 @@ resource "aws_transfer_server" "test" { vpc_id = aws_vpc.test.id } } -`, rName)) +`) } func testAccAWSTransferServerVpcAddressAllocationIdsUpdateConfig(rName string) string { return composeConfig( testAccAWSTransferServerConfigBaseVpc(rName), - fmt.Sprintf(` -resource "aws_eip" "test" { - count = 2 - - vpc = true - - tags = { - Name = %[1]q - } -} - + ` resource "aws_transfer_server" "test" { endpoint_type = "VPC" @@ -1356,23 +1346,13 @@ resource "aws_transfer_server" "test" { vpc_id = aws_vpc.test.id } } -`, rName)) +`) } func testAccAWSTransferServerVpcAddressAllocationIdsSecurityGroupIdsConfig(rName string) string { return composeConfig( testAccAWSTransferServerConfigBaseVpc(rName), - fmt.Sprintf(` -resource "aws_eip" "test" { - count = 2 - - vpc = true - - tags = { - Name = %[1]q - } -} - + ` resource "aws_transfer_server" "test" { endpoint_type = "VPC" @@ -1383,7 +1363,7 @@ resource "aws_transfer_server" "test" { vpc_id = aws_vpc.test.id } } -`, rName)) +`) } func testAccAWSTransferServerVpcAddressAllocationIdsSecurityGroupIdsUpdateConfig(rName string) string { @@ -1398,17 +1378,6 @@ resource "aws_security_group" "test2" { Name = "%[1]s-2" } } - -resource "aws_eip" "test" { - count = 2 - - vpc = true - - tags = { - Name = %[1]q - } -} - resource "aws_transfer_server" "test" { endpoint_type = "VPC" From 5f0574f3461eb780e1b865316d39064b3fd89a24 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Sun, 4 Jul 2021 02:51:33 +0300 Subject: [PATCH 135/398] docs + more tests --- a.txt | 2 + ..._aws_ecr_replication_configuration_test.go | 49 +++++++++++++++++++ ...cr_replication_configuration.html.markdown | 24 +++++++++ 3 files changed, 75 insertions(+) create mode 100644 a.txt diff --git a/a.txt b/a.txt new file mode 100644 index 00000000000..8bdec30e087 --- /dev/null +++ b/a.txt @@ -0,0 +1,2 @@ +==> Checking that code complies with gofmt requirements... +TF_ACC=1 go test ./aws -v -count 1 -parallel 20 -run=TestAccAWSEcrReplicationConfiguration_basic -timeout 180m diff --git a/aws/resource_aws_ecr_replication_configuration_test.go b/aws/resource_aws_ecr_replication_configuration_test.go index 133be0214c6..d6d964d25b9 100644 --- a/aws/resource_aws_ecr_replication_configuration_test.go +++ b/aws/resource_aws_ecr_replication_configuration_test.go @@ -35,6 +35,32 @@ func TestAccAWSEcrReplicationConfiguration_basic(t *testing.T) { ImportState: true, ImportStateVerify: true, }, + { + Config: testAccAWSEcrReplicationMultipleRegionConfiguration(testAccGetAlternateRegion(), testAccGetThirdRegion()), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEcrReplicationConfigurationExists(resourceName), + testAccCheckResourceAttrAccountID(resourceName, "registry_id"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.destination.#", "2"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.destination.0.region", testAccGetAlternateRegion()), + testAccCheckResourceAttrAccountID(resourceName, "replication_configuration.0.rule.0.destination.0.registry_id"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.destination.1.region", testAccGetThirdRegion()), + testAccCheckResourceAttrAccountID(resourceName, "replication_configuration.0.rule.0.destination..registry_id"), + ), + }, + { + Config: testAccAWSEcrReplicationConfiguration(testAccGetAlternateRegion()), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEcrReplicationConfigurationExists(resourceName), + testAccCheckResourceAttrAccountID(resourceName, "registry_id"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.destination.#", "1"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.destination.0.region", testAccGetAlternateRegion()), + testAccCheckResourceAttrAccountID(resourceName, "replication_configuration.0.rule.0.destination.0.registry_id"), + ), + }, }, }) } @@ -97,3 +123,26 @@ resource "aws_ecr_replication_configuration" "test" { } `, region) } + +func testAccAWSEcrReplicationMultipleRegionConfiguration(region1, region2 string) string { + return fmt.Sprintf(` +data "aws_caller_identity" "current" {} + +resource "aws_ecr_replication_configuration" "test" { + replication_configuration { + rule { + destination { + region = %[1]q + registry_id = data.aws_caller_identity.current.account_id + } + + + destination { + region = %[2]q + registry_id = data.aws_caller_identity.current.account_id + } + } + } +} +`, region1, region2) +} diff --git a/website/docs/r/ecr_replication_configuration.html.markdown b/website/docs/r/ecr_replication_configuration.html.markdown index cb673562056..b5da3657b97 100644 --- a/website/docs/r/ecr_replication_configuration.html.markdown +++ b/website/docs/r/ecr_replication_configuration.html.markdown @@ -29,6 +29,30 @@ resource "aws_ecr_replication_configuration" "example" { } ``` +## Multiple Region Usage + +```terraform +data "aws_caller_identity" "current" {} + +data "aws_regions" "example" {} + +resource "aws_ecr_replication_configuration" "example" { + replication_configuration { + rule { + destination { + region = data.aws_regions.example.names[0] + registry_id = data.aws_caller_identity.current.account_id + } + + destination { + region = data.aws_regions.example.names[1] + registry_id = data.aws_caller_identity.current.account_id + } + } + } +} +``` + ## Argument Reference The following arguments are supported: From 69fee06eb6ac2c108f5888b188669eb2b5b5f7ec Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Sun, 4 Jul 2021 02:53:55 +0300 Subject: [PATCH 136/398] fix test --- aws/resource_aws_ecr_replication_configuration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_ecr_replication_configuration_test.go b/aws/resource_aws_ecr_replication_configuration_test.go index d6d964d25b9..9ff3bc9c93b 100644 --- a/aws/resource_aws_ecr_replication_configuration_test.go +++ b/aws/resource_aws_ecr_replication_configuration_test.go @@ -46,7 +46,7 @@ func TestAccAWSEcrReplicationConfiguration_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.destination.0.region", testAccGetAlternateRegion()), testAccCheckResourceAttrAccountID(resourceName, "replication_configuration.0.rule.0.destination.0.registry_id"), resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.destination.1.region", testAccGetThirdRegion()), - testAccCheckResourceAttrAccountID(resourceName, "replication_configuration.0.rule.0.destination..registry_id"), + testAccCheckResourceAttrAccountID(resourceName, "replication_configuration.0.rule.0.destination.1.registry_id"), ), }, { From 9f001df9932c62dc7ed70c7f107ba3093e202040 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Sun, 4 Jul 2021 02:58:58 +0300 Subject: [PATCH 137/398] doc fmt --- website/docs/r/ecr_replication_configuration.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/ecr_replication_configuration.html.markdown b/website/docs/r/ecr_replication_configuration.html.markdown index b5da3657b97..c98f137060a 100644 --- a/website/docs/r/ecr_replication_configuration.html.markdown +++ b/website/docs/r/ecr_replication_configuration.html.markdown @@ -47,7 +47,7 @@ resource "aws_ecr_replication_configuration" "example" { destination { region = data.aws_regions.example.names[1] registry_id = data.aws_caller_identity.current.account_id - } + } } } } From 547ae52af6f320744679d40c5d24552f5fa9ae61 Mon Sep 17 00:00:00 2001 From: huikaihoo Date: Tue, 6 Jul 2021 11:53:05 +0800 Subject: [PATCH 138/398] Update amplify_branch.html.markdown Fix typo on `enable_notification` --- website/docs/r/amplify_branch.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/amplify_branch.html.markdown b/website/docs/r/amplify_branch.html.markdown index 38a78617f55..223f9427a27 100644 --- a/website/docs/r/amplify_branch.html.markdown +++ b/website/docs/r/amplify_branch.html.markdown @@ -162,7 +162,7 @@ The following arguments are supported: * `display_name` - (Optional) The display name for a branch. This is used as the default domain prefix. * `enable_auto_build` - (Optional) Enables auto building for the branch. * `enable_basic_auth` - (Optional) Enables basic authorization for the branch. -* `enable_notifications` - (Optional) Enables notifications for the branch. +* `enable_notification` - (Optional) Enables notifications for the branch. * `enable_performance_mode` - (Optional) Enables performance mode for the branch. * `enable_pull_request_preview` - (Optional) Enables pull request previews for this branch. * `environment_variables` - (Optional) The environment variables for the branch. From b0a239dcbcd45cc606e9aaa343674e82a62477ba Mon Sep 17 00:00:00 2001 From: "zhigang.huang" Date: Tue, 6 Jul 2021 16:26:26 +0800 Subject: [PATCH 139/398] add tags when create target group in aws --- aws/resource_aws_lb_target_group.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/aws/resource_aws_lb_target_group.go b/aws/resource_aws_lb_target_group.go index 23a5f91b252..73ee689aa8c 100644 --- a/aws/resource_aws_lb_target_group.go +++ b/aws/resource_aws_lb_target_group.go @@ -286,6 +286,8 @@ func suppressIfTargetType(t string) schema.SchemaDiffSuppressFunc { func resourceAwsLbTargetGroupCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).elbv2conn + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig + tags := defaultTagsConfig.MergeTags(keyvaluetags.New(d.Get("tags").(map[string]interface{}))) var groupName string if v, ok := d.GetOk("name"); ok { @@ -363,6 +365,10 @@ func resourceAwsLbTargetGroupCreate(d *schema.ResourceData, meta interface{}) er } } + if len(tags) > 0 { + params.Tags = tags.IgnoreAws().Elbv2Tags() + } + resp, err := conn.CreateTargetGroup(params) if err != nil { return fmt.Errorf("error creating LB Target Group: %w", err) From b56d16157e39290905a30cce94d6cc8438456263 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Jul 2021 16:48:48 -0400 Subject: [PATCH 140/398] r/aws_db_cluster_role_association - New resource. Co-authored-by: Matthieu ANTOINE --- ...esource_aws_db_cluster_role_association.go | 254 ++++++++++++++++++ ...ce_aws_db_cluster_role_association_test.go | 198 ++++++++++++++ .../db_cluster_role_association.html.markdown | 46 ++++ 3 files changed, 498 insertions(+) create mode 100644 aws/resource_aws_db_cluster_role_association.go create mode 100644 aws/resource_aws_db_cluster_role_association_test.go create mode 100644 website/docs/r/db_cluster_role_association.html.markdown diff --git a/aws/resource_aws_db_cluster_role_association.go b/aws/resource_aws_db_cluster_role_association.go new file mode 100644 index 00000000000..5137a3b0147 --- /dev/null +++ b/aws/resource_aws_db_cluster_role_association.go @@ -0,0 +1,254 @@ +package aws + +import ( + "fmt" + "log" + "strings" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/rds" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// Constants not currently provided by the AWS Go SDK +const ( + rdsDbClusterRoleStatusActive = "ACTIVE" + rdsDbClusterRoleStatusDeleted = "DELETED" + rdsDbClusterRoleStatusPending = "PENDING" +) + +func resourceAwsDbClusterRoleAssociation() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsDbClusterRoleAssociationCreate, + Read: resourceAwsDbClusterRoleAssociationRead, + Delete: resourceAwsDbClusterRoleAssociationDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "db_cluster_identifier": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "feature_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "role_arn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArn, + }, + }, + } +} + +func resourceAwsDbClusterRoleAssociationCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).rdsconn + + dbClusterIdentifier := d.Get("db_cluster_identifier").(string) + roleArn := d.Get("role_arn").(string) + + input := &rds.AddRoleToDBClusterInput{ + DBClusterIdentifier: aws.String(dbClusterIdentifier), + FeatureName: aws.String(d.Get("feature_name").(string)), + RoleArn: aws.String(roleArn), + } + + log.Printf("[DEBUG] RDS DB Cluster (%s) IAM Role associating: %s", dbClusterIdentifier, roleArn) + _, err := conn.AddRoleToDBCluster(input) + + if err != nil { + return fmt.Errorf("error associating RDS DB Cluster (%s) IAM Role (%s): %s", dbClusterIdentifier, roleArn, err) + } + + d.SetId(fmt.Sprintf("%s,%s", dbClusterIdentifier, roleArn)) + + if err := waitForRdsDbClusterRoleAssociation(conn, dbClusterIdentifier, roleArn); err != nil { + return fmt.Errorf("error waiting for RDS DB Cluster (%s) IAM Role (%s) association: %s", dbClusterIdentifier, roleArn, err) + } + + return resourceAwsDbClusterRoleAssociationRead(d, meta) +} + +func resourceAwsDbClusterRoleAssociationRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).rdsconn + + dbClusterIdentifier, roleArn, err := resourceAwsDbClusterRoleAssociationDecodeID(d.Id()) + + if err != nil { + return fmt.Errorf("error reading resource ID: %s", err) + } + + dbClusterRole, err := rdsDescribeDbClusterRole(conn, dbClusterIdentifier, roleArn) + + if isAWSErr(err, rds.ErrCodeDBClusterNotFoundFault, "") { + log.Printf("[WARN] RDS DB Cluster (%s) not found, removing from state", dbClusterIdentifier) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error reading RDS DB Cluster (%s) IAM Role (%s) association: %s", dbClusterIdentifier, roleArn, err) + } + + if dbClusterRole == nil { + log.Printf("[WARN] RDS DB Cluster (%s) IAM Role (%s) association not found, removing from state", dbClusterIdentifier, roleArn) + d.SetId("") + return nil + } + + d.Set("db_cluster_identifier", dbClusterIdentifier) + d.Set("feature_name", dbClusterRole.FeatureName) + d.Set("role_arn", dbClusterRole.RoleArn) + + return nil +} + +func resourceAwsDbClusterRoleAssociationDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).rdsconn + + dbClusterIdentifier, roleArn, err := resourceAwsDbClusterRoleAssociationDecodeID(d.Id()) + + if err != nil { + return fmt.Errorf("error reading resource ID: %s", err) + } + + input := &rds.RemoveRoleFromDBClusterInput{ + DBClusterIdentifier: aws.String(dbClusterIdentifier), + FeatureName: aws.String(d.Get("feature_name").(string)), + RoleArn: aws.String(roleArn), + } + + log.Printf("[DEBUG] RDS DB Cluster (%s) IAM Role disassociating: %s", dbClusterIdentifier, roleArn) + _, err = conn.RemoveRoleFromDBCluster(input) + + if isAWSErr(err, rds.ErrCodeDBClusterNotFoundFault, "") { + return nil + } + + if isAWSErr(err, rds.ErrCodeDBClusterRoleNotFoundFault, "") { + return nil + } + + if err != nil { + return fmt.Errorf("error disassociating RDS DB Cluster (%s) IAM Role (%s): %s", dbClusterIdentifier, roleArn, err) + } + + if err := waitForRdsDbClusterRoleDisassociation(conn, dbClusterIdentifier, roleArn); err != nil { + return fmt.Errorf("error waiting for RDS DB Cluster (%s) IAM Role (%s) disassociation: %s", dbClusterIdentifier, roleArn, err) + } + + return nil +} + +func resourceAwsDbClusterRoleAssociationDecodeID(id string) (string, string, error) { + parts := strings.SplitN(id, ",", 2) + + if len(parts) != 2 || parts[0] == "" || parts[1] == "" { + return "", "", fmt.Errorf("unexpected format of ID (%s), expected DB-CLUSTER-ID,ROLE-ARN", id) + } + + return parts[0], parts[1], nil +} + +func rdsDescribeDbClusterRole(conn *rds.RDS, dbClusterIdentifier, roleArn string) (*rds.DBClusterRole, error) { + input := &rds.DescribeDBClustersInput{ + DBClusterIdentifier: aws.String(dbClusterIdentifier), + } + + log.Printf("[DEBUG] Describing RDS DB Cluster: %s", input) + output, err := conn.DescribeDBClusters(input) + + if err != nil { + return nil, err + } + + var dbCluster *rds.DBCluster + + for _, outputDbCluster := range output.DBClusters { + if aws.StringValue(outputDbCluster.DBClusterIdentifier) == dbClusterIdentifier { + dbCluster = outputDbCluster + break + } + } + + if dbCluster == nil { + return nil, nil + } + + var dbClusterRole *rds.DBClusterRole + + for _, associatedRole := range dbCluster.AssociatedRoles { + if aws.StringValue(associatedRole.RoleArn) == roleArn { + dbClusterRole = associatedRole + break + } + } + + return dbClusterRole, nil +} + +func waitForRdsDbClusterRoleAssociation(conn *rds.RDS, dbClusterIdentifier, roleArn string) error { + stateConf := &resource.StateChangeConf{ + Pending: []string{rdsDbClusterRoleStatusPending}, + Target: []string{rdsDbClusterRoleStatusActive}, + Refresh: func() (interface{}, string, error) { + dbClusterRole, err := rdsDescribeDbClusterRole(conn, dbClusterIdentifier, roleArn) + + if err != nil { + return nil, "", err + } + + return dbClusterRole, aws.StringValue(dbClusterRole.Status), nil + }, + Timeout: 5 * time.Minute, + Delay: 5 * time.Second, + } + + log.Printf("[DEBUG] Waiting for RDS DB Cluster (%s) IAM Role association: %s", dbClusterIdentifier, roleArn) + _, err := stateConf.WaitForState() + + return err +} + +func waitForRdsDbClusterRoleDisassociation(conn *rds.RDS, dbClusterIdentifier, roleArn string) error { + stateConf := &resource.StateChangeConf{ + Pending: []string{ + rdsDbClusterRoleStatusActive, + rdsDbClusterRoleStatusPending, + }, + Target: []string{rdsDbClusterRoleStatusDeleted}, + Refresh: func() (interface{}, string, error) { + dbClusterRole, err := rdsDescribeDbClusterRole(conn, dbClusterIdentifier, roleArn) + + if isAWSErr(err, rds.ErrCodeDBClusterNotFoundFault, "") { + return &rds.DBClusterRole{}, rdsDbClusterRoleStatusDeleted, nil + } + + if err != nil { + return nil, "", err + } + + if dbClusterRole != nil { + return dbClusterRole, aws.StringValue(dbClusterRole.Status), nil + } + + return &rds.DBClusterRole{}, rdsDbClusterRoleStatusDeleted, nil + }, + Timeout: 5 * time.Minute, + Delay: 5 * time.Second, + } + + log.Printf("[DEBUG] Waiting for RDS DB Cluster (%s) IAM Role disassociation: %s", dbClusterIdentifier, roleArn) + _, err := stateConf.WaitForState() + + return err +} diff --git a/aws/resource_aws_db_cluster_role_association_test.go b/aws/resource_aws_db_cluster_role_association_test.go new file mode 100644 index 00000000000..93b1cb455cd --- /dev/null +++ b/aws/resource_aws_db_cluster_role_association_test.go @@ -0,0 +1,198 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/rds" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccAWSDbClusterRoleAssociation_basic(t *testing.T) { + var dbClusterRole1 rds.DBClusterRole + rName := acctest.RandomWithPrefix("tf-acc-test") + dbClusterResourceName := "aws_rds_cluster.test" + iamRoleResourceName := "aws_iam_role.test" + resourceName := "aws_db_cluster_role_association.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, rds.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSDbClusterRoleAssociationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSDbClusterRoleAssociationConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDbClusterRoleAssociationExists(resourceName, &dbClusterRole1), + resource.TestCheckResourceAttrPair(resourceName, "db_cluster_identifier", dbClusterResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "feature_name", "s3Import"), + resource.TestCheckResourceAttrPair(resourceName, "role_arn", iamRoleResourceName, "arn"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSDbClusterRoleAssociation_disappears(t *testing.T) { + var dbCluster1 rds.DBCluster + var dbClusterRole1 rds.DBClusterRole + rName := acctest.RandomWithPrefix("tf-acc-test") + dbClusterResourceName := "aws_rds_cluster.test" + resourceName := "aws_db_cluster_role_association.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, rds.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSDbClusterRoleAssociationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSDbClusterRoleAssociationConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSClusterExists(dbClusterResourceName, &dbCluster1), + testAccCheckAWSDbClusterRoleAssociationExists(resourceName, &dbClusterRole1), + testAccCheckAWSDbClusterRoleAssociationDisappears(&dbCluster1, &dbClusterRole1), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckAWSDbClusterRoleAssociationExists(resourceName string, dbClusterRole *rds.DBClusterRole) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + + if !ok { + return fmt.Errorf("Resource not found: %s", resourceName) + } + + dbClusterIdentifier, roleArn, err := resourceAwsDbClusterRoleAssociationDecodeID(rs.Primary.ID) + + if err != nil { + return fmt.Errorf("error reading resource ID: %s", err) + } + + conn := testAccProvider.Meta().(*AWSClient).rdsconn + + role, err := rdsDescribeDbClusterRole(conn, dbClusterIdentifier, roleArn) + + if err != nil { + return err + } + + if role == nil { + return fmt.Errorf("RDS DB Cluster IAM Role Association not found") + } + + if aws.StringValue(role.Status) != "ACTIVE" { + return fmt.Errorf("RDS DB Cluster (%s) IAM Role (%s) association exists in non-ACTIVE (%s) state", dbClusterIdentifier, roleArn, aws.StringValue(role.Status)) + } + + *dbClusterRole = *role + + return nil + } +} + +func testAccCheckAWSDbClusterRoleAssociationDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).rdsconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_db_cluster_role_association" { + continue + } + + dbClusterIdentifier, roleArn, err := resourceAwsDbClusterRoleAssociationDecodeID(rs.Primary.ID) + + if err != nil { + return fmt.Errorf("error reading resource ID: %s", err) + } + + dbClusterRole, err := rdsDescribeDbClusterRole(conn, dbClusterIdentifier, roleArn) + + if isAWSErr(err, rds.ErrCodeDBClusterNotFoundFault, "") { + continue + } + + if err != nil { + return err + } + + if dbClusterRole == nil { + continue + } + + return fmt.Errorf("RDS DB Cluster (%s) IAM Role (%s) association still exists in non-deleted (%s) state", dbClusterIdentifier, roleArn, aws.StringValue(dbClusterRole.Status)) + } + + return nil +} + +func testAccCheckAWSDbClusterRoleAssociationDisappears(dbCluster *rds.DBCluster, dbClusterRole *rds.DBClusterRole) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).rdsconn + + input := &rds.RemoveRoleFromDBClusterInput{ + DBClusterIdentifier: dbCluster.DBClusterIdentifier, + FeatureName: dbClusterRole.FeatureName, + RoleArn: dbClusterRole.RoleArn, + } + + _, err := conn.RemoveRoleFromDBCluster(input) + + if err != nil { + return err + } + + return waitForRdsDbClusterRoleDisassociation(conn, aws.StringValue(dbCluster.DBClusterIdentifier), aws.StringValue(dbClusterRole.RoleArn)) + } +} + +func testAccAWSDbClusterRoleAssociationConfig(rName string) string { + return composeConfig(testAccAvailableAZsNoOptInConfig(), fmt.Sprintf(` +data "aws_iam_policy_document" "rds_assume_role_policy" { + statement { + actions = ["sts:AssumeRole"] + effect = "Allow" + + principals { + identifiers = ["rds.amazonaws.com"] + type = "Service" + } + } +} + +resource "aws_iam_role" "test" { + assume_role_policy = data.aws_iam_policy_document.rds_assume_role_policy.json + name = %[1]q +} + +resource "aws_rds_cluster" "test" { + cluster_identifier = %[1]q + engine = "aurora-postgresql" + availability_zones = [data.aws_availability_zones.available.names[0], data.aws_availability_zones.available.names[1], data.aws_availability_zones.available.names[2]] + database_name = "mydb" + master_username = "foo" + master_password = "foobarfoobarfoobar" + backup_retention_period = 5 + preferred_backup_window = "07:00-09:00" + skip_final_snapshot = true +} + +resource "aws_db_cluster_role_association" "test" { + db_cluster_identifier = aws_rds_cluster.test.id + feature_name = "s3Import" + role_arn = aws_iam_role.test.arn +} +`, rName)) +} diff --git a/website/docs/r/db_cluster_role_association.html.markdown b/website/docs/r/db_cluster_role_association.html.markdown new file mode 100644 index 00000000000..2845c9d6821 --- /dev/null +++ b/website/docs/r/db_cluster_role_association.html.markdown @@ -0,0 +1,46 @@ +--- +subcategory: "RDS" +layout: "aws" +page_title: "AWS: aws_db_cluster_role_association" +description: |- + Manages a RDS DB Cluster association with an IAM Role. +--- + +# Resource: aws_db_cluster_role_association + +Manages a RDS DB Cluster association with an IAM Role. Example use cases: + +* [Creating an IAM Role to Allow Amazon Aurora to Access AWS Services](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraMySQL.Integrating.Authorizing.IAM.CreateRole.html) +* [Importing Amazon S3 Data into an RDS PostgreSQL DB Cluster](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_PostgreSQL.S3Import.html) + +## Example Usage + +```terraform +resource "aws_db_cluster_role_association" "example" { + db_cluster_identifier = "${aws_rds_cluster.example.id}" + feature_name = "S3_INTEGRATION" + role_arn = "${aws_iam_role.example.id}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `db_cluster_identifier` - (Required) DB Cluster Identifier to associate with the IAM Role. +* `feature_name` - (Required) Name of the feature for association. This can be found in the AWS documentation relevant to the integration or a full list is available in the `SupportedFeatureNames` list returned by [AWS CLI rds describe-db-engine-versions](https://docs.aws.amazon.com/cli/latest/reference/rds/describe-db-engine-versions.html). +* `role_arn` - (Required) Amazon Resource Name (ARN) of the IAM Role to associate with the DB Cluster. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - DB Cluster Identifier and IAM Role ARN separated by a comma (`,`) + +## Import + +`aws_db_cluster_role_association` can be imported using the DB Cluster Identifier and IAM Role ARN separated by a comma (`,`), e.g. + +``` +$ terraform import aws_db_cluster_role_association.example my-db-cluster,arn:aws:iam::123456789012:role/my-role +``` From 223edb168c7f20a3a2c81d1b63b14bc24c00b6a0 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 6 Jul 2021 09:54:46 -0400 Subject: [PATCH 141/398] 'aws_db_cluster_role_association' -> 'aws_rds_cluster_role_association'. --- aws/provider.go | 1 + ...ource_aws_rds_cluster_role_association.go} | 16 +++--- ..._aws_rds_cluster_role_association_test.go} | 57 ++++++------------- ...ds_cluster_role_association.html.markdown} | 14 ++--- 4 files changed, 33 insertions(+), 55 deletions(-) rename aws/{resource_aws_db_cluster_role_association.go => resource_aws_rds_cluster_role_association.go} (92%) rename aws/{resource_aws_db_cluster_role_association_test.go => resource_aws_rds_cluster_role_association_test.go} (68%) rename website/docs/r/{db_cluster_role_association.html.markdown => rds_cluster_role_association.html.markdown} (73%) diff --git a/aws/provider.go b/aws/provider.go index 1a20232f300..284c667ae22 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -952,6 +952,7 @@ func Provider() *schema.Provider { "aws_rds_cluster_endpoint": resourceAwsRDSClusterEndpoint(), "aws_rds_cluster_instance": resourceAwsRDSClusterInstance(), "aws_rds_cluster_parameter_group": resourceAwsRDSClusterParameterGroup(), + "aws_rds_cluster_role_association": resourceAwsRDSClusterRoleAssociation(), "aws_rds_global_cluster": resourceAwsRDSGlobalCluster(), "aws_redshift_cluster": resourceAwsRedshiftCluster(), "aws_redshift_security_group": resourceAwsRedshiftSecurityGroup(), diff --git a/aws/resource_aws_db_cluster_role_association.go b/aws/resource_aws_rds_cluster_role_association.go similarity index 92% rename from aws/resource_aws_db_cluster_role_association.go rename to aws/resource_aws_rds_cluster_role_association.go index 5137a3b0147..dfae74a59ec 100644 --- a/aws/resource_aws_db_cluster_role_association.go +++ b/aws/resource_aws_rds_cluster_role_association.go @@ -19,11 +19,11 @@ const ( rdsDbClusterRoleStatusPending = "PENDING" ) -func resourceAwsDbClusterRoleAssociation() *schema.Resource { +func resourceAwsRDSClusterRoleAssociation() *schema.Resource { return &schema.Resource{ - Create: resourceAwsDbClusterRoleAssociationCreate, - Read: resourceAwsDbClusterRoleAssociationRead, - Delete: resourceAwsDbClusterRoleAssociationDelete, + Create: resourceAwsRDSClusterRoleAssociationCreate, + Read: resourceAwsRDSClusterRoleAssociationRead, + Delete: resourceAwsRDSClusterRoleAssociationDelete, Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, @@ -50,7 +50,7 @@ func resourceAwsDbClusterRoleAssociation() *schema.Resource { } } -func resourceAwsDbClusterRoleAssociationCreate(d *schema.ResourceData, meta interface{}) error { +func resourceAwsRDSClusterRoleAssociationCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).rdsconn dbClusterIdentifier := d.Get("db_cluster_identifier").(string) @@ -75,10 +75,10 @@ func resourceAwsDbClusterRoleAssociationCreate(d *schema.ResourceData, meta inte return fmt.Errorf("error waiting for RDS DB Cluster (%s) IAM Role (%s) association: %s", dbClusterIdentifier, roleArn, err) } - return resourceAwsDbClusterRoleAssociationRead(d, meta) + return resourceAwsRDSClusterRoleAssociationRead(d, meta) } -func resourceAwsDbClusterRoleAssociationRead(d *schema.ResourceData, meta interface{}) error { +func resourceAwsRDSClusterRoleAssociationRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).rdsconn dbClusterIdentifier, roleArn, err := resourceAwsDbClusterRoleAssociationDecodeID(d.Id()) @@ -112,7 +112,7 @@ func resourceAwsDbClusterRoleAssociationRead(d *schema.ResourceData, meta interf return nil } -func resourceAwsDbClusterRoleAssociationDelete(d *schema.ResourceData, meta interface{}) error { +func resourceAwsRDSClusterRoleAssociationDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).rdsconn dbClusterIdentifier, roleArn, err := resourceAwsDbClusterRoleAssociationDecodeID(d.Id()) diff --git a/aws/resource_aws_db_cluster_role_association_test.go b/aws/resource_aws_rds_cluster_role_association_test.go similarity index 68% rename from aws/resource_aws_db_cluster_role_association_test.go rename to aws/resource_aws_rds_cluster_role_association_test.go index 93b1cb455cd..aeb904b1adb 100644 --- a/aws/resource_aws_db_cluster_role_association_test.go +++ b/aws/resource_aws_rds_cluster_role_association_test.go @@ -11,23 +11,23 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) -func TestAccAWSDbClusterRoleAssociation_basic(t *testing.T) { - var dbClusterRole1 rds.DBClusterRole +func TestAccAWSRDSClusterRoleAssociation_basic(t *testing.T) { + var dbClusterRole rds.DBClusterRole rName := acctest.RandomWithPrefix("tf-acc-test") dbClusterResourceName := "aws_rds_cluster.test" iamRoleResourceName := "aws_iam_role.test" - resourceName := "aws_db_cluster_role_association.test" + resourceName := "aws_rds_cluster_role_association.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, rds.EndpointsID), Providers: testAccProviders, - CheckDestroy: testAccCheckAWSDbClusterRoleAssociationDestroy, + CheckDestroy: testAccCheckAWSRDSClusterRoleAssociationDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSDbClusterRoleAssociationConfig(rName), + Config: testAccAWSRDSClusterRoleAssociationConfig(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSDbClusterRoleAssociationExists(resourceName, &dbClusterRole1), + testAccCheckAWSRDSClusterRoleAssociationExists(resourceName, &dbClusterRole), resource.TestCheckResourceAttrPair(resourceName, "db_cluster_identifier", dbClusterResourceName, "id"), resource.TestCheckResourceAttr(resourceName, "feature_name", "s3Import"), resource.TestCheckResourceAttrPair(resourceName, "role_arn", iamRoleResourceName, "arn"), @@ -42,25 +42,22 @@ func TestAccAWSDbClusterRoleAssociation_basic(t *testing.T) { }) } -func TestAccAWSDbClusterRoleAssociation_disappears(t *testing.T) { - var dbCluster1 rds.DBCluster - var dbClusterRole1 rds.DBClusterRole +func TestAccAWSRDSClusterRoleAssociation_disappears(t *testing.T) { + var dbClusterRole rds.DBClusterRole rName := acctest.RandomWithPrefix("tf-acc-test") - dbClusterResourceName := "aws_rds_cluster.test" - resourceName := "aws_db_cluster_role_association.test" + resourceName := "aws_rds_cluster_role_association.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, rds.EndpointsID), Providers: testAccProviders, - CheckDestroy: testAccCheckAWSDbClusterRoleAssociationDestroy, + CheckDestroy: testAccCheckAWSRDSClusterRoleAssociationDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSDbClusterRoleAssociationConfig(rName), + Config: testAccAWSRDSClusterRoleAssociationConfig(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSClusterExists(dbClusterResourceName, &dbCluster1), - testAccCheckAWSDbClusterRoleAssociationExists(resourceName, &dbClusterRole1), - testAccCheckAWSDbClusterRoleAssociationDisappears(&dbCluster1, &dbClusterRole1), + testAccCheckAWSRDSClusterRoleAssociationExists(resourceName, &dbClusterRole), + testAccCheckResourceDisappears(testAccProvider, resourceAwsRDSClusterRoleAssociation(), resourceName), ), ExpectNonEmptyPlan: true, }, @@ -68,7 +65,7 @@ func TestAccAWSDbClusterRoleAssociation_disappears(t *testing.T) { }) } -func testAccCheckAWSDbClusterRoleAssociationExists(resourceName string, dbClusterRole *rds.DBClusterRole) resource.TestCheckFunc { +func testAccCheckAWSRDSClusterRoleAssociationExists(resourceName string, dbClusterRole *rds.DBClusterRole) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] @@ -104,7 +101,7 @@ func testAccCheckAWSDbClusterRoleAssociationExists(resourceName string, dbCluste } } -func testAccCheckAWSDbClusterRoleAssociationDestroy(s *terraform.State) error { +func testAccCheckAWSRDSClusterRoleAssociationDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).rdsconn for _, rs := range s.RootModule().Resources { @@ -138,27 +135,7 @@ func testAccCheckAWSDbClusterRoleAssociationDestroy(s *terraform.State) error { return nil } -func testAccCheckAWSDbClusterRoleAssociationDisappears(dbCluster *rds.DBCluster, dbClusterRole *rds.DBClusterRole) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).rdsconn - - input := &rds.RemoveRoleFromDBClusterInput{ - DBClusterIdentifier: dbCluster.DBClusterIdentifier, - FeatureName: dbClusterRole.FeatureName, - RoleArn: dbClusterRole.RoleArn, - } - - _, err := conn.RemoveRoleFromDBCluster(input) - - if err != nil { - return err - } - - return waitForRdsDbClusterRoleDisassociation(conn, aws.StringValue(dbCluster.DBClusterIdentifier), aws.StringValue(dbClusterRole.RoleArn)) - } -} - -func testAccAWSDbClusterRoleAssociationConfig(rName string) string { +func testAccAWSRDSClusterRoleAssociationConfig(rName string) string { return composeConfig(testAccAvailableAZsNoOptInConfig(), fmt.Sprintf(` data "aws_iam_policy_document" "rds_assume_role_policy" { statement { @@ -189,7 +166,7 @@ resource "aws_rds_cluster" "test" { skip_final_snapshot = true } -resource "aws_db_cluster_role_association" "test" { +resource "aws_rds_cluster_role_association" "test" { db_cluster_identifier = aws_rds_cluster.test.id feature_name = "s3Import" role_arn = aws_iam_role.test.arn diff --git a/website/docs/r/db_cluster_role_association.html.markdown b/website/docs/r/rds_cluster_role_association.html.markdown similarity index 73% rename from website/docs/r/db_cluster_role_association.html.markdown rename to website/docs/r/rds_cluster_role_association.html.markdown index 2845c9d6821..8fbe9d16c87 100644 --- a/website/docs/r/db_cluster_role_association.html.markdown +++ b/website/docs/r/rds_cluster_role_association.html.markdown @@ -1,12 +1,12 @@ --- subcategory: "RDS" layout: "aws" -page_title: "AWS: aws_db_cluster_role_association" +page_title: "AWS: aws_rds_cluster_role_association" description: |- Manages a RDS DB Cluster association with an IAM Role. --- -# Resource: aws_db_cluster_role_association +# Resource: aws_rds_cluster_role_association Manages a RDS DB Cluster association with an IAM Role. Example use cases: @@ -16,10 +16,10 @@ Manages a RDS DB Cluster association with an IAM Role. Example use cases: ## Example Usage ```terraform -resource "aws_db_cluster_role_association" "example" { - db_cluster_identifier = "${aws_rds_cluster.example.id}" +resource "aws_rds_cluster_role_association" "example" { + db_cluster_identifier = aws_rds_cluster.example.id feature_name = "S3_INTEGRATION" - role_arn = "${aws_iam_role.example.id}" + role_arn = aws_iam_role.example.id } ``` @@ -39,8 +39,8 @@ In addition to all arguments above, the following attributes are exported: ## Import -`aws_db_cluster_role_association` can be imported using the DB Cluster Identifier and IAM Role ARN separated by a comma (`,`), e.g. +`aws_rds_cluster_role_association` can be imported using the DB Cluster Identifier and IAM Role ARN separated by a comma (`,`), e.g. ``` -$ terraform import aws_db_cluster_role_association.example my-db-cluster,arn:aws:iam::123456789012:role/my-role +$ terraform import aws_rds_cluster_role_association.example my-db-cluster,arn:aws:iam::123456789012:role/my-role ``` From 8057b4e80f92650f2d3e4e38e18bfcc51449a918 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 6 Jul 2021 09:55:00 -0400 Subject: [PATCH 142/398] Add CHANGELOG entry. --- .changelog/12370.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/12370.txt diff --git a/.changelog/12370.txt b/.changelog/12370.txt new file mode 100644 index 00000000000..82be242611f --- /dev/null +++ b/.changelog/12370.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_rds_cluster_role_association +``` \ No newline at end of file From d623472f416b4c6d72f13d70e8fbd3d7f0b98923 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 6 Jul 2021 10:49:34 -0400 Subject: [PATCH 143/398] Use internal finder and waiter packages. --- aws/internal/service/rds/enum.go | 7 + aws/internal/service/rds/finder/finder.go | 48 +++++ aws/internal/service/rds/id.go | 19 ++ aws/internal/service/rds/waiter/status.go | 17 ++ aws/internal/service/rds/waiter/waiter.go | 38 ++++ ...source_aws_rds_cluster_role_association.go | 186 ++++-------------- ...e_aws_rds_cluster_role_association_test.go | 38 ++-- 7 files changed, 178 insertions(+), 175 deletions(-) create mode 100644 aws/internal/service/rds/enum.go diff --git a/aws/internal/service/rds/enum.go b/aws/internal/service/rds/enum.go new file mode 100644 index 00000000000..84531413bf2 --- /dev/null +++ b/aws/internal/service/rds/enum.go @@ -0,0 +1,7 @@ +package rds + +const ( + DBClusterRoleStatusActive = "ACTIVE" + DBClusterRoleStatusDeleted = "DELETED" + DBClusterRoleStatusPending = "PENDING" +) diff --git a/aws/internal/service/rds/finder/finder.go b/aws/internal/service/rds/finder/finder.go index 8ef227f7099..76a5f33675d 100644 --- a/aws/internal/service/rds/finder/finder.go +++ b/aws/internal/service/rds/finder/finder.go @@ -3,6 +3,8 @@ package finder import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/rds" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" tfrds "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/rds" ) @@ -63,3 +65,49 @@ func DBProxyEndpoint(conn *rds.RDS, id string) (*rds.DBProxyEndpoint, error) { return dbProxyEndpoint, err } + +func DBClusterRoleByDBClusterIDAndRoleARN(conn *rds.RDS, dbClusterID, roleARN string) (*rds.DBClusterRole, error) { + dbCluster, err := DBClusterByID(conn, dbClusterID) + + if err != nil { + return nil, err + } + + for _, associatedRole := range dbCluster.AssociatedRoles { + if aws.StringValue(associatedRole.RoleArn) == roleARN { + if status := aws.StringValue(associatedRole.Status); status == tfrds.DBClusterRoleStatusDeleted { + return nil, &resource.NotFoundError{ + Message: status, + } + } + + return associatedRole, nil + } + } + + return nil, &resource.NotFoundError{} +} + +func DBClusterByID(conn *rds.RDS, id string) (*rds.DBCluster, error) { + input := &rds.DescribeDBClustersInput{ + DBClusterIdentifier: aws.String(id), + } + + output, err := conn.DescribeDBClusters(input) + + if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBClusterNotFoundFault) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if output == nil || len(output.DBClusters) == 0 || output.DBClusters[0] == nil { + return nil, &resource.NotFoundError{ + Message: "Empty result", + LastRequest: input, + } + } + + return output.DBClusters[0], nil +} diff --git a/aws/internal/service/rds/id.go b/aws/internal/service/rds/id.go index f8a8250f33a..b0ac3c52bb4 100644 --- a/aws/internal/service/rds/id.go +++ b/aws/internal/service/rds/id.go @@ -12,3 +12,22 @@ func ResourceAwsDbProxyEndpointParseID(id string) (string, string, error) { } return idParts[0], idParts[1], nil } + +const dbClusterRoleAssociationResourceIDSeparator = "," + +func DBClusterRoleAssociationCreateResourceID(dbClusterID, roleARN string) string { + parts := []string{dbClusterID, roleARN} + id := strings.Join(parts, dbClusterRoleAssociationResourceIDSeparator) + + return id +} + +func DBClusterRoleAssociationParseResourceID(id string) (string, string, error) { + parts := strings.Split(id, dbClusterRoleAssociationResourceIDSeparator) + + if len(parts) == 2 && parts[0] != "" && parts[1] != "" { + return parts[0], parts[1], nil + } + + return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected DBCLUSTERID%[2]sROLEARN", id, dbClusterRoleAssociationResourceIDSeparator) +} diff --git a/aws/internal/service/rds/waiter/status.go b/aws/internal/service/rds/waiter/status.go index 59fa5e5b5e4..870f1b58e8e 100644 --- a/aws/internal/service/rds/waiter/status.go +++ b/aws/internal/service/rds/waiter/status.go @@ -5,6 +5,7 @@ import ( "github.com/aws/aws-sdk-go/service/rds" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/rds/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) const ( @@ -58,3 +59,19 @@ func DBProxyEndpointStatus(conn *rds.RDS, id string) resource.StateRefreshFunc { return output, aws.StringValue(output.Status), nil } } + +func DBClusterRoleStatus(conn *rds.RDS, dbClusterID, roleARN string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := finder.DBClusterRoleByDBClusterIDAndRoleARN(conn, dbClusterID, roleARN) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, aws.StringValue(output.Status), nil + } +} diff --git a/aws/internal/service/rds/waiter/waiter.go b/aws/internal/service/rds/waiter/waiter.go index 2e80027be93..d3ad5da52d0 100644 --- a/aws/internal/service/rds/waiter/waiter.go +++ b/aws/internal/service/rds/waiter/waiter.go @@ -5,12 +5,16 @@ import ( "github.com/aws/aws-sdk-go/service/rds" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + tfrds "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/rds" ) const ( // Maximum amount of time to wait for an EventSubscription to return Deleted EventSubscriptionDeletedTimeout = 10 * time.Minute RdsClusterInitiateUpgradeTimeout = 5 * time.Minute + + DBClusterRoleAssociationCreatedTimeout = 5 * time.Minute + DBClusterRoleAssociationDeletedTimeout = 5 * time.Minute ) // EventSubscriptionDeleted waits for a EventSubscription to return Deleted @@ -69,3 +73,37 @@ func DBProxyEndpointDeleted(conn *rds.RDS, id string, timeout time.Duration) (*r return nil, err } + +func DBClusterRoleAssociationCreated(conn *rds.RDS, dbClusterID, roleARN string) (*rds.DBClusterRole, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{tfrds.DBClusterRoleStatusPending}, + Target: []string{tfrds.DBClusterRoleStatusActive}, + Refresh: DBClusterRoleStatus(conn, dbClusterID, roleARN), + Timeout: DBClusterRoleAssociationCreatedTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*rds.DBClusterRole); ok { + return output, err + } + + return nil, err +} + +func DBClusterRoleAssociationDeleted(conn *rds.RDS, dbClusterID, roleARN string) (*rds.DBClusterRole, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{tfrds.DBClusterRoleStatusActive, tfrds.DBClusterRoleStatusPending}, + Target: []string{}, + Refresh: DBClusterRoleStatus(conn, dbClusterID, roleARN), + Timeout: DBClusterRoleAssociationDeletedTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*rds.DBClusterRole); ok { + return output, err + } + + return nil, err +} diff --git a/aws/resource_aws_rds_cluster_role_association.go b/aws/resource_aws_rds_cluster_role_association.go index dfae74a59ec..322cdae4422 100644 --- a/aws/resource_aws_rds_cluster_role_association.go +++ b/aws/resource_aws_rds_cluster_role_association.go @@ -3,20 +3,15 @@ package aws import ( "fmt" "log" - "strings" - "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/rds" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -// Constants not currently provided by the AWS Go SDK -const ( - rdsDbClusterRoleStatusActive = "ACTIVE" - rdsDbClusterRoleStatusDeleted = "DELETED" - rdsDbClusterRoleStatusPending = "PENDING" + tfrds "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/rds" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/rds/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/rds/waiter" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func resourceAwsRDSClusterRoleAssociation() *schema.Resource { @@ -53,26 +48,27 @@ func resourceAwsRDSClusterRoleAssociation() *schema.Resource { func resourceAwsRDSClusterRoleAssociationCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).rdsconn - dbClusterIdentifier := d.Get("db_cluster_identifier").(string) - roleArn := d.Get("role_arn").(string) - + dbClusterID := d.Get("db_cluster_identifier").(string) + roleARN := d.Get("role_arn").(string) input := &rds.AddRoleToDBClusterInput{ - DBClusterIdentifier: aws.String(dbClusterIdentifier), + DBClusterIdentifier: aws.String(dbClusterID), FeatureName: aws.String(d.Get("feature_name").(string)), - RoleArn: aws.String(roleArn), + RoleArn: aws.String(roleARN), } - log.Printf("[DEBUG] RDS DB Cluster (%s) IAM Role associating: %s", dbClusterIdentifier, roleArn) + log.Printf("[DEBUG] Creating RDS DB Cluster IAM Role Association: %s", input) _, err := conn.AddRoleToDBCluster(input) if err != nil { - return fmt.Errorf("error associating RDS DB Cluster (%s) IAM Role (%s): %s", dbClusterIdentifier, roleArn, err) + return fmt.Errorf("error creating RDS DB Cluster (%s) IAM Role (%s) Association: %w", dbClusterID, roleARN, err) } - d.SetId(fmt.Sprintf("%s,%s", dbClusterIdentifier, roleArn)) + d.SetId(tfrds.DBClusterRoleAssociationCreateResourceID(dbClusterID, roleARN)) + + _, err = waiter.DBClusterRoleAssociationCreated(conn, dbClusterID, roleARN) - if err := waitForRdsDbClusterRoleAssociation(conn, dbClusterIdentifier, roleArn); err != nil { - return fmt.Errorf("error waiting for RDS DB Cluster (%s) IAM Role (%s) association: %s", dbClusterIdentifier, roleArn, err) + if err != nil { + return fmt.Errorf("error waiting for RDS DB Cluster (%s) IAM Role (%s) Association to create: %w", dbClusterID, roleARN, err) } return resourceAwsRDSClusterRoleAssociationRead(d, meta) @@ -81,33 +77,27 @@ func resourceAwsRDSClusterRoleAssociationCreate(d *schema.ResourceData, meta int func resourceAwsRDSClusterRoleAssociationRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).rdsconn - dbClusterIdentifier, roleArn, err := resourceAwsDbClusterRoleAssociationDecodeID(d.Id()) + dbClusterID, roleARN, err := tfrds.DBClusterRoleAssociationParseResourceID(d.Id()) if err != nil { - return fmt.Errorf("error reading resource ID: %s", err) + return fmt.Errorf("error parsing RDS DB Cluster IAM Role Association ID: %s", err) } - dbClusterRole, err := rdsDescribeDbClusterRole(conn, dbClusterIdentifier, roleArn) + output, err := finder.DBClusterRoleByDBClusterIDAndRoleARN(conn, dbClusterID, roleARN) - if isAWSErr(err, rds.ErrCodeDBClusterNotFoundFault, "") { - log.Printf("[WARN] RDS DB Cluster (%s) not found, removing from state", dbClusterIdentifier) + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] RDS DB Cluster (%s) IAM Role (%s) Association not found, removing from state", dbClusterID, roleARN) d.SetId("") return nil } if err != nil { - return fmt.Errorf("error reading RDS DB Cluster (%s) IAM Role (%s) association: %s", dbClusterIdentifier, roleArn, err) + return fmt.Errorf("error reading RDS DB Cluster (%s) IAM Role (%s) Association: %w", dbClusterID, roleARN, err) } - if dbClusterRole == nil { - log.Printf("[WARN] RDS DB Cluster (%s) IAM Role (%s) association not found, removing from state", dbClusterIdentifier, roleArn) - d.SetId("") - return nil - } - - d.Set("db_cluster_identifier", dbClusterIdentifier) - d.Set("feature_name", dbClusterRole.FeatureName) - d.Set("role_arn", dbClusterRole.RoleArn) + d.Set("db_cluster_identifier", dbClusterID) + d.Set("feature_name", output.FeatureName) + d.Set("role_arn", output.RoleArn) return nil } @@ -115,140 +105,34 @@ func resourceAwsRDSClusterRoleAssociationRead(d *schema.ResourceData, meta inter func resourceAwsRDSClusterRoleAssociationDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).rdsconn - dbClusterIdentifier, roleArn, err := resourceAwsDbClusterRoleAssociationDecodeID(d.Id()) + dbClusterID, roleARN, err := tfrds.DBClusterRoleAssociationParseResourceID(d.Id()) if err != nil { - return fmt.Errorf("error reading resource ID: %s", err) + return fmt.Errorf("error parsing RDS DB Cluster IAM Role Association ID: %s", err) } input := &rds.RemoveRoleFromDBClusterInput{ - DBClusterIdentifier: aws.String(dbClusterIdentifier), + DBClusterIdentifier: aws.String(dbClusterID), FeatureName: aws.String(d.Get("feature_name").(string)), - RoleArn: aws.String(roleArn), + RoleArn: aws.String(roleARN), } - log.Printf("[DEBUG] RDS DB Cluster (%s) IAM Role disassociating: %s", dbClusterIdentifier, roleArn) + log.Printf("[DEBUG] Deleting RDS DB Cluster IAM Role Association: %s", d.Id()) _, err = conn.RemoveRoleFromDBCluster(input) - if isAWSErr(err, rds.ErrCodeDBClusterNotFoundFault, "") { - return nil - } - - if isAWSErr(err, rds.ErrCodeDBClusterRoleNotFoundFault, "") { + if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBClusterNotFoundFault) || tfawserr.ErrCodeEquals(err, rds.ErrCodeDBClusterRoleNotFoundFault) { return nil } if err != nil { - return fmt.Errorf("error disassociating RDS DB Cluster (%s) IAM Role (%s): %s", dbClusterIdentifier, roleArn, err) - } - - if err := waitForRdsDbClusterRoleDisassociation(conn, dbClusterIdentifier, roleArn); err != nil { - return fmt.Errorf("error waiting for RDS DB Cluster (%s) IAM Role (%s) disassociation: %s", dbClusterIdentifier, roleArn, err) + return fmt.Errorf("error deleting RDS DB Cluster (%s) IAM Role (%s) Association: %w", dbClusterID, roleARN, err) } - return nil -} - -func resourceAwsDbClusterRoleAssociationDecodeID(id string) (string, string, error) { - parts := strings.SplitN(id, ",", 2) - - if len(parts) != 2 || parts[0] == "" || parts[1] == "" { - return "", "", fmt.Errorf("unexpected format of ID (%s), expected DB-CLUSTER-ID,ROLE-ARN", id) - } - - return parts[0], parts[1], nil -} - -func rdsDescribeDbClusterRole(conn *rds.RDS, dbClusterIdentifier, roleArn string) (*rds.DBClusterRole, error) { - input := &rds.DescribeDBClustersInput{ - DBClusterIdentifier: aws.String(dbClusterIdentifier), - } - - log.Printf("[DEBUG] Describing RDS DB Cluster: %s", input) - output, err := conn.DescribeDBClusters(input) + _, err = waiter.DBClusterRoleAssociationDeleted(conn, dbClusterID, roleARN) if err != nil { - return nil, err - } - - var dbCluster *rds.DBCluster - - for _, outputDbCluster := range output.DBClusters { - if aws.StringValue(outputDbCluster.DBClusterIdentifier) == dbClusterIdentifier { - dbCluster = outputDbCluster - break - } - } - - if dbCluster == nil { - return nil, nil - } - - var dbClusterRole *rds.DBClusterRole - - for _, associatedRole := range dbCluster.AssociatedRoles { - if aws.StringValue(associatedRole.RoleArn) == roleArn { - dbClusterRole = associatedRole - break - } - } - - return dbClusterRole, nil -} - -func waitForRdsDbClusterRoleAssociation(conn *rds.RDS, dbClusterIdentifier, roleArn string) error { - stateConf := &resource.StateChangeConf{ - Pending: []string{rdsDbClusterRoleStatusPending}, - Target: []string{rdsDbClusterRoleStatusActive}, - Refresh: func() (interface{}, string, error) { - dbClusterRole, err := rdsDescribeDbClusterRole(conn, dbClusterIdentifier, roleArn) - - if err != nil { - return nil, "", err - } - - return dbClusterRole, aws.StringValue(dbClusterRole.Status), nil - }, - Timeout: 5 * time.Minute, - Delay: 5 * time.Second, + return fmt.Errorf("error waiting for RDS DB Cluster (%s) IAM Role (%s) Association to delete: %w", dbClusterID, roleARN, err) } - log.Printf("[DEBUG] Waiting for RDS DB Cluster (%s) IAM Role association: %s", dbClusterIdentifier, roleArn) - _, err := stateConf.WaitForState() - - return err -} - -func waitForRdsDbClusterRoleDisassociation(conn *rds.RDS, dbClusterIdentifier, roleArn string) error { - stateConf := &resource.StateChangeConf{ - Pending: []string{ - rdsDbClusterRoleStatusActive, - rdsDbClusterRoleStatusPending, - }, - Target: []string{rdsDbClusterRoleStatusDeleted}, - Refresh: func() (interface{}, string, error) { - dbClusterRole, err := rdsDescribeDbClusterRole(conn, dbClusterIdentifier, roleArn) - - if isAWSErr(err, rds.ErrCodeDBClusterNotFoundFault, "") { - return &rds.DBClusterRole{}, rdsDbClusterRoleStatusDeleted, nil - } - - if err != nil { - return nil, "", err - } - - if dbClusterRole != nil { - return dbClusterRole, aws.StringValue(dbClusterRole.Status), nil - } - - return &rds.DBClusterRole{}, rdsDbClusterRoleStatusDeleted, nil - }, - Timeout: 5 * time.Minute, - Delay: 5 * time.Second, - } - - log.Printf("[DEBUG] Waiting for RDS DB Cluster (%s) IAM Role disassociation: %s", dbClusterIdentifier, roleArn) - _, err := stateConf.WaitForState() - - return err + return nil } diff --git a/aws/resource_aws_rds_cluster_role_association_test.go b/aws/resource_aws_rds_cluster_role_association_test.go index aeb904b1adb..988077e8c44 100644 --- a/aws/resource_aws_rds_cluster_role_association_test.go +++ b/aws/resource_aws_rds_cluster_role_association_test.go @@ -4,11 +4,13 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/rds" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + tfrds "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/rds" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/rds/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func TestAccAWSRDSClusterRoleAssociation_basic(t *testing.T) { @@ -65,37 +67,29 @@ func TestAccAWSRDSClusterRoleAssociation_disappears(t *testing.T) { }) } -func testAccCheckAWSRDSClusterRoleAssociationExists(resourceName string, dbClusterRole *rds.DBClusterRole) resource.TestCheckFunc { +func testAccCheckAWSRDSClusterRoleAssociationExists(resourceName string, v *rds.DBClusterRole) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] if !ok { - return fmt.Errorf("Resource not found: %s", resourceName) + return fmt.Errorf("Not found: %s", resourceName) } - dbClusterIdentifier, roleArn, err := resourceAwsDbClusterRoleAssociationDecodeID(rs.Primary.ID) + dbClusterID, roleARN, err := tfrds.DBClusterRoleAssociationParseResourceID(rs.Primary.ID) if err != nil { - return fmt.Errorf("error reading resource ID: %s", err) + return err } conn := testAccProvider.Meta().(*AWSClient).rdsconn - role, err := rdsDescribeDbClusterRole(conn, dbClusterIdentifier, roleArn) + role, err := finder.DBClusterRoleByDBClusterIDAndRoleARN(conn, dbClusterID, roleARN) if err != nil { return err } - if role == nil { - return fmt.Errorf("RDS DB Cluster IAM Role Association not found") - } - - if aws.StringValue(role.Status) != "ACTIVE" { - return fmt.Errorf("RDS DB Cluster (%s) IAM Role (%s) association exists in non-ACTIVE (%s) state", dbClusterIdentifier, roleArn, aws.StringValue(role.Status)) - } - - *dbClusterRole = *role + *v = *role return nil } @@ -109,15 +103,15 @@ func testAccCheckAWSRDSClusterRoleAssociationDestroy(s *terraform.State) error { continue } - dbClusterIdentifier, roleArn, err := resourceAwsDbClusterRoleAssociationDecodeID(rs.Primary.ID) + dbClusterID, roleARN, err := tfrds.DBClusterRoleAssociationParseResourceID(rs.Primary.ID) if err != nil { - return fmt.Errorf("error reading resource ID: %s", err) + return err } - dbClusterRole, err := rdsDescribeDbClusterRole(conn, dbClusterIdentifier, roleArn) + _, err = finder.DBClusterRoleByDBClusterIDAndRoleARN(conn, dbClusterID, roleARN) - if isAWSErr(err, rds.ErrCodeDBClusterNotFoundFault, "") { + if tfresource.NotFound(err) { continue } @@ -125,11 +119,7 @@ func testAccCheckAWSRDSClusterRoleAssociationDestroy(s *terraform.State) error { return err } - if dbClusterRole == nil { - continue - } - - return fmt.Errorf("RDS DB Cluster (%s) IAM Role (%s) association still exists in non-deleted (%s) state", dbClusterIdentifier, roleArn, aws.StringValue(dbClusterRole.Status)) + return fmt.Errorf("RDS DB Cluster IAM Role Association %s still exists", rs.Primary.ID) } return nil From 9b61333318cb2bc3e5a38a2ba970444c319d494b Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 6 Jul 2021 11:06:35 -0400 Subject: [PATCH 144/398] r/aws_rds_cluster_role_association: Additional acceptance tests. --- aws/internal/service/rds/finder/finder.go | 11 +++- aws/internal/service/rds/id.go | 12 ++--- aws/resource_aws_rds_cluster.go | 1 + ...source_aws_rds_cluster_role_association.go | 6 +-- ...e_aws_rds_cluster_role_association_test.go | 52 ++++++++++++++++++- 5 files changed, 70 insertions(+), 12 deletions(-) diff --git a/aws/internal/service/rds/finder/finder.go b/aws/internal/service/rds/finder/finder.go index 76a5f33675d..a401d39b5b2 100644 --- a/aws/internal/service/rds/finder/finder.go +++ b/aws/internal/service/rds/finder/finder.go @@ -109,5 +109,14 @@ func DBClusterByID(conn *rds.RDS, id string) (*rds.DBCluster, error) { } } - return output.DBClusters[0], nil + dbCluster := output.DBClusters[0] + + // Eventual consistency check. + if aws.StringValue(dbCluster.DBClusterIdentifier) != id { + return nil, &resource.NotFoundError{ + LastRequest: input, + } + } + + return dbCluster, nil } diff --git a/aws/internal/service/rds/id.go b/aws/internal/service/rds/id.go index b0ac3c52bb4..74e74c85784 100644 --- a/aws/internal/service/rds/id.go +++ b/aws/internal/service/rds/id.go @@ -13,21 +13,21 @@ func ResourceAwsDbProxyEndpointParseID(id string) (string, string, error) { return idParts[0], idParts[1], nil } -const dbClusterRoleAssociationResourceIDSeparator = "," +const clusterRoleAssociationResourceIDSeparator = "," -func DBClusterRoleAssociationCreateResourceID(dbClusterID, roleARN string) string { +func ClusterRoleAssociationCreateResourceID(dbClusterID, roleARN string) string { parts := []string{dbClusterID, roleARN} - id := strings.Join(parts, dbClusterRoleAssociationResourceIDSeparator) + id := strings.Join(parts, clusterRoleAssociationResourceIDSeparator) return id } -func DBClusterRoleAssociationParseResourceID(id string) (string, string, error) { - parts := strings.Split(id, dbClusterRoleAssociationResourceIDSeparator) +func ClusterRoleAssociationParseResourceID(id string) (string, string, error) { + parts := strings.Split(id, clusterRoleAssociationResourceIDSeparator) if len(parts) == 2 && parts[0] != "" && parts[1] != "" { return parts[0], parts[1], nil } - return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected DBCLUSTERID%[2]sROLEARN", id, dbClusterRoleAssociationResourceIDSeparator) + return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected DBCLUSTERID%[2]sROLEARN", id, clusterRoleAssociationResourceIDSeparator) } diff --git a/aws/resource_aws_rds_cluster.go b/aws/resource_aws_rds_cluster.go index bb54eb5533b..7fbac5d135d 100644 --- a/aws/resource_aws_rds_cluster.go +++ b/aws/resource_aws_rds_cluster.go @@ -415,6 +415,7 @@ func resourceAwsRDSCluster() *schema.Resource { "iam_roles": { Type: schema.TypeSet, Optional: true, + Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, Set: schema.HashString, }, diff --git a/aws/resource_aws_rds_cluster_role_association.go b/aws/resource_aws_rds_cluster_role_association.go index 322cdae4422..a73fa2e7a17 100644 --- a/aws/resource_aws_rds_cluster_role_association.go +++ b/aws/resource_aws_rds_cluster_role_association.go @@ -63,7 +63,7 @@ func resourceAwsRDSClusterRoleAssociationCreate(d *schema.ResourceData, meta int return fmt.Errorf("error creating RDS DB Cluster (%s) IAM Role (%s) Association: %w", dbClusterID, roleARN, err) } - d.SetId(tfrds.DBClusterRoleAssociationCreateResourceID(dbClusterID, roleARN)) + d.SetId(tfrds.ClusterRoleAssociationCreateResourceID(dbClusterID, roleARN)) _, err = waiter.DBClusterRoleAssociationCreated(conn, dbClusterID, roleARN) @@ -77,7 +77,7 @@ func resourceAwsRDSClusterRoleAssociationCreate(d *schema.ResourceData, meta int func resourceAwsRDSClusterRoleAssociationRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).rdsconn - dbClusterID, roleARN, err := tfrds.DBClusterRoleAssociationParseResourceID(d.Id()) + dbClusterID, roleARN, err := tfrds.ClusterRoleAssociationParseResourceID(d.Id()) if err != nil { return fmt.Errorf("error parsing RDS DB Cluster IAM Role Association ID: %s", err) @@ -105,7 +105,7 @@ func resourceAwsRDSClusterRoleAssociationRead(d *schema.ResourceData, meta inter func resourceAwsRDSClusterRoleAssociationDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).rdsconn - dbClusterID, roleARN, err := tfrds.DBClusterRoleAssociationParseResourceID(d.Id()) + dbClusterID, roleARN, err := tfrds.ClusterRoleAssociationParseResourceID(d.Id()) if err != nil { return fmt.Errorf("error parsing RDS DB Cluster IAM Role Association ID: %s", err) diff --git a/aws/resource_aws_rds_cluster_role_association_test.go b/aws/resource_aws_rds_cluster_role_association_test.go index 988077e8c44..71602b30150 100644 --- a/aws/resource_aws_rds_cluster_role_association_test.go +++ b/aws/resource_aws_rds_cluster_role_association_test.go @@ -67,6 +67,54 @@ func TestAccAWSRDSClusterRoleAssociation_disappears(t *testing.T) { }) } +func TestAccAWSRDSClusterRoleAssociation_disappears_cluster(t *testing.T) { + var dbClusterRole rds.DBClusterRole + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_rds_cluster_role_association.test" + clusterResourceName := "aws_rds_cluster.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, rds.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSRDSClusterRoleAssociationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSRDSClusterRoleAssociationConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSRDSClusterRoleAssociationExists(resourceName, &dbClusterRole), + testAccCheckResourceDisappears(testAccProvider, resourceAwsRDSCluster(), clusterResourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAWSRDSClusterRoleAssociation_disappears_role(t *testing.T) { + var dbClusterRole rds.DBClusterRole + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_rds_cluster_role_association.test" + roleResourceName := "aws_iam_role.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, rds.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSRDSClusterRoleAssociationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSRDSClusterRoleAssociationConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSRDSClusterRoleAssociationExists(resourceName, &dbClusterRole), + testAccCheckResourceDisappears(testAccProvider, resourceAwsIamRole(), roleResourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + func testAccCheckAWSRDSClusterRoleAssociationExists(resourceName string, v *rds.DBClusterRole) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] @@ -75,7 +123,7 @@ func testAccCheckAWSRDSClusterRoleAssociationExists(resourceName string, v *rds. return fmt.Errorf("Not found: %s", resourceName) } - dbClusterID, roleARN, err := tfrds.DBClusterRoleAssociationParseResourceID(rs.Primary.ID) + dbClusterID, roleARN, err := tfrds.ClusterRoleAssociationParseResourceID(rs.Primary.ID) if err != nil { return err @@ -103,7 +151,7 @@ func testAccCheckAWSRDSClusterRoleAssociationDestroy(s *terraform.State) error { continue } - dbClusterID, roleARN, err := tfrds.DBClusterRoleAssociationParseResourceID(rs.Primary.ID) + dbClusterID, roleARN, err := tfrds.ClusterRoleAssociationParseResourceID(rs.Primary.ID) if err != nil { return err From 7bd40b65afe076444940fdb892f1469570240002 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 6 Jul 2021 11:19:46 -0400 Subject: [PATCH 145/398] Documentation udpdate. Note that RDS Cluster IAM Role Associations can now be managed via either the aws_rds_cluster.iam_roles attribute or the aws_rds_cluster_role_association resource, not both. --- .changelog/12370.txt | 4 ++++ website/docs/r/rds_cluster.html.markdown | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/.changelog/12370.txt b/.changelog/12370.txt index 82be242611f..9a39c9e4cb6 100644 --- a/.changelog/12370.txt +++ b/.changelog/12370.txt @@ -1,3 +1,7 @@ ```release-note:new-resource aws_rds_cluster_role_association +``` + +```release-note:enhancement +aws_rds_cluster: Set `iam_roles` as Computed to prevent drift when the `aws_rds_cluster_role_association` resource is used ``` \ No newline at end of file diff --git a/website/docs/r/rds_cluster.html.markdown b/website/docs/r/rds_cluster.html.markdown index 82bd7cb7d4c..31b3548087b 100644 --- a/website/docs/r/rds_cluster.html.markdown +++ b/website/docs/r/rds_cluster.html.markdown @@ -28,6 +28,11 @@ for more information. ~> **Note:** All arguments including the username and password will be stored in the raw state as plain-text. [Read more about sensitive data in state](https://www.terraform.io/docs/state/sensitive-data.html). +~> **NOTE on RDS Clusters and RDS Cluster Role Associations:** Terraform provides both a standalone [RDS Cluster Role Association](rds_cluster_role_association.html) - (an association between an RDS Cluster and a single IAM Role) and +an RDS Cluster resource with `iam_roles` attributes. +Use one resource or the other to associate IAM Roles and RDS Clusters. +Not doing so will cause a conflict of associations and will result in the association being overwritten. + ## Example Usage ### Aurora MySQL 2.x (MySQL 5.7) From ef35c30c06fecdf98475cc4100822c4c806fd39b Mon Sep 17 00:00:00 2001 From: changelogbot Date: Tue, 6 Jul 2021 15:44:35 +0000 Subject: [PATCH 146/398] Update CHANGELOG.md (Manual Trigger) --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b7731a01b7..daf1257a6f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,14 @@ ## 3.49.0 (Unreleased) + +FEATURES: + +* **New Resource:** `aws_rds_cluster_role_association` ([#12370](https://github.com/hashicorp/terraform-provider-aws/issues/12370)) + +ENHANCEMENTS: + +* aws_rds_cluster: Set `iam_roles` as Computed to prevent drift when the `aws_rds_cluster_role_association` resource is used ([#12370](https://github.com/hashicorp/terraform-provider-aws/issues/12370)) +* resource/aws_transfer_server: Add `security_group_ids` argument to `endpoint_details` configuration block. ([#17539](https://github.com/hashicorp/terraform-provider-aws/issues/17539)) + ## 3.48.0 (July 02, 2021) FEATURES: From 8edd03acddbe6df19eee76341a03fb14cbc020ab Mon Sep 17 00:00:00 2001 From: kuritonasu <33165903+kuritonasu@users.noreply.github.com> Date: Wed, 7 Jul 2021 15:38:18 +0300 Subject: [PATCH 147/398] docs: removed duplicate example code snippet --- website/docs/r/transfer_server.html.markdown | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/website/docs/r/transfer_server.html.markdown b/website/docs/r/transfer_server.html.markdown index 2b754ab8544..b226803b975 100644 --- a/website/docs/r/transfer_server.html.markdown +++ b/website/docs/r/transfer_server.html.markdown @@ -24,16 +24,6 @@ resource "aws_transfer_server" "example" { } ``` -### Basic - -```terraform -resource "aws_transfer_server" "example" { - tags = { - Name = "Example" - } -} -``` - ### Security Policy Name ```terraform From c7afaa8e68797b504b049e2a3256acd33ec694bf Mon Sep 17 00:00:00 2001 From: kuritonasu <33165903+kuritonasu@users.noreply.github.com> Date: Wed, 7 Jul 2021 16:10:37 +0300 Subject: [PATCH 148/398] docs: removed duplicate example code snippet (Security Policy Name) --- website/docs/r/transfer_server.html.markdown | 8 -------- 1 file changed, 8 deletions(-) diff --git a/website/docs/r/transfer_server.html.markdown b/website/docs/r/transfer_server.html.markdown index b226803b975..c72fa091dc1 100644 --- a/website/docs/r/transfer_server.html.markdown +++ b/website/docs/r/transfer_server.html.markdown @@ -32,14 +32,6 @@ resource "aws_transfer_server" "example" { } ``` -### Security Policy Name - -```terraform -resource "aws_transfer_server" "example" { - security_policy_name = "TransferSecurityPolicy-2020-06" -} -``` - ### VPC Endpoint ```terraform From c2f28d02e8a2e480cb353d2fd12fadbb5b2fe372 Mon Sep 17 00:00:00 2001 From: Will Varney Date: Tue, 2 Mar 2021 10:29:00 +0000 Subject: [PATCH 149/398] New resource: aws_eks_identity_provider_config. This adds a new resource for associating an identity provider with an EKS cluster. This commit implements only the required fields. --- aws/provider.go | 1 + ...source_aws_eks_identity_provider_config.go | 312 ++++++++++++++++++ ...e_aws_eks_identity_provider_config_test.go | 276 ++++++++++++++++ 3 files changed, 589 insertions(+) create mode 100644 aws/resource_aws_eks_identity_provider_config.go create mode 100644 aws/resource_aws_eks_identity_provider_config_test.go diff --git a/aws/provider.go b/aws/provider.go index 284c667ae22..8364ac2b7cc 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -729,6 +729,7 @@ func Provider() *schema.Provider { "aws_eks_cluster": resourceAwsEksCluster(), "aws_eks_addon": resourceAwsEksAddon(), "aws_eks_fargate_profile": resourceAwsEksFargateProfile(), + "aws_eks_identity_provider_config": resourceAwsEksIdentityProviderConfig(), "aws_eks_node_group": resourceAwsEksNodeGroup(), "aws_elasticache_cluster": resourceAwsElasticacheCluster(), "aws_elasticache_global_replication_group": resourceAwsElasticacheGlobalReplicationGroup(), diff --git a/aws/resource_aws_eks_identity_provider_config.go b/aws/resource_aws_eks_identity_provider_config.go new file mode 100644 index 00000000000..49aa3cd9499 --- /dev/null +++ b/aws/resource_aws_eks_identity_provider_config.go @@ -0,0 +1,312 @@ +package aws + +import ( + "context" + "fmt" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/eks" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "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" + "log" + "strings" + "time" +) + +const typeOidc string = "oidc" + +func resourceAwsEksIdentityProviderConfig() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceAwsEksIdentityProviderConfigCreate, + ReadContext: resourceAwsEksIdentityProviderConfigRead, + DeleteContext: resourceAwsEksIdentityProviderConfigDelete, + + // TODO - Not sure if I can enable importing? + + // TODO - AWS is suggesting it can take 15-20 minutes to associate an identity provider with EKS. Seeing `context deadline exceeded`. + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(25 * time.Minute), + Delete: schema.DefaultTimeout(25 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "cluster_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.NoZeroValues, + }, + // TODO - Do I set ForceNew here to true as it doesn't look like you can update a identity provider config + "oidc": { + Type: schema.TypeList, + Required: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "client_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.NoZeroValues, + }, + "identity_provider_config_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.NoZeroValues, + }, + "issuer_url": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.IsURLWithHTTPS, + }, + }, + }, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceAwsEksIdentityProviderConfigCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).eksconn + clusterName := d.Get("cluster_name").(string) + + //TODO - Should I break away from the aws sdk api and move the name outside of the oidc map? + configName, oidcRequest := expandEksOidcIdentityProviderConfigRequest(d.Get("oidc").([]interface{})) + + id := fmt.Sprintf("%s:%s", clusterName, configName) + + input := &eks.AssociateIdentityProviderConfigInput{ + ClientRequestToken: aws.String(resource.UniqueId()), + ClusterName: aws.String(clusterName), + Oidc: oidcRequest, + } + + //if v := d.Get("tags").(map[string]interface{}); len(v) > 0 { + // input.Tags = keyvaluetags.New(v).IgnoreAws().EksTags() + //} + + _, err := conn.AssociateIdentityProviderConfig(input) + if err != nil { + return diag.Errorf("error associating EKS Identity Provider Config (%s): %s", id, err) + } + + d.SetId(id) + + stateConf := resource.StateChangeConf{ + Pending: []string{eks.ConfigStatusCreating}, + Target: []string{eks.ConfigStatusActive}, + Timeout: d.Timeout(schema.TimeoutCreate), + Refresh: refreshEksIdentityProviderConfigStatus(conn, clusterName, &eks.IdentityProviderConfig{ + Name: aws.String(configName), + Type: aws.String(typeOidc), + }), + } + + if _, err := stateConf.WaitForStateContext(ctx); err != nil { + return diag.Errorf("error waiting for EKS Identity Provider Config (%s) association: %s", d.Id(), err) + } + + return resourceAwsEksIdentityProviderConfigRead(ctx, d, meta) +} + +func resourceAwsEksIdentityProviderConfigRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).eksconn + //ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + + clusterName, configName, err := resourceAwsEksIdentityProviderConfigParseId(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + input := &eks.DescribeIdentityProviderConfigInput{ + ClusterName: aws.String(clusterName), + IdentityProviderConfig: &eks.IdentityProviderConfig{ + Name: aws.String(configName), + Type: aws.String(typeOidc), + }, + } + + output, err := conn.DescribeIdentityProviderConfigWithContext(ctx, input) + + if isAWSErr(err, eks.ErrCodeResourceNotFoundException, "") { + log.Printf("[WARN] EKS Identity Provider Config (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return diag.Errorf("error reading EKS Identity Provider Config (%s): %s", d.Id(), err) + } + + config := output.IdentityProviderConfig + + if config == nil { + log.Printf("[WARN] EKS Identity Provider Config (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + // TODO - Do I need the nil check here? + if config.Oidc == nil { + log.Printf("[WARN] EKS OIDC Identity Provider Config (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + d.Set("cluster_name", clusterName) + + if err := d.Set("oidc", flattenEksOidcIdentityProviderConfig(config.Oidc)); err != nil { + return diag.Errorf("error setting oidc: %s", err) + } + + d.Set("status", config.Oidc.Status) + + //if err := d.Set("tags", keyvaluetags.EksKeyValueTags(fargateProfile.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + // return fmt.Errorf("error setting tags: %s", err) + //} + + return nil +} + +func resourceAwsEksIdentityProviderConfigDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).eksconn + + clusterName, configName, err := resourceAwsEksIdentityProviderConfigParseId(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + config := &eks.IdentityProviderConfig{ + Name: aws.String(configName), + Type: aws.String(typeOidc), + } + + log.Printf("[DEBUG] Disassociating EKS Identity Provider Config: %s", d.Id()) + input := &eks.DisassociateIdentityProviderConfigInput{ + ClusterName: aws.String(clusterName), + IdentityProviderConfig: config, + } + + _, err = conn.DisassociateIdentityProviderConfigWithContext(ctx, input) + + if isAWSErr(err, eks.ErrCodeResourceNotFoundException, "") { + return nil + } + + // TODO - if we timeout on the association whilst acc testing this will fail with: + // { + // RespMetadata: { + // StatusCode: 400, + // RequestID: "abf3f851-04e7-4d66-867e-f871897efbb3" + // }, + // ClusterName: "tf-acc-test-387064423719985885", + // Message_: "Identity provider config is not associated with cluster tf-acc-test-387064423719985885" + // } + if err != nil { + return diag.Errorf("error disassociating EKS Identity Provider Config (%s): %s", d.Id(), err) + } + + if err := waitForEksIdentityProviderConfigDisassociation(ctx, conn, clusterName, config, d.Timeout(schema.TimeoutDelete)); err != nil { + return diag.Errorf("error waiting for EKS Identity Provider Config (%s) disassociation: %s", d.Id(), err) + } + + return nil +} + +func refreshEksIdentityProviderConfigStatus(conn *eks.EKS, clusterName string, config *eks.IdentityProviderConfig) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + input := &eks.DescribeIdentityProviderConfigInput{ + ClusterName: aws.String(clusterName), + IdentityProviderConfig: config, + } + + output, err := conn.DescribeIdentityProviderConfig(input) + + if err != nil { + return "", "", err + } + + identityProviderConfig := output.IdentityProviderConfig + + // TODO: not sure about the use of StringValue here. + if identityProviderConfig == nil { + return identityProviderConfig, "", fmt.Errorf("EKS Identity Provider Config (%s:%s) missing", clusterName, aws.StringValue(config.Name)) + } + + oidc := identityProviderConfig.Oidc + + // TODO: Should I be checking for nil on OIDC here too? + if oidc == nil { + return identityProviderConfig, "", fmt.Errorf("EKS OIDC Identity Provider Config (%s:%s) missing", clusterName, aws.StringValue(config.Name)) + } + + return identityProviderConfig, aws.StringValue(oidc.Status), nil + } +} + +func waitForEksIdentityProviderConfigDisassociation(ctx context.Context, conn *eks.EKS, clusterName string, config *eks.IdentityProviderConfig, timeout time.Duration) error { + stateConf := resource.StateChangeConf{ + Pending: []string{ + eks.ConfigStatusActive, + eks.ConfigStatusDeleting, + }, + Target: []string{""}, + Timeout: timeout, + Refresh: refreshEksIdentityProviderConfigStatus(conn, clusterName, config), + } + _, err := stateConf.WaitForStateContext(ctx) + + if isAWSErr(err, eks.ErrCodeResourceNotFoundException, "") { + return nil + } + + return err +} + +func resourceAwsEksIdentityProviderConfigParseId(id string) (string, string, error) { + parts := strings.Split(id, ":") + + if len(parts) != 2 || parts[0] == "" || parts[1] == "" { + return "", "", fmt.Errorf("unexpected format of ID (%s), expected cluster-name:identity-provider-config-name", id) + } + + return parts[0], parts[1], nil +} + +func expandEksOidcIdentityProviderConfigRequest(l []interface{}) (string, *eks.OidcIdentityProviderConfigRequest) { + if len(l) == 0 { + return "", nil + } + + m := l[0].(map[string]interface{}) + + configName := m["identity_provider_config_name"].(string) + oidcIdentityProviderConfigRequest := &eks.OidcIdentityProviderConfigRequest{ + ClientId: aws.String(m["client_id"].(string)), + IdentityProviderConfigName: aws.String(configName), + IssuerUrl: aws.String(m["issuer_url"].(string)), + } + + return configName, oidcIdentityProviderConfigRequest +} + +func flattenEksOidcIdentityProviderConfig(config *eks.OidcIdentityProviderConfig) []map[string]interface{} { + if config == nil { + return []map[string]interface{}{} + } + + m := map[string]interface{}{ + "client_id": aws.StringValue(config.ClientId), + "identity_provider_config_name": aws.StringValue(config.IdentityProviderConfigName), + "issuer_url": aws.StringValue(config.IssuerUrl), + } + + return []map[string]interface{}{m} +} diff --git a/aws/resource_aws_eks_identity_provider_config_test.go b/aws/resource_aws_eks_identity_provider_config_test.go new file mode 100644 index 00000000000..7b1ccd189c8 --- /dev/null +++ b/aws/resource_aws_eks_identity_provider_config_test.go @@ -0,0 +1,276 @@ +package aws + +import ( + "context" + "fmt" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/eks" + "github.com/hashicorp/go-multierror" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "log" + "testing" + "time" +) + +func init() { + resource.AddTestSweepers("aws_eks_identity_provider_config", &resource.Sweeper{ + Name: "aws_eks_identity_provider_config", + F: testSweepEksIdentityProviderConfigs, + }) +} + +func testSweepEksIdentityProviderConfigs(region string) error { + client, err := sharedClientForRegion(region) + if err != nil { + return fmt.Errorf("error getting client: %s", err) + } + conn := client.(*AWSClient).eksconn + + var errors error + input := &eks.ListClustersInput{} + err = conn.ListClustersPages(input, func(page *eks.ListClustersOutput, lastPage bool) bool { + for _, cluster := range page.Clusters { + clusterName := aws.StringValue(cluster) + input := &eks.ListIdentityProviderConfigsInput{ + ClusterName: cluster, + } + err := conn.ListIdentityProviderConfigsPages(input, func(page *eks.ListIdentityProviderConfigsOutput, lastPage bool) bool { + for _, config := range page.IdentityProviderConfigs { + configName := aws.StringValue(config.Name) + log.Printf("[INFO] Disassociating Identity Provider Config %q", configName) + input := &eks.DisassociateIdentityProviderConfigInput{ + ClusterName: cluster, + IdentityProviderConfig: config, + } + _, err := conn.DisassociateIdentityProviderConfig(input) + + if err != nil && !isAWSErr(err, eks.ErrCodeResourceNotFoundException, "") { + errors = multierror.Append(errors, fmt.Errorf("error disassociating Identity Provider Config %q: %w", configName, err)) + continue + } + + // TODO - Should I use context.Background here? + if err := waitForEksIdentityProviderConfigDisassociation(context.Background(), conn, clusterName, config, 10*time.Minute); err != nil { + errors = multierror.Append(errors, fmt.Errorf("error waiting for EKS Identity Provider Config %q disassociation: %w", configName, err)) + continue + } + } + return true + }) + if err != nil { + errors = multierror.Append(errors, fmt.Errorf("error listing Identity Provider Configs for EKS Cluster %s: %w", clusterName, err)) + } + } + + return true + }) + if testSweepSkipSweepError(err) { + log.Printf("[WARN] Skipping EKS Clusters sweep for %s: %s", region, err) + return errors // In case we have completed some pages, but had errors + } + if err != nil { + errors = multierror.Append(errors, fmt.Errorf("error retrieving EKS Clusters: %w", err)) + } + + return errors +} + +func TestAccAWSEksIdentityProviderConfig_basic(t *testing.T) { + var config eks.IdentityProviderConfig + rName := acctest.RandomWithPrefix("tf-acc-test") + eksClusterResourceName := "aws_eks_cluster.test" + resourceName := "aws_eks_identity_provider_config.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t) }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckAWSEksIdentityProviderConfigDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEksIdentityProviderConfigProviderConfigName(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEksIdentityProviderConfigExists(resourceName, &config), + resource.TestCheckResourceAttrPair(resourceName, "cluster_name", eksClusterResourceName, "name"), + resource.TestCheckResourceAttr(resourceName, "oidc.#", "1"), + resource.TestCheckResourceAttr(resourceName, "oidc.0.identity_provider_config_name", rName), + resource.TestCheckResourceAttr(resourceName, "oidc.0.client_id", "test-url.apps.googleusercontent.com"), + resource.TestCheckResourceAttr(resourceName, "oidc.0.issuer_url", "https://accounts.google.com/.well-known/openid-configuration"), + ), + }, + }, + }) +} + +func testAccCheckAWSEksIdentityProviderConfigExists(resourceName string, config *eks.IdentityProviderConfig) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No EKS Identity Profile Config is set") + } + + clusterName, configName, err := resourceAwsEksIdentityProviderConfigParseId(rs.Primary.ID) + if err != nil { + return err + } + + conn := testAccProvider.Meta().(*AWSClient).eksconn + + input := &eks.DescribeIdentityProviderConfigInput{ + ClusterName: aws.String(clusterName), + IdentityProviderConfig: &eks.IdentityProviderConfig{ + Name: aws.String(configName), + Type: aws.String(typeOidc), + }, + } + + output, err := conn.DescribeIdentityProviderConfig(input) + + if err != nil { + return err + } + + if output == nil || output.IdentityProviderConfig == nil { + return fmt.Errorf("EKS Identity Provider Config (%s) not found", rs.Primary.ID) + } + + if aws.StringValue(output.IdentityProviderConfig.Oidc.IdentityProviderConfigName) != configName { + return fmt.Errorf("EKS OIDC Identity Provider Config (%s) not found", rs.Primary.ID) + } + + if got, want := aws.StringValue(output.IdentityProviderConfig.Oidc.Status), eks.ConfigStatusActive; got != want { + return fmt.Errorf("EKS OIDC Identity Provider Config (%s) not in %s status, got: %s", rs.Primary.ID, want, got) + } + + *config = eks.IdentityProviderConfig{ + Name: output.IdentityProviderConfig.Oidc.IdentityProviderConfigName, + Type: aws.String(typeOidc), + } + + return nil + } +} + +// TODO - still needs some work +func testAccCheckAWSEksIdentityProviderConfigDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).eksconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_eks_identity_provider_config" { + continue + } + + clusterName, configName, err := resourceAwsEksIdentityProviderConfigParseId(rs.Primary.ID) + if err != nil { + return err + } + + input := &eks.DescribeIdentityProviderConfigInput{ + ClusterName: aws.String(clusterName), + IdentityProviderConfig: &eks.IdentityProviderConfig{ + Name: aws.String(configName), + Type: aws.String(typeOidc), + }, + } + + output, err := conn.DescribeIdentityProviderConfig(input) + + if isAWSErr(err, eks.ErrCodeResourceNotFoundException, "") { + continue + } + + if output != nil && output.IdentityProviderConfig != nil && aws.StringValue(output.IdentityProviderConfig.Oidc.IdentityProviderConfigName) == configName { + return fmt.Errorf("EKS Identity Provider Config (%s) still exists", rs.Primary.ID) + } + } + + return nil +} + +func testAccAWSEksIdentityProviderConfigBase(rName string) string { + return fmt.Sprintf(` +data "aws_availability_zones" "available" { + state = "available" + + filter { + name = "opt-in-status" + values = ["opt-in-not-required"] + } +} + +data "aws_partition" "current" {} + +resource "aws_iam_role" "cluster" { + name = "%[1]s-cluster" + + assume_role_policy = jsonencode({ + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "eks.${data.aws_partition.current.dns_suffix}" + } + }] + Version = "2012-10-17" + }) +} + +resource "aws_iam_role_policy_attachment" "cluster-AmazonEKSClusterPolicy" { + policy_arn = "arn:${data.aws_partition.current.partition}:iam::aws:policy/AmazonEKSClusterPolicy" + role = aws_iam_role.cluster.name +} + +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" + enable_dns_hostnames = true + enable_dns_support = true + + tags = { + Name = "tf-acc-test-eks-identity-provider-config" + "kubernetes.io/cluster/%[1]s" = "shared" + } +} + +resource "aws_subnet" "test" { + count = 2 + + availability_zone = data.aws_availability_zones.available.names[count.index] + cidr_block = "10.0.${count.index}.0/24" + vpc_id = aws_vpc.test.id + + tags = { + Name = "tf-acc-test-eks-identity-provider-config" + "kubernetes.io/cluster/%[1]s" = "shared" + } +} + +resource "aws_eks_cluster" "test" { + name = %[1]q + role_arn = aws_iam_role.cluster.arn + + vpc_config { + subnet_ids = aws_subnet.test[*].id + } + + depends_on = [aws_iam_role_policy_attachment.cluster-AmazonEKSClusterPolicy] +} +`, rName) +} + +func testAccAWSEksIdentityProviderConfigProviderConfigName(rName string) string { + return testAccAWSEksIdentityProviderConfigBase(rName) + fmt.Sprintf(` +resource "aws_eks_identity_provider_config" "test" { + cluster_name = aws_eks_cluster.test.name + oidc { + client_id = "test-url.apps.googleusercontent.com" + identity_provider_config_name = %[1]q + issuer_url = "https://accounts.google.com/.well-known/openid-configuration" + } +} +`, rName) +} From 4a7b303c8db7c5aa1b6404b03e616439a2ccdb63 Mon Sep 17 00:00:00 2001 From: Will Varney Date: Tue, 2 Mar 2021 11:15:20 +0000 Subject: [PATCH 150/398] New resource: aws_eks_identity_provider_config. This adds a new resource for associating an identity provider with an EKS cluster. This commit adds a placeholder for the changelog entry and initial documentation for the resource. --- .changelog/pending.txt | 3 + go.sum | 5 ++ ...eks_identity_provider_config.html.markdown | 69 +++++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 .changelog/pending.txt create mode 100644 website/docs/r/eks_identity_provider_config.html.markdown diff --git a/.changelog/pending.txt b/.changelog/pending.txt new file mode 100644 index 00000000000..9b0b4cd67b6 --- /dev/null +++ b/.changelog/pending.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_eks_identity_provider_config +``` \ No newline at end of file diff --git a/go.sum b/go.sum index 009b6a2c58d..15c3f8ef968 100644 --- a/go.sum +++ b/go.sum @@ -50,6 +50,7 @@ github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7ISrnJIXKzwaspym5BTKGx93EI= github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0= github.com/andybalholm/crlf v0.0.0-20171020200849-670099aa064f/go.mod h1:k8feO4+kXDxro6ErPXBRTJ/ro2mf0SsFG8s7doP9kJE= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/apparentlymart/go-cidr v1.0.1 h1:NmIwLZ/KdsjIUlhf+/Np40atNXm/+lZ5txfTJ/SpF+U= github.com/apparentlymart/go-cidr v1.0.1/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= @@ -62,6 +63,7 @@ github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJE github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= github.com/aws/aws-sdk-go v1.25.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= @@ -95,7 +97,9 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= @@ -294,6 +298,7 @@ github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= diff --git a/website/docs/r/eks_identity_provider_config.html.markdown b/website/docs/r/eks_identity_provider_config.html.markdown new file mode 100644 index 00000000000..be71227d683 --- /dev/null +++ b/website/docs/r/eks_identity_provider_config.html.markdown @@ -0,0 +1,69 @@ +--- +subcategory: "EKS" +layout: "aws" +page_title: "AWS: aws_eks_identity_provider_config" +description: |- + Manages an EKS Identity Provider Configuration +--- + +# Resource: aws_eks_identity_provider_config + +Manages an EKS Identity Provider Configuration. + +## Example Usage + +```hcl +resource "aws_eks_identity_provider_config" "example" { + cluster_name = aws_eks_cluster.example.name + oidc { + client_id = "your client_id" + identity_provider_config_name = "example" + issuer_url = "your issuer_url" + } +} +``` + +## Argument Reference + +The following arguments are required: + +* `cluster_name` – Name of the EKS Cluster. +* `oidc` - Nested attribute containing [OpenID Connect](https://openid.net/connect/) identity provider information for the cluster. + * `client_id` – Client ID for the OpenID Connect identity provider. + * `identity_provider_config_name` – The name of the identity provider config. + * `issuer_url` - Issuer URL for the OpenID Connect identity provider. + +The following arguments are optional: + +* `tags` - (Optional) Key-value map of resource tags. + +### selector Configuration Block + +The following arguments are required: + +* `namespace` - (Required) Kubernetes namespace for selection. + +The following arguments are optional: + +* `labels` - (Optional) Key-value map of Kubernetes labels for selection. + +## Attributes Reference +In addition to all arguments above, the following attributes are exported: + +* `id` - EKS Cluster name and EKS Identity Provider Configuration name separated by a colon (`:`). +* `status` - Status of the EKS Identity Provider Configuration. + +## Timeouts + +`aws_eks_identity_provider_config` provides the following [Timeouts](https://www.terraform.io/docs/configuration/blocks/resources/syntax.html#operation-timeouts) configuration options: + +* `create` - (Default `25 minutes`) How long to wait for the EKS Identity Provider Configuration to be associated. +* `delete` - (Default `25 minutes`) How long to wait for the EKS Identity Provider Configuration to be disassociated. + +## Import + +EKS Fargate Profiles can be imported using the `cluster_name` and `fargate_profile_name` separated by a colon (`:`), e.g. + +``` +$ terraform import aws_eks_identity_provider_config.my_identity_provider_config my_cluster:my_identity_provider_config +``` From 4ff23ac57bce5614ce4b31767f9767a3283a2fb0 Mon Sep 17 00:00:00 2001 From: Will Varney Date: Tue, 2 Mar 2021 12:47:56 +0000 Subject: [PATCH 151/398] New resource: aws_eks_identity_provider_config. This adds a new resource for associating an identity provider with an EKS cluster. This commit adds a the ability to import an existing identity provider config resource. --- aws/resource_aws_eks_identity_provider_config.go | 5 +++-- aws/resource_aws_eks_identity_provider_config_test.go | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_eks_identity_provider_config.go b/aws/resource_aws_eks_identity_provider_config.go index 49aa3cd9499..dfefa7b16c0 100644 --- a/aws/resource_aws_eks_identity_provider_config.go +++ b/aws/resource_aws_eks_identity_provider_config.go @@ -22,9 +22,10 @@ func resourceAwsEksIdentityProviderConfig() *schema.Resource { ReadContext: resourceAwsEksIdentityProviderConfigRead, DeleteContext: resourceAwsEksIdentityProviderConfigDelete, - // TODO - Not sure if I can enable importing? + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, - // TODO - AWS is suggesting it can take 15-20 minutes to associate an identity provider with EKS. Seeing `context deadline exceeded`. Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(25 * time.Minute), Delete: schema.DefaultTimeout(25 * time.Minute), diff --git a/aws/resource_aws_eks_identity_provider_config_test.go b/aws/resource_aws_eks_identity_provider_config_test.go index 7b1ccd189c8..71c401f9d91 100644 --- a/aws/resource_aws_eks_identity_provider_config_test.go +++ b/aws/resource_aws_eks_identity_provider_config_test.go @@ -99,6 +99,11 @@ func TestAccAWSEksIdentityProviderConfig_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "oidc.0.issuer_url", "https://accounts.google.com/.well-known/openid-configuration"), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } From fae1a31c18144a151fec9007ff21c3ca7ea0ef68 Mon Sep 17 00:00:00 2001 From: Will Varney Date: Tue, 2 Mar 2021 17:35:00 +0000 Subject: [PATCH 152/398] New resource: aws_eks_identity_provider_config. This adds a new resource for associating an identity provider with an EKS cluster. This commit adds a test for if the resource dissapears and handles an invalid request exception when the identity provider config is not associated to the specified cluster on delete. --- ...source_aws_eks_identity_provider_config.go | 13 ++---- ...e_aws_eks_identity_provider_config_test.go | 45 +++++++++++++++++++ 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/aws/resource_aws_eks_identity_provider_config.go b/aws/resource_aws_eks_identity_provider_config.go index dfefa7b16c0..74d3f6d683c 100644 --- a/aws/resource_aws_eks_identity_provider_config.go +++ b/aws/resource_aws_eks_identity_provider_config.go @@ -197,19 +197,12 @@ func resourceAwsEksIdentityProviderConfigDelete(ctx context.Context, d *schema.R _, err = conn.DisassociateIdentityProviderConfigWithContext(ctx, input) - if isAWSErr(err, eks.ErrCodeResourceNotFoundException, "") { + // TODO - Is checking for the exception message too brittle? + if isAWSErr(err, eks.ErrCodeResourceNotFoundException, "") || + isAWSErr(err, eks.ErrCodeInvalidRequestException, "Identity provider config is not associated with cluster") { return nil } - // TODO - if we timeout on the association whilst acc testing this will fail with: - // { - // RespMetadata: { - // StatusCode: 400, - // RequestID: "abf3f851-04e7-4d66-867e-f871897efbb3" - // }, - // ClusterName: "tf-acc-test-387064423719985885", - // Message_: "Identity provider config is not associated with cluster tf-acc-test-387064423719985885" - // } if err != nil { return diag.Errorf("error disassociating EKS Identity Provider Config (%s): %s", d.Id(), err) } diff --git a/aws/resource_aws_eks_identity_provider_config_test.go b/aws/resource_aws_eks_identity_provider_config_test.go index 71c401f9d91..e702cd086fe 100644 --- a/aws/resource_aws_eks_identity_provider_config_test.go +++ b/aws/resource_aws_eks_identity_provider_config_test.go @@ -108,6 +108,28 @@ func TestAccAWSEksIdentityProviderConfig_basic(t *testing.T) { }) } +func TestAccAWSEksIdentityProviderConfig_disappears(t *testing.T) { + var config eks.IdentityProviderConfig + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_eks_identity_provider_config.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t) }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckAWSEksIdentityProviderConfigDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEksIdentityProviderConfigProviderConfigName(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEksIdentityProviderConfigExists(resourceName, &config), + testAccCheckAWSEksIdentityProviderConfigDisappears(rName, &config), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + func testAccCheckAWSEksIdentityProviderConfigExists(resourceName string, config *eks.IdentityProviderConfig) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] @@ -197,6 +219,29 @@ func testAccCheckAWSEksIdentityProviderConfigDestroy(s *terraform.State) error { return nil } +func testAccCheckAWSEksIdentityProviderConfigDisappears(clusterName string, config *eks.IdentityProviderConfig) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).eksconn + + input := &eks.DisassociateIdentityProviderConfigInput{ + ClusterName: aws.String(clusterName), + IdentityProviderConfig: config, + } + + _, err := conn.DisassociateIdentityProviderConfig(input) + + if isAWSErr(err, eks.ErrCodeResourceNotFoundException, "") { + return nil + } + + if err != nil { + return err + } + + return waitForEksIdentityProviderConfigDisassociation(context.Background(), conn, clusterName, config, 25*time.Minute) + } +} + func testAccAWSEksIdentityProviderConfigBase(rName string) string { return fmt.Sprintf(` data "aws_availability_zones" "available" { From ff36e80dd945299030b198e7d105e2f5529d5c91 Mon Sep 17 00:00:00 2001 From: Will Varney Date: Fri, 5 Mar 2021 18:07:34 +0000 Subject: [PATCH 153/398] New resource: aws_eks_identity_provider_config. This adds a new resource for associating an identity provider with an EKS cluster. This commit adds the optional fields to the resource and tests for them. It also adds tests for the validation on the schema. --- ...source_aws_eks_identity_provider_config.go | 93 +++++++- ...e_aws_eks_identity_provider_config_test.go | 207 +++++++++++++++++- ...eks_identity_provider_config.html.markdown | 18 +- 3 files changed, 294 insertions(+), 24 deletions(-) diff --git a/aws/resource_aws_eks_identity_provider_config.go b/aws/resource_aws_eks_identity_provider_config.go index 74d3f6d683c..7a09591aced 100644 --- a/aws/resource_aws_eks_identity_provider_config.go +++ b/aws/resource_aws_eks_identity_provider_config.go @@ -5,10 +5,12 @@ import ( "fmt" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/eks" + "github.com/hashicorp/go-cty/cty" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "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" "log" "strings" "time" @@ -38,7 +40,6 @@ func resourceAwsEksIdentityProviderConfig() *schema.Resource { ForceNew: true, ValidateFunc: validation.NoZeroValues, }, - // TODO - Do I set ForceNew here to true as it doesn't look like you can update a identity provider config "oidc": { Type: schema.TypeList, Required: true, @@ -51,6 +52,18 @@ func resourceAwsEksIdentityProviderConfig() *schema.Resource { ForceNew: true, ValidateFunc: validation.NoZeroValues, }, + "groups_claim": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.NoZeroValues, + }, + "groups_prefix": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.NoZeroValues, + }, "identity_provider_config_name": { Type: schema.TypeString, Required: true, @@ -63,9 +76,35 @@ func resourceAwsEksIdentityProviderConfig() *schema.Resource { ForceNew: true, ValidateFunc: validation.IsURLWithHTTPS, }, + "required_claims": { + Type: schema.TypeMap, + Optional: true, + ForceNew: true, + ValidateDiagFunc: all( + validation.MapKeyLenBetween(1, 63), + validation.MapValueLenBetween(1, 253), + ), + Elem: &schema.Schema{ + Type: schema.TypeString, + ForceNew: true, + }, + }, + "username_claim": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.NoZeroValues, + }, + "username_prefix": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.NoZeroValues, + }, }, }, }, + "tags": tagsSchemaForceNew(), "status": { Type: schema.TypeString, Computed: true, @@ -74,6 +113,16 @@ func resourceAwsEksIdentityProviderConfig() *schema.Resource { } } +func all(validators ...schema.SchemaValidateDiagFunc) schema.SchemaValidateDiagFunc { + return func(i interface{}, k cty.Path) diag.Diagnostics { + var diags diag.Diagnostics + for _, validator := range validators { + diags = append(diags, validator(i, k)...) + } + return diags + } +} + func resourceAwsEksIdentityProviderConfigCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*AWSClient).eksconn clusterName := d.Get("cluster_name").(string) @@ -89,9 +138,9 @@ func resourceAwsEksIdentityProviderConfigCreate(ctx context.Context, d *schema.R Oidc: oidcRequest, } - //if v := d.Get("tags").(map[string]interface{}); len(v) > 0 { - // input.Tags = keyvaluetags.New(v).IgnoreAws().EksTags() - //} + if v := d.Get("tags").(map[string]interface{}); len(v) > 0 { + input.Tags = keyvaluetags.New(v).IgnoreAws().EksTags() + } _, err := conn.AssociateIdentityProviderConfig(input) if err != nil { @@ -119,7 +168,7 @@ func resourceAwsEksIdentityProviderConfigCreate(ctx context.Context, d *schema.R func resourceAwsEksIdentityProviderConfigRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*AWSClient).eksconn - //ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig clusterName, configName, err := resourceAwsEksIdentityProviderConfigParseId(d.Id()) if err != nil { @@ -154,7 +203,6 @@ func resourceAwsEksIdentityProviderConfigRead(ctx context.Context, d *schema.Res return nil } - // TODO - Do I need the nil check here? if config.Oidc == nil { log.Printf("[WARN] EKS OIDC Identity Provider Config (%s) not found, removing from state", d.Id()) d.SetId("") @@ -169,9 +217,9 @@ func resourceAwsEksIdentityProviderConfigRead(ctx context.Context, d *schema.Res d.Set("status", config.Oidc.Status) - //if err := d.Set("tags", keyvaluetags.EksKeyValueTags(fargateProfile.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { - // return fmt.Errorf("error setting tags: %s", err) - //} + if err := d.Set("tags", keyvaluetags.EksKeyValueTags(config.Oidc.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return diag.Errorf("error setting tags: %s", err) + } return nil } @@ -229,14 +277,12 @@ func refreshEksIdentityProviderConfigStatus(conn *eks.EKS, clusterName string, c identityProviderConfig := output.IdentityProviderConfig - // TODO: not sure about the use of StringValue here. if identityProviderConfig == nil { return identityProviderConfig, "", fmt.Errorf("EKS Identity Provider Config (%s:%s) missing", clusterName, aws.StringValue(config.Name)) } oidc := identityProviderConfig.Oidc - // TODO: Should I be checking for nil on OIDC here too? if oidc == nil { return identityProviderConfig, "", fmt.Errorf("EKS OIDC Identity Provider Config (%s:%s) missing", clusterName, aws.StringValue(config.Name)) } @@ -288,6 +334,26 @@ func expandEksOidcIdentityProviderConfigRequest(l []interface{}) (string, *eks.O IssuerUrl: aws.String(m["issuer_url"].(string)), } + if v, ok := m["groups_claim"].(string); ok && v != "" { + oidcIdentityProviderConfigRequest.GroupsClaim = aws.String(v) + } + + if v, ok := m["groups_prefix"].(string); ok && v != "" { + oidcIdentityProviderConfigRequest.GroupsPrefix = aws.String(v) + } + + if v, ok := m["required_claims"].(map[string]interface{}); ok && len(v) > 0 { + oidcIdentityProviderConfigRequest.RequiredClaims = stringMapToPointers(v) + } + + if v, ok := m["username_claim"].(string); ok && v != "" { + oidcIdentityProviderConfigRequest.UsernameClaim = aws.String(v) + } + + if v, ok := m["username_prefix"].(string); ok && v != "" { + oidcIdentityProviderConfigRequest.UsernamePrefix = aws.String(v) + } + return configName, oidcIdentityProviderConfigRequest } @@ -298,8 +364,13 @@ func flattenEksOidcIdentityProviderConfig(config *eks.OidcIdentityProviderConfig m := map[string]interface{}{ "client_id": aws.StringValue(config.ClientId), + "groups_claim": aws.StringValue(config.GroupsClaim), + "groups_prefix": aws.StringValue(config.GroupsPrefix), "identity_provider_config_name": aws.StringValue(config.IdentityProviderConfigName), "issuer_url": aws.StringValue(config.IssuerUrl), + "required_claims": aws.StringValueMap(config.RequiredClaims), + "username_claim": aws.StringValue(config.UsernameClaim), + "username_prefix": aws.StringValue(config.UsernamePrefix), } return []map[string]interface{}{m} diff --git a/aws/resource_aws_eks_identity_provider_config_test.go b/aws/resource_aws_eks_identity_provider_config_test.go index e702cd086fe..4d7a94d2664 100644 --- a/aws/resource_aws_eks_identity_provider_config_test.go +++ b/aws/resource_aws_eks_identity_provider_config_test.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "log" + "regexp" "testing" "time" ) @@ -88,15 +89,21 @@ func TestAccAWSEksIdentityProviderConfig_basic(t *testing.T) { ProviderFactories: testAccProviderFactories, CheckDestroy: testAccCheckAWSEksIdentityProviderConfigDestroy, Steps: []resource.TestStep{ + { + Config: testAccAWSEksIdentityProviderConfigProvider_Oidc_IssuerUrl(rName, "http://accounts.google.com/.well-known/openid-configuration"), + ExpectError: regexp.MustCompile(`expected .* to have a url with schema of: "https", got http://accounts.google.com/.well-known/openid-configuration`), + }, { Config: testAccAWSEksIdentityProviderConfigProviderConfigName(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSEksIdentityProviderConfigExists(resourceName, &config), resource.TestCheckResourceAttrPair(resourceName, "cluster_name", eksClusterResourceName, "name"), resource.TestCheckResourceAttr(resourceName, "oidc.#", "1"), - resource.TestCheckResourceAttr(resourceName, "oidc.0.identity_provider_config_name", rName), resource.TestCheckResourceAttr(resourceName, "oidc.0.client_id", "test-url.apps.googleusercontent.com"), + resource.TestCheckResourceAttr(resourceName, "oidc.0.identity_provider_config_name", rName), resource.TestCheckResourceAttr(resourceName, "oidc.0.issuer_url", "https://accounts.google.com/.well-known/openid-configuration"), + resource.TestCheckResourceAttr(resourceName, "oidc.0.required_claims.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, { @@ -130,6 +137,126 @@ func TestAccAWSEksIdentityProviderConfig_disappears(t *testing.T) { }) } +func TestAccAWSEksIdentityProviderConfig_Oidc_Group(t *testing.T) { + var config eks.IdentityProviderConfig + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_eks_identity_provider_config.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t) }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckAWSEksIdentityProviderConfigDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEksIdentityProviderConfigProvider_Oidc_Groups(rName, "groups", "oidc:"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEksIdentityProviderConfigExists(resourceName, &config), + resource.TestCheckResourceAttr(resourceName, "oidc.#", "1"), + resource.TestCheckResourceAttr(resourceName, "oidc.0.groups_claim", "groups"), + resource.TestCheckResourceAttr(resourceName, "oidc.0.groups_prefix", "oidc:"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSEksIdentityProviderConfig_Oidc_Username(t *testing.T) { + var config eks.IdentityProviderConfig + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_eks_identity_provider_config.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t) }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckAWSEksIdentityProviderConfigDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEksIdentityProviderConfigProvider_Oidc_Username(rName, "email", "-"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEksIdentityProviderConfigExists(resourceName, &config), + resource.TestCheckResourceAttr(resourceName, "oidc.#", "1"), + resource.TestCheckResourceAttr(resourceName, "oidc.0.username_claim", "email"), + resource.TestCheckResourceAttr(resourceName, "oidc.0.username_prefix", "-"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSEksIdentityProviderConfig_Oidc_RequiredClaims(t *testing.T) { + var config eks.IdentityProviderConfig + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_eks_identity_provider_config.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t) }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckAWSEksIdentityProviderConfigDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEksIdentityProviderConfig_Oidc_RequiredClaims(rName, "4qkvw9k2RbpSO6wgCbynY10T6Rc2n89PQblyi6bZ5VhfpMr6V7FVvrA12FiJxarh", "valueOne", "keyTwo", "valueTwo"), + ExpectError: regexp.MustCompile("Bad map key length"), + }, + { + Config: testAccAWSEksIdentityProviderConfig_Oidc_RequiredClaims(rName, "keyOne", "bUUUiuIXeFGw0M2VwiCVjR8oIIavv0PF49Ba6yNwOOC7IcoLawczSeb6MpEIhqtXKcf9aogW4uc4smLGvdTQ8uTTkVFvQTPyWXQ3F0uZP02YyoSw0d9MZ7laGRjpXSph9oFE2UlT5IyRaXIsTwl1qvItvVXLN40Pd3PDyPa6de4nlYcRNy6YIikZz2P1QUSYuvMGSJxGUzhTKYRUniolIt1vjHsXt3MAsaJtCcWz0tjLWalvG27pQ3Gl5Cs7K1", "keyTwo", "valueTwo"), + ExpectError: regexp.MustCompile("Bad map value length"), + }, + { + Config: testAccAWSEksIdentityProviderConfig_Oidc_RequiredClaims(rName, "keyOne", "valueOne", "keyTwo", "valueTwo"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEksIdentityProviderConfigExists(resourceName, &config), + resource.TestCheckResourceAttr(resourceName, "oidc.0.required_claims.%", "2"), + resource.TestCheckResourceAttr(resourceName, "oidc.0.required_claims.keyOne", "valueOne"), + resource.TestCheckResourceAttr(resourceName, "oidc.0.required_claims.keyTwo", "valueTwo"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSEksIdentityProviderConfig_Tags(t *testing.T) { + var config eks.IdentityProviderConfig + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_eks_identity_provider_config.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t) }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckAWSEksIdentityProviderConfigDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEksIdentityProviderConfig_Tags(rName, "keyOne", "valueOne", "keyTwo", "valueTwo"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEksIdentityProviderConfigExists(resourceName, &config), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.keyOne", "valueOne"), + resource.TestCheckResourceAttr(resourceName, "tags.keyTwo", "valueTwo"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testAccCheckAWSEksIdentityProviderConfigExists(resourceName string, config *eks.IdentityProviderConfig) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] @@ -183,7 +310,6 @@ func testAccCheckAWSEksIdentityProviderConfigExists(resourceName string, config } } -// TODO - still needs some work func testAccCheckAWSEksIdentityProviderConfigDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).eksconn @@ -324,3 +450,80 @@ resource "aws_eks_identity_provider_config" "test" { } `, rName) } + +func testAccAWSEksIdentityProviderConfigProvider_Oidc_IssuerUrl(rName, issuerUrl string) string { + return testAccAWSEksIdentityProviderConfigBase(rName) + fmt.Sprintf(` +resource "aws_eks_identity_provider_config" "test" { + cluster_name = aws_eks_cluster.test.name + oidc { + client_id = "test-url.apps.googleusercontent.com" + identity_provider_config_name = %[1]q + issuer_url = %[2]q + } +} +`, rName, issuerUrl) +} + +func testAccAWSEksIdentityProviderConfigProvider_Oidc_Groups(rName, groupsClaim, groupsPrefix string) string { + return testAccAWSEksIdentityProviderConfigBase(rName) + fmt.Sprintf(` +resource "aws_eks_identity_provider_config" "test" { + cluster_name = aws_eks_cluster.test.name + oidc { + client_id = "test-url.apps.googleusercontent.com" + groups_claim = %[2]q + groups_prefix = %[3]q + identity_provider_config_name = %[1]q + issuer_url = "https://accounts.google.com/.well-known/openid-configuration" + } +} +`, rName, groupsClaim, groupsPrefix) +} + +func testAccAWSEksIdentityProviderConfigProvider_Oidc_Username(rName, usernameClaim, usernamePrefix string) string { + return testAccAWSEksIdentityProviderConfigBase(rName) + fmt.Sprintf(` +resource "aws_eks_identity_provider_config" "test" { + cluster_name = aws_eks_cluster.test.name + oidc { + client_id = "test-url.apps.googleusercontent.com" + identity_provider_config_name = %[1]q + issuer_url = "https://accounts.google.com/.well-known/openid-configuration" + username_claim = %[2]q + username_prefix = %[3]q + } +} +`, rName, usernameClaim, usernamePrefix) +} + +func testAccAWSEksIdentityProviderConfig_Oidc_RequiredClaims(rName, claimsKeyOne, claimsValueOne, claimsKeyTwo, claimsValueTwo string) string { + return testAccAWSEksIdentityProviderConfigBase(rName) + fmt.Sprintf(` +resource "aws_eks_identity_provider_config" "test" { + cluster_name = aws_eks_cluster.test.name + oidc { + client_id = "test-url.apps.googleusercontent.com" + identity_provider_config_name = %[1]q + issuer_url = "https://accounts.google.com/.well-known/openid-configuration" + required_claims = { + %[2]q = %[3]q + %[4]q = %[5]q + } + } +} +`, rName, claimsKeyOne, claimsValueOne, claimsKeyTwo, claimsValueTwo) +} + +func testAccAWSEksIdentityProviderConfig_Tags(rName, tagsKeyOne, tagsValueOne, tagsKeyTwo, tagsValueTwo string) string { + return testAccAWSEksIdentityProviderConfigBase(rName) + fmt.Sprintf(` +resource "aws_eks_identity_provider_config" "test" { + cluster_name = aws_eks_cluster.test.name + oidc { + client_id = "test-url.apps.googleusercontent.com" + identity_provider_config_name = %[1]q + issuer_url = "https://accounts.google.com/.well-known/openid-configuration" + } + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } +} +`, rName, tagsKeyOne, tagsValueOne, tagsKeyTwo, tagsValueTwo) +} diff --git a/website/docs/r/eks_identity_provider_config.html.markdown b/website/docs/r/eks_identity_provider_config.html.markdown index be71227d683..f480b0568b3 100644 --- a/website/docs/r/eks_identity_provider_config.html.markdown +++ b/website/docs/r/eks_identity_provider_config.html.markdown @@ -35,18 +35,14 @@ The following arguments are required: The following arguments are optional: +* `oidc` + * `groups_claim` - The JWT claim that the provider will use to return groups. + * `groups_prefix` - A prefix that is prepended to group claims e.g. `oidc:`. + * `required_claims` - The key value pairs that describe required claims in the identity token. + * `username_claim` - The JWT claim that the provider will use as the username. + * `username_prefix` - A prefix that is prepended to username claims. * `tags` - (Optional) Key-value map of resource tags. -### selector Configuration Block - -The following arguments are required: - -* `namespace` - (Required) Kubernetes namespace for selection. - -The following arguments are optional: - -* `labels` - (Optional) Key-value map of Kubernetes labels for selection. - ## Attributes Reference In addition to all arguments above, the following attributes are exported: @@ -62,7 +58,7 @@ In addition to all arguments above, the following attributes are exported: ## Import -EKS Fargate Profiles can be imported using the `cluster_name` and `fargate_profile_name` separated by a colon (`:`), e.g. +EKS Identity Provider Configuration can be imported using the `cluster_name` and `identity_provider_config_name` separated by a colon (`:`), e.g. ``` $ terraform import aws_eks_identity_provider_config.my_identity_provider_config my_cluster:my_identity_provider_config From 01d0616e3e505941d02828946dffe3822d1ba90b Mon Sep 17 00:00:00 2001 From: Will Varney Date: Mon, 8 Mar 2021 10:31:27 +0000 Subject: [PATCH 154/398] New resource: aws_eks_identity_provider_config. This adds a new resource for associating an identity provider with an EKS cluster. This commit fixes the formatting of the terraform code in string templates and fixes the wrong grouping for imports. --- ...source_aws_eks_identity_provider_config.go | 7 ++- ...e_aws_eks_identity_provider_config_test.go | 61 ++++++++++--------- 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/aws/resource_aws_eks_identity_provider_config.go b/aws/resource_aws_eks_identity_provider_config.go index 7a09591aced..e5a27b4cd1c 100644 --- a/aws/resource_aws_eks_identity_provider_config.go +++ b/aws/resource_aws_eks_identity_provider_config.go @@ -3,6 +3,10 @@ package aws import ( "context" "fmt" + "log" + "strings" + "time" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/eks" "github.com/hashicorp/go-cty/cty" @@ -11,9 +15,6 @@ import ( "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" - "log" - "strings" - "time" ) const typeOidc string = "oidc" diff --git a/aws/resource_aws_eks_identity_provider_config_test.go b/aws/resource_aws_eks_identity_provider_config_test.go index 4d7a94d2664..4b2207a9898 100644 --- a/aws/resource_aws_eks_identity_provider_config_test.go +++ b/aws/resource_aws_eks_identity_provider_config_test.go @@ -3,16 +3,17 @@ package aws import ( "context" "fmt" + "log" + "regexp" + "testing" + "time" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/eks" "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" - "log" - "regexp" - "testing" - "time" ) func init() { @@ -441,11 +442,11 @@ resource "aws_eks_cluster" "test" { func testAccAWSEksIdentityProviderConfigProviderConfigName(rName string) string { return testAccAWSEksIdentityProviderConfigBase(rName) + fmt.Sprintf(` resource "aws_eks_identity_provider_config" "test" { - cluster_name = aws_eks_cluster.test.name + cluster_name = aws_eks_cluster.test.name oidc { - client_id = "test-url.apps.googleusercontent.com" + client_id = "test-url.apps.googleusercontent.com" identity_provider_config_name = %[1]q - issuer_url = "https://accounts.google.com/.well-known/openid-configuration" + issuer_url = "https://accounts.google.com/.well-known/openid-configuration" } } `, rName) @@ -454,11 +455,11 @@ resource "aws_eks_identity_provider_config" "test" { func testAccAWSEksIdentityProviderConfigProvider_Oidc_IssuerUrl(rName, issuerUrl string) string { return testAccAWSEksIdentityProviderConfigBase(rName) + fmt.Sprintf(` resource "aws_eks_identity_provider_config" "test" { - cluster_name = aws_eks_cluster.test.name + cluster_name = aws_eks_cluster.test.name oidc { - client_id = "test-url.apps.googleusercontent.com" + client_id = "test-url.apps.googleusercontent.com" identity_provider_config_name = %[1]q - issuer_url = %[2]q + issuer_url = %[2]q } } `, rName, issuerUrl) @@ -467,13 +468,13 @@ resource "aws_eks_identity_provider_config" "test" { func testAccAWSEksIdentityProviderConfigProvider_Oidc_Groups(rName, groupsClaim, groupsPrefix string) string { return testAccAWSEksIdentityProviderConfigBase(rName) + fmt.Sprintf(` resource "aws_eks_identity_provider_config" "test" { - cluster_name = aws_eks_cluster.test.name + cluster_name = aws_eks_cluster.test.name oidc { - client_id = "test-url.apps.googleusercontent.com" - groups_claim = %[2]q - groups_prefix = %[3]q + client_id = "test-url.apps.googleusercontent.com" + groups_claim = %[2]q + groups_prefix = %[3]q identity_provider_config_name = %[1]q - issuer_url = "https://accounts.google.com/.well-known/openid-configuration" + issuer_url = "https://accounts.google.com/.well-known/openid-configuration" } } `, rName, groupsClaim, groupsPrefix) @@ -482,13 +483,13 @@ resource "aws_eks_identity_provider_config" "test" { func testAccAWSEksIdentityProviderConfigProvider_Oidc_Username(rName, usernameClaim, usernamePrefix string) string { return testAccAWSEksIdentityProviderConfigBase(rName) + fmt.Sprintf(` resource "aws_eks_identity_provider_config" "test" { - cluster_name = aws_eks_cluster.test.name + cluster_name = aws_eks_cluster.test.name oidc { - client_id = "test-url.apps.googleusercontent.com" + client_id = "test-url.apps.googleusercontent.com" identity_provider_config_name = %[1]q - issuer_url = "https://accounts.google.com/.well-known/openid-configuration" - username_claim = %[2]q - username_prefix = %[3]q + issuer_url = "https://accounts.google.com/.well-known/openid-configuration" + username_claim = %[2]q + username_prefix = %[3]q } } `, rName, usernameClaim, usernamePrefix) @@ -497,15 +498,15 @@ resource "aws_eks_identity_provider_config" "test" { func testAccAWSEksIdentityProviderConfig_Oidc_RequiredClaims(rName, claimsKeyOne, claimsValueOne, claimsKeyTwo, claimsValueTwo string) string { return testAccAWSEksIdentityProviderConfigBase(rName) + fmt.Sprintf(` resource "aws_eks_identity_provider_config" "test" { - cluster_name = aws_eks_cluster.test.name + cluster_name = aws_eks_cluster.test.name oidc { - client_id = "test-url.apps.googleusercontent.com" + client_id = "test-url.apps.googleusercontent.com" identity_provider_config_name = %[1]q - issuer_url = "https://accounts.google.com/.well-known/openid-configuration" - required_claims = { - %[2]q = %[3]q - %[4]q = %[5]q - } + issuer_url = "https://accounts.google.com/.well-known/openid-configuration" + required_claims = { + %[2]q = %[3]q + %[4]q = %[5]q + } } } `, rName, claimsKeyOne, claimsValueOne, claimsKeyTwo, claimsValueTwo) @@ -514,11 +515,11 @@ resource "aws_eks_identity_provider_config" "test" { func testAccAWSEksIdentityProviderConfig_Tags(rName, tagsKeyOne, tagsValueOne, tagsKeyTwo, tagsValueTwo string) string { return testAccAWSEksIdentityProviderConfigBase(rName) + fmt.Sprintf(` resource "aws_eks_identity_provider_config" "test" { - cluster_name = aws_eks_cluster.test.name + cluster_name = aws_eks_cluster.test.name oidc { - client_id = "test-url.apps.googleusercontent.com" + client_id = "test-url.apps.googleusercontent.com" identity_provider_config_name = %[1]q - issuer_url = "https://accounts.google.com/.well-known/openid-configuration" + issuer_url = "https://accounts.google.com/.well-known/openid-configuration" } tags = { %[2]q = %[3]q From 439ca460b77bd99b63801d2a5a1499a85b113028 Mon Sep 17 00:00:00 2001 From: Will Varney Date: Mon, 8 Mar 2021 10:53:05 +0000 Subject: [PATCH 155/398] New resource: aws_eks_identity_provider_config. This adds a new resource for associating an identity provider with an EKS cluster. This commit fixes the formatting of the terraform code in the docs and the indenting of unordered lists. --- ...eks_identity_provider_config.html.markdown | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/website/docs/r/eks_identity_provider_config.html.markdown b/website/docs/r/eks_identity_provider_config.html.markdown index f480b0568b3..d687898e953 100644 --- a/website/docs/r/eks_identity_provider_config.html.markdown +++ b/website/docs/r/eks_identity_provider_config.html.markdown @@ -14,11 +14,11 @@ Manages an EKS Identity Provider Configuration. ```hcl resource "aws_eks_identity_provider_config" "example" { - cluster_name = aws_eks_cluster.example.name + cluster_name = aws_eks_cluster.example.name oidc { - client_id = "your client_id" + client_id = "your client_id" identity_provider_config_name = "example" - issuer_url = "your issuer_url" + issuer_url = "your issuer_url" } } ``` @@ -29,18 +29,18 @@ The following arguments are required: * `cluster_name` – Name of the EKS Cluster. * `oidc` - Nested attribute containing [OpenID Connect](https://openid.net/connect/) identity provider information for the cluster. - * `client_id` – Client ID for the OpenID Connect identity provider. - * `identity_provider_config_name` – The name of the identity provider config. - * `issuer_url` - Issuer URL for the OpenID Connect identity provider. + * `client_id` – Client ID for the OpenID Connect identity provider. + * `identity_provider_config_name` – The name of the identity provider config. + * `issuer_url` - Issuer URL for the OpenID Connect identity provider. The following arguments are optional: * `oidc` - * `groups_claim` - The JWT claim that the provider will use to return groups. - * `groups_prefix` - A prefix that is prepended to group claims e.g. `oidc:`. - * `required_claims` - The key value pairs that describe required claims in the identity token. - * `username_claim` - The JWT claim that the provider will use as the username. - * `username_prefix` - A prefix that is prepended to username claims. + * `groups_claim` - The JWT claim that the provider will use to return groups. + * `groups_prefix` - A prefix that is prepended to group claims e.g. `oidc:`. + * `required_claims` - The key value pairs that describe required claims in the identity token. + * `username_claim` - The JWT claim that the provider will use as the username. + * `username_prefix` - A prefix that is prepended to username claims. * `tags` - (Optional) Key-value map of resource tags. ## Attributes Reference From d647de8868017b2702c71ec7702ddfc1b24037ae Mon Sep 17 00:00:00 2001 From: Will Varney Date: Mon, 8 Mar 2021 13:27:28 +0000 Subject: [PATCH 156/398] New resource: aws_eks_identity_provider_config. This adds a new resource for associating an identity provider with an EKS cluster. This commit renames the changelog entry to the pull request number. --- .changelog/{pending.txt => 17959.txt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .changelog/{pending.txt => 17959.txt} (100%) diff --git a/.changelog/pending.txt b/.changelog/17959.txt similarity index 100% rename from .changelog/pending.txt rename to .changelog/17959.txt From d3812eb3fea8182d47554e15c0b868ee9fc0e1b6 Mon Sep 17 00:00:00 2001 From: Will Varney Date: Fri, 11 Jun 2021 15:07:37 +0100 Subject: [PATCH 157/398] New resource: aws_eks_identity_provider_config. Moving to the new 'expandStringMap' method after pulling updates from upstream main branch. --- aws/resource_aws_eks_identity_provider_config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_eks_identity_provider_config.go b/aws/resource_aws_eks_identity_provider_config.go index e5a27b4cd1c..1bb126a3dc2 100644 --- a/aws/resource_aws_eks_identity_provider_config.go +++ b/aws/resource_aws_eks_identity_provider_config.go @@ -344,7 +344,7 @@ func expandEksOidcIdentityProviderConfigRequest(l []interface{}) (string, *eks.O } if v, ok := m["required_claims"].(map[string]interface{}); ok && len(v) > 0 { - oidcIdentityProviderConfigRequest.RequiredClaims = stringMapToPointers(v) + oidcIdentityProviderConfigRequest.RequiredClaims = expandStringMap(v) } if v, ok := m["username_claim"].(string); ok && v != "" { From 4222c31756da28fcc0fb38ecdfa9eb2365b0cd13 Mon Sep 17 00:00:00 2001 From: Will Varney Date: Tue, 29 Jun 2021 14:48:53 +0100 Subject: [PATCH 158/398] New resource: aws_eks_identity_provider_config. Adding in the required 'ErrorCheck' to fix the awsproviderlint error. Moving from using the background context to the TODO context in the acceptance tests. --- aws/resource_aws_eks_identity_provider_config_test.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/aws/resource_aws_eks_identity_provider_config_test.go b/aws/resource_aws_eks_identity_provider_config_test.go index 4b2207a9898..adcb0f24a6e 100644 --- a/aws/resource_aws_eks_identity_provider_config_test.go +++ b/aws/resource_aws_eks_identity_provider_config_test.go @@ -53,8 +53,7 @@ func testSweepEksIdentityProviderConfigs(region string) error { continue } - // TODO - Should I use context.Background here? - if err := waitForEksIdentityProviderConfigDisassociation(context.Background(), conn, clusterName, config, 10*time.Minute); err != nil { + if err := waitForEksIdentityProviderConfigDisassociation(context.TODO(), conn, clusterName, config, 10*time.Minute); err != nil { errors = multierror.Append(errors, fmt.Errorf("error waiting for EKS Identity Provider Config %q disassociation: %w", configName, err)) continue } @@ -87,6 +86,7 @@ func TestAccAWSEksIdentityProviderConfig_basic(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t) }, + ErrorCheck: testAccErrorCheck(t, eks.EndpointsID), ProviderFactories: testAccProviderFactories, CheckDestroy: testAccCheckAWSEksIdentityProviderConfigDestroy, Steps: []resource.TestStep{ @@ -123,6 +123,7 @@ func TestAccAWSEksIdentityProviderConfig_disappears(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t) }, + ErrorCheck: testAccErrorCheck(t, eks.EndpointsID), ProviderFactories: testAccProviderFactories, CheckDestroy: testAccCheckAWSEksIdentityProviderConfigDestroy, Steps: []resource.TestStep{ @@ -145,6 +146,7 @@ func TestAccAWSEksIdentityProviderConfig_Oidc_Group(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t) }, + ErrorCheck: testAccErrorCheck(t, eks.EndpointsID), ProviderFactories: testAccProviderFactories, CheckDestroy: testAccCheckAWSEksIdentityProviderConfigDestroy, Steps: []resource.TestStep{ @@ -173,6 +175,7 @@ func TestAccAWSEksIdentityProviderConfig_Oidc_Username(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t) }, + ErrorCheck: testAccErrorCheck(t, eks.EndpointsID), ProviderFactories: testAccProviderFactories, CheckDestroy: testAccCheckAWSEksIdentityProviderConfigDestroy, Steps: []resource.TestStep{ @@ -201,6 +204,7 @@ func TestAccAWSEksIdentityProviderConfig_Oidc_RequiredClaims(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t) }, + ErrorCheck: testAccErrorCheck(t, eks.EndpointsID), ProviderFactories: testAccProviderFactories, CheckDestroy: testAccCheckAWSEksIdentityProviderConfigDestroy, Steps: []resource.TestStep{ @@ -237,6 +241,7 @@ func TestAccAWSEksIdentityProviderConfig_Tags(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t) }, + ErrorCheck: testAccErrorCheck(t, eks.EndpointsID), ProviderFactories: testAccProviderFactories, CheckDestroy: testAccCheckAWSEksIdentityProviderConfigDestroy, Steps: []resource.TestStep{ @@ -365,7 +370,7 @@ func testAccCheckAWSEksIdentityProviderConfigDisappears(clusterName string, conf return err } - return waitForEksIdentityProviderConfigDisassociation(context.Background(), conn, clusterName, config, 25*time.Minute) + return waitForEksIdentityProviderConfigDisassociation(context.TODO(), conn, clusterName, config, 25*time.Minute) } } From 2d000de23ac4609097e72b65a69e57f6e676e424 Mon Sep 17 00:00:00 2001 From: Will Varney Date: Wed, 30 Jun 2021 10:16:46 +0100 Subject: [PATCH 159/398] New resource: aws_eks_identity_provider_config. Running a go mod tidy to clean up go.sum. --- go.sum | 5 ----- 1 file changed, 5 deletions(-) diff --git a/go.sum b/go.sum index 15c3f8ef968..009b6a2c58d 100644 --- a/go.sum +++ b/go.sum @@ -50,7 +50,6 @@ github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7ISrnJIXKzwaspym5BTKGx93EI= github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0= github.com/andybalholm/crlf v0.0.0-20171020200849-670099aa064f/go.mod h1:k8feO4+kXDxro6ErPXBRTJ/ro2mf0SsFG8s7doP9kJE= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/apparentlymart/go-cidr v1.0.1 h1:NmIwLZ/KdsjIUlhf+/Np40atNXm/+lZ5txfTJ/SpF+U= github.com/apparentlymart/go-cidr v1.0.1/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= @@ -63,7 +62,6 @@ github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJE github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= github.com/aws/aws-sdk-go v1.25.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= @@ -97,9 +95,7 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= @@ -298,7 +294,6 @@ github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= From eb34721ac26f91852a807d9c414e2e49e9757fd5 Mon Sep 17 00:00:00 2001 From: Will Varney Date: Mon, 5 Jul 2021 15:00:24 +0100 Subject: [PATCH 160/398] New resource: aws_eks_identity_provider_config. Fixing the tfproviderdocs linting error around using 'terraform' and not 'hcl' for the markdown code samples. --- website/docs/r/eks_identity_provider_config.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/eks_identity_provider_config.html.markdown b/website/docs/r/eks_identity_provider_config.html.markdown index d687898e953..1aff5578d6c 100644 --- a/website/docs/r/eks_identity_provider_config.html.markdown +++ b/website/docs/r/eks_identity_provider_config.html.markdown @@ -12,7 +12,7 @@ Manages an EKS Identity Provider Configuration. ## Example Usage -```hcl +```terraform resource "aws_eks_identity_provider_config" "example" { cluster_name = aws_eks_cluster.example.name oidc { From 312700fa615ee161309cbfc5a80204d872d54be4 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 7 Jul 2021 11:25:51 -0400 Subject: [PATCH 161/398] r/aws_eks_identity_provider_config: Use internal finder and waiter packages. --- aws/internal/service/eks/enum.go | 4 + aws/internal/service/eks/finder/finder.go | 33 +++ aws/internal/service/eks/id.go | 19 ++ aws/internal/service/eks/waiter/status.go | 16 ++ aws/internal/service/eks/waiter/waiter.go | 34 +++ ...source_aws_eks_identity_provider_config.go | 246 +++++++--------- ...e_aws_eks_identity_provider_config_test.go | 263 ++++++++---------- ...eks_identity_provider_config.html.markdown | 34 +-- 8 files changed, 344 insertions(+), 305 deletions(-) diff --git a/aws/internal/service/eks/enum.go b/aws/internal/service/eks/enum.go index 8e0f2c21f43..e21b72d590b 100644 --- a/aws/internal/service/eks/enum.go +++ b/aws/internal/service/eks/enum.go @@ -1,5 +1,9 @@ package eks +const ( + IdentityProviderConfigTypeOidc = "oidc" +) + const ( ResourcesSecrets = "secrets" ) diff --git a/aws/internal/service/eks/finder/finder.go b/aws/internal/service/eks/finder/finder.go index 8aef2b0ba6c..3334e44c6eb 100644 --- a/aws/internal/service/eks/finder/finder.go +++ b/aws/internal/service/eks/finder/finder.go @@ -7,6 +7,7 @@ import ( "github.com/aws/aws-sdk-go/service/eks" "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + tfeks "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/eks" ) func AddonByClusterNameAndAddonName(ctx context.Context, conn *eks.EKS, clusterName, addonName string) (*eks.Addon, error) { @@ -214,3 +215,35 @@ func NodegroupUpdateByClusterNameNodegroupNameAndID(conn *eks.EKS, clusterName, return output.Update, nil } + +func OidcIdentityProviderConfigByClusterNameAndConfigName(ctx context.Context, conn *eks.EKS, clusterName, configName string) (*eks.OidcIdentityProviderConfig, error) { + input := &eks.DescribeIdentityProviderConfigInput{ + ClusterName: aws.String(clusterName), + IdentityProviderConfig: &eks.IdentityProviderConfig{ + Name: aws.String(configName), + Type: aws.String(tfeks.IdentityProviderConfigTypeOidc), + }, + } + + output, err := conn.DescribeIdentityProviderConfigWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, eks.ErrCodeResourceNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.IdentityProviderConfig == nil || output.IdentityProviderConfig.Oidc == nil { + return nil, &resource.NotFoundError{ + Message: "Empty result", + LastRequest: input, + } + } + + return output.IdentityProviderConfig.Oidc, nil +} diff --git a/aws/internal/service/eks/id.go b/aws/internal/service/eks/id.go index 8aec4cf9a3e..66c86d64104 100644 --- a/aws/internal/service/eks/id.go +++ b/aws/internal/service/eks/id.go @@ -43,6 +43,25 @@ func FargateProfileParseResourceID(id string) (string, string, error) { return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected cluster-name%[2]sfargate-profile-name", id, fargateProfileResourceIDSeparator) } +const identityProviderConfigResourceIDSeparator = ":" + +func IdentityProviderConfigCreateResourceID(clusterName, configName string) string { + parts := []string{clusterName, configName} + id := strings.Join(parts, identityProviderConfigResourceIDSeparator) + + return id +} + +func IdentityProviderConfigParseResourceID(id string) (string, string, error) { + parts := strings.Split(id, identityProviderConfigResourceIDSeparator) + + if len(parts) == 2 && parts[0] != "" && parts[1] != "" { + return parts[0], parts[1], nil + } + + return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected cluster-name%[2]sconfig-name", id, identityProviderConfigResourceIDSeparator) +} + const nodeGroupResourceIDSeparator = ":" func NodeGroupCreateResourceID(clusterName, nodeGroupName string) string { diff --git a/aws/internal/service/eks/waiter/status.go b/aws/internal/service/eks/waiter/status.go index 96a1a3daa46..c0b4920ef1c 100644 --- a/aws/internal/service/eks/waiter/status.go +++ b/aws/internal/service/eks/waiter/status.go @@ -121,3 +121,19 @@ func NodegroupUpdateStatus(conn *eks.EKS, clusterName, nodeGroupName, id string) return output, aws.StringValue(output.Status), nil } } + +func OidcIdentityProviderConfigStatus(ctx context.Context, conn *eks.EKS, clusterName, configName string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := finder.OidcIdentityProviderConfigByClusterNameAndConfigName(ctx, conn, clusterName, configName) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, aws.StringValue(output.Status), nil + } +} diff --git a/aws/internal/service/eks/waiter/waiter.go b/aws/internal/service/eks/waiter/waiter.go index 171f55c27fe..2a3289dacd8 100644 --- a/aws/internal/service/eks/waiter/waiter.go +++ b/aws/internal/service/eks/waiter/waiter.go @@ -240,3 +240,37 @@ func NodegroupUpdateSuccessful(conn *eks.EKS, clusterName, nodeGroupName, id str return nil, err } + +func OidcIdentityProviderConfigCreated(ctx context.Context, conn *eks.EKS, clusterName, configName string, timeout time.Duration) (*eks.OidcIdentityProviderConfig, error) { + stateConf := resource.StateChangeConf{ + Pending: []string{eks.ConfigStatusCreating}, + Target: []string{eks.ConfigStatusActive}, + Refresh: OidcIdentityProviderConfigStatus(ctx, conn, clusterName, configName), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*eks.OidcIdentityProviderConfig); ok { + return output, err + } + + return nil, err +} + +func OidcIdentityProviderConfigDeleted(ctx context.Context, conn *eks.EKS, clusterName, configName string, timeout time.Duration) (*eks.OidcIdentityProviderConfig, error) { + stateConf := resource.StateChangeConf{ + Pending: []string{eks.ConfigStatusActive, eks.ConfigStatusDeleting}, + Target: []string{}, + Refresh: OidcIdentityProviderConfigStatus(ctx, conn, clusterName, configName), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*eks.OidcIdentityProviderConfig); ok { + return output, err + } + + return nil, err +} diff --git a/aws/resource_aws_eks_identity_provider_config.go b/aws/resource_aws_eks_identity_provider_config.go index 1bb126a3dc2..6f37517df7f 100644 --- a/aws/resource_aws_eks_identity_provider_config.go +++ b/aws/resource_aws_eks_identity_provider_config.go @@ -2,23 +2,24 @@ package aws import ( "context" - "fmt" "log" - "strings" "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/eks" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/go-cty/cty" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "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" + tfeks "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/eks" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/eks/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/eks/waiter" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) -const typeOidc string = "oidc" - func resourceAwsEksIdentityProviderConfig() *schema.Resource { return &schema.Resource{ CreateContext: resourceAwsEksIdentityProviderConfigCreate, @@ -35,16 +36,23 @@ func resourceAwsEksIdentityProviderConfig() *schema.Resource { }, Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "cluster_name": { Type: schema.TypeString, Required: true, ForceNew: true, ValidateFunc: validation.NoZeroValues, }, + "oidc": { Type: schema.TypeList, Required: true, ForceNew: true, + MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "client_id": { @@ -81,14 +89,11 @@ func resourceAwsEksIdentityProviderConfig() *schema.Resource { Type: schema.TypeMap, Optional: true, ForceNew: true, - ValidateDiagFunc: all( + ValidateDiagFunc: allDiagFunc( validation.MapKeyLenBetween(1, 63), validation.MapValueLenBetween(1, 253), ), - Elem: &schema.Schema{ - Type: schema.TypeString, - ForceNew: true, - }, + Elem: &schema.Schema{Type: schema.TypeString}, }, "username_claim": { Type: schema.TypeString, @@ -105,16 +110,19 @@ func resourceAwsEksIdentityProviderConfig() *schema.Resource { }, }, }, - "tags": tagsSchemaForceNew(), + "status": { Type: schema.TypeString, Computed: true, }, + + "tags": tagsSchemaForceNew(), }, } } -func all(validators ...schema.SchemaValidateDiagFunc) schema.SchemaValidateDiagFunc { +// https://github.com/hashicorp/terraform-plugin-sdk/issues/780. +func allDiagFunc(validators ...schema.SchemaValidateDiagFunc) schema.SchemaValidateDiagFunc { return func(i interface{}, k cty.Path) diag.Diagnostics { var diags diag.Diagnostics for _, validator := range validators { @@ -128,15 +136,13 @@ func resourceAwsEksIdentityProviderConfigCreate(ctx context.Context, d *schema.R conn := meta.(*AWSClient).eksconn clusterName := d.Get("cluster_name").(string) - //TODO - Should I break away from the aws sdk api and move the name outside of the oidc map? - configName, oidcRequest := expandEksOidcIdentityProviderConfigRequest(d.Get("oidc").([]interface{})) - - id := fmt.Sprintf("%s:%s", clusterName, configName) + configName, oidc := expandEksOidcIdentityProviderConfigRequest(d.Get("oidc").([]interface{})[0].(map[string]interface{})) + id := tfeks.IdentityProviderConfigCreateResourceID(clusterName, configName) input := &eks.AssociateIdentityProviderConfigInput{ ClientRequestToken: aws.String(resource.UniqueId()), ClusterName: aws.String(clusterName), - Oidc: oidcRequest, + Oidc: oidc, } if v := d.Get("tags").(map[string]interface{}); len(v) > 0 { @@ -144,23 +150,16 @@ func resourceAwsEksIdentityProviderConfigCreate(ctx context.Context, d *schema.R } _, err := conn.AssociateIdentityProviderConfig(input) + if err != nil { return diag.Errorf("error associating EKS Identity Provider Config (%s): %s", id, err) } d.SetId(id) - stateConf := resource.StateChangeConf{ - Pending: []string{eks.ConfigStatusCreating}, - Target: []string{eks.ConfigStatusActive}, - Timeout: d.Timeout(schema.TimeoutCreate), - Refresh: refreshEksIdentityProviderConfigStatus(conn, clusterName, &eks.IdentityProviderConfig{ - Name: aws.String(configName), - Type: aws.String(typeOidc), - }), - } + _, err = waiter.OidcIdentityProviderConfigCreated(ctx, conn, clusterName, configName, d.Timeout(schema.TimeoutCreate)) - if _, err := stateConf.WaitForStateContext(ctx); err != nil { + if err != nil { return diag.Errorf("error waiting for EKS Identity Provider Config (%s) association: %s", d.Id(), err) } @@ -171,22 +170,15 @@ func resourceAwsEksIdentityProviderConfigRead(ctx context.Context, d *schema.Res conn := meta.(*AWSClient).eksconn ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig - clusterName, configName, err := resourceAwsEksIdentityProviderConfigParseId(d.Id()) + clusterName, configName, err := tfeks.IdentityProviderConfigParseResourceID(d.Id()) + if err != nil { return diag.FromErr(err) } - input := &eks.DescribeIdentityProviderConfigInput{ - ClusterName: aws.String(clusterName), - IdentityProviderConfig: &eks.IdentityProviderConfig{ - Name: aws.String(configName), - Type: aws.String(typeOidc), - }, - } + oidc, err := finder.OidcIdentityProviderConfigByClusterNameAndConfigName(ctx, conn, clusterName, configName) - output, err := conn.DescribeIdentityProviderConfigWithContext(ctx, input) - - if isAWSErr(err, eks.ErrCodeResourceNotFoundException, "") { + if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] EKS Identity Provider Config (%s) not found, removing from state", d.Id()) d.SetId("") return nil @@ -196,29 +188,16 @@ func resourceAwsEksIdentityProviderConfigRead(ctx context.Context, d *schema.Res return diag.Errorf("error reading EKS Identity Provider Config (%s): %s", d.Id(), err) } - config := output.IdentityProviderConfig - - if config == nil { - log.Printf("[WARN] EKS Identity Provider Config (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } - - if config.Oidc == nil { - log.Printf("[WARN] EKS OIDC Identity Provider Config (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } - - d.Set("cluster_name", clusterName) + d.Set("arn", oidc.IdentityProviderConfigArn) + d.Set("cluster_name", oidc.ClusterName) - if err := d.Set("oidc", flattenEksOidcIdentityProviderConfig(config.Oidc)); err != nil { + if err := d.Set("oidc", []interface{}{flattenEksOidcIdentityProviderConfig(oidc)}); err != nil { return diag.Errorf("error setting oidc: %s", err) } - d.Set("status", config.Oidc.Status) + d.Set("status", oidc.Status) - if err := d.Set("tags", keyvaluetags.EksKeyValueTags(config.Oidc.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + if err := d.Set("tags", keyvaluetags.EksKeyValueTags(oidc.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { return diag.Errorf("error setting tags: %s", err) } @@ -228,27 +207,26 @@ func resourceAwsEksIdentityProviderConfigRead(ctx context.Context, d *schema.Res func resourceAwsEksIdentityProviderConfigDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*AWSClient).eksconn - clusterName, configName, err := resourceAwsEksIdentityProviderConfigParseId(d.Id()) + clusterName, configName, err := tfeks.IdentityProviderConfigParseResourceID(d.Id()) + if err != nil { return diag.FromErr(err) } - config := &eks.IdentityProviderConfig{ - Name: aws.String(configName), - Type: aws.String(typeOidc), - } - log.Printf("[DEBUG] Disassociating EKS Identity Provider Config: %s", d.Id()) - input := &eks.DisassociateIdentityProviderConfigInput{ - ClusterName: aws.String(clusterName), - IdentityProviderConfig: config, - } + _, err = conn.DisassociateIdentityProviderConfigWithContext(ctx, &eks.DisassociateIdentityProviderConfigInput{ + ClusterName: aws.String(clusterName), + IdentityProviderConfig: &eks.IdentityProviderConfig{ + Name: aws.String(configName), + Type: aws.String(tfeks.IdentityProviderConfigTypeOidc), + }, + }) - _, err = conn.DisassociateIdentityProviderConfigWithContext(ctx, input) + if tfawserr.ErrCodeEquals(err, eks.ErrCodeResourceNotFoundException) { + return nil + } - // TODO - Is checking for the exception message too brittle? - if isAWSErr(err, eks.ErrCodeResourceNotFoundException, "") || - isAWSErr(err, eks.ErrCodeInvalidRequestException, "Identity provider config is not associated with cluster") { + if tfawserr.ErrMessageContains(err, eks.ErrCodeInvalidRequestException, "Identity provider config is not associated with cluster") { return nil } @@ -256,123 +234,97 @@ func resourceAwsEksIdentityProviderConfigDelete(ctx context.Context, d *schema.R return diag.Errorf("error disassociating EKS Identity Provider Config (%s): %s", d.Id(), err) } - if err := waitForEksIdentityProviderConfigDisassociation(ctx, conn, clusterName, config, d.Timeout(schema.TimeoutDelete)); err != nil { + _, err = waiter.OidcIdentityProviderConfigDeleted(ctx, conn, clusterName, configName, d.Timeout(schema.TimeoutDelete)) + + if err != nil { return diag.Errorf("error waiting for EKS Identity Provider Config (%s) disassociation: %s", d.Id(), err) } return nil } -func refreshEksIdentityProviderConfigStatus(conn *eks.EKS, clusterName string, config *eks.IdentityProviderConfig) resource.StateRefreshFunc { - return func() (interface{}, string, error) { - input := &eks.DescribeIdentityProviderConfigInput{ - ClusterName: aws.String(clusterName), - IdentityProviderConfig: config, - } - - output, err := conn.DescribeIdentityProviderConfig(input) - - if err != nil { - return "", "", err - } - - identityProviderConfig := output.IdentityProviderConfig +func expandEksOidcIdentityProviderConfigRequest(tfMap map[string]interface{}) (string, *eks.OidcIdentityProviderConfigRequest) { + if tfMap == nil { + return "", nil + } - if identityProviderConfig == nil { - return identityProviderConfig, "", fmt.Errorf("EKS Identity Provider Config (%s:%s) missing", clusterName, aws.StringValue(config.Name)) - } + apiObject := &eks.OidcIdentityProviderConfigRequest{} - oidc := identityProviderConfig.Oidc + if v, ok := tfMap["client_id"].(string); ok && v != "" { + apiObject.ClientId = aws.String(v) + } - if oidc == nil { - return identityProviderConfig, "", fmt.Errorf("EKS OIDC Identity Provider Config (%s:%s) missing", clusterName, aws.StringValue(config.Name)) - } + if v, ok := tfMap["groups_claim"].(string); ok && v != "" { + apiObject.GroupsClaim = aws.String(v) + } - return identityProviderConfig, aws.StringValue(oidc.Status), nil + if v, ok := tfMap["groups_prefix"].(string); ok && v != "" { + apiObject.GroupsPrefix = aws.String(v) } -} -func waitForEksIdentityProviderConfigDisassociation(ctx context.Context, conn *eks.EKS, clusterName string, config *eks.IdentityProviderConfig, timeout time.Duration) error { - stateConf := resource.StateChangeConf{ - Pending: []string{ - eks.ConfigStatusActive, - eks.ConfigStatusDeleting, - }, - Target: []string{""}, - Timeout: timeout, - Refresh: refreshEksIdentityProviderConfigStatus(conn, clusterName, config), + var identityProviderConfigName string + if v, ok := tfMap["identity_provider_config_name"].(string); ok && v != "" { + identityProviderConfigName = v + apiObject.IdentityProviderConfigName = aws.String(v) } - _, err := stateConf.WaitForStateContext(ctx) - if isAWSErr(err, eks.ErrCodeResourceNotFoundException, "") { - return nil + if v, ok := tfMap["issuer_url"].(string); ok && v != "" { + apiObject.IssuerUrl = aws.String(v) } - return err -} + if v, ok := tfMap["required_claims"].(map[string]interface{}); ok && len(v) > 0 { + apiObject.RequiredClaims = expandStringMap(v) + } -func resourceAwsEksIdentityProviderConfigParseId(id string) (string, string, error) { - parts := strings.Split(id, ":") + if v, ok := tfMap["username_claim"].(string); ok && v != "" { + apiObject.UsernameClaim = aws.String(v) + } - if len(parts) != 2 || parts[0] == "" || parts[1] == "" { - return "", "", fmt.Errorf("unexpected format of ID (%s), expected cluster-name:identity-provider-config-name", id) + if v, ok := tfMap["username_prefix"].(string); ok && v != "" { + apiObject.UsernamePrefix = aws.String(v) } - return parts[0], parts[1], nil + return identityProviderConfigName, apiObject } -func expandEksOidcIdentityProviderConfigRequest(l []interface{}) (string, *eks.OidcIdentityProviderConfigRequest) { - if len(l) == 0 { - return "", nil +func flattenEksOidcIdentityProviderConfig(apiObject *eks.OidcIdentityProviderConfig) map[string]interface{} { + if apiObject == nil { + return nil } - m := l[0].(map[string]interface{}) + tfMap := map[string]interface{}{} - configName := m["identity_provider_config_name"].(string) - oidcIdentityProviderConfigRequest := &eks.OidcIdentityProviderConfigRequest{ - ClientId: aws.String(m["client_id"].(string)), - IdentityProviderConfigName: aws.String(configName), - IssuerUrl: aws.String(m["issuer_url"].(string)), + if v := apiObject.ClientId; v != nil { + tfMap["client_id"] = aws.StringValue(v) } - if v, ok := m["groups_claim"].(string); ok && v != "" { - oidcIdentityProviderConfigRequest.GroupsClaim = aws.String(v) + if v := apiObject.GroupsClaim; v != nil { + tfMap["groups_claim"] = aws.StringValue(v) } - if v, ok := m["groups_prefix"].(string); ok && v != "" { - oidcIdentityProviderConfigRequest.GroupsPrefix = aws.String(v) + if v := apiObject.GroupsPrefix; v != nil { + tfMap["groups_prefix"] = aws.StringValue(v) } - if v, ok := m["required_claims"].(map[string]interface{}); ok && len(v) > 0 { - oidcIdentityProviderConfigRequest.RequiredClaims = expandStringMap(v) + if v := apiObject.IdentityProviderConfigName; v != nil { + tfMap["identity_provider_config_name"] = aws.StringValue(v) } - if v, ok := m["username_claim"].(string); ok && v != "" { - oidcIdentityProviderConfigRequest.UsernameClaim = aws.String(v) + if v := apiObject.IssuerUrl; v != nil { + tfMap["issuer_url"] = aws.StringValue(v) } - if v, ok := m["username_prefix"].(string); ok && v != "" { - oidcIdentityProviderConfigRequest.UsernamePrefix = aws.String(v) + if v := apiObject.RequiredClaims; v != nil { + tfMap["required_claims"] = aws.StringValueMap(v) } - return configName, oidcIdentityProviderConfigRequest -} - -func flattenEksOidcIdentityProviderConfig(config *eks.OidcIdentityProviderConfig) []map[string]interface{} { - if config == nil { - return []map[string]interface{}{} + if v := apiObject.UsernameClaim; v != nil { + tfMap["username_claim"] = aws.StringValue(v) } - m := map[string]interface{}{ - "client_id": aws.StringValue(config.ClientId), - "groups_claim": aws.StringValue(config.GroupsClaim), - "groups_prefix": aws.StringValue(config.GroupsPrefix), - "identity_provider_config_name": aws.StringValue(config.IdentityProviderConfigName), - "issuer_url": aws.StringValue(config.IssuerUrl), - "required_claims": aws.StringValueMap(config.RequiredClaims), - "username_claim": aws.StringValue(config.UsernameClaim), - "username_prefix": aws.StringValue(config.UsernamePrefix), + if v := apiObject.UsernamePrefix; v != nil { + tfMap["username_prefix"] = aws.StringValue(v) } - return []map[string]interface{}{m} + return tfMap } diff --git a/aws/resource_aws_eks_identity_provider_config_test.go b/aws/resource_aws_eks_identity_provider_config_test.go index adcb0f24a6e..72bd5d6b3c2 100644 --- a/aws/resource_aws_eks_identity_provider_config_test.go +++ b/aws/resource_aws_eks_identity_provider_config_test.go @@ -6,14 +6,16 @@ import ( "log" "regexp" "testing" - "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/eks" - "github.com/hashicorp/go-multierror" + multierror "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + tfeks "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/eks" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/eks/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func init() { @@ -26,63 +28,72 @@ func init() { func testSweepEksIdentityProviderConfigs(region string) error { client, err := sharedClientForRegion(region) if err != nil { - return fmt.Errorf("error getting client: %s", err) + return fmt.Errorf("error getting client: %w", err) } + ctx := context.TODO() conn := client.(*AWSClient).eksconn - - var errors error input := &eks.ListClustersInput{} - err = conn.ListClustersPages(input, func(page *eks.ListClustersOutput, lastPage bool) bool { + var sweeperErrs *multierror.Error + sweepResources := make([]*testSweepResource, 0) + + err = conn.ListClustersPagesWithContext(ctx, input, func(page *eks.ListClustersOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + for _, cluster := range page.Clusters { - clusterName := aws.StringValue(cluster) input := &eks.ListIdentityProviderConfigsInput{ ClusterName: cluster, } - err := conn.ListIdentityProviderConfigsPages(input, func(page *eks.ListIdentityProviderConfigsOutput, lastPage bool) bool { - for _, config := range page.IdentityProviderConfigs { - configName := aws.StringValue(config.Name) - log.Printf("[INFO] Disassociating Identity Provider Config %q", configName) - input := &eks.DisassociateIdentityProviderConfigInput{ - ClusterName: cluster, - IdentityProviderConfig: config, - } - _, err := conn.DisassociateIdentityProviderConfig(input) - - if err != nil && !isAWSErr(err, eks.ErrCodeResourceNotFoundException, "") { - errors = multierror.Append(errors, fmt.Errorf("error disassociating Identity Provider Config %q: %w", configName, err)) - continue - } - - if err := waitForEksIdentityProviderConfigDisassociation(context.TODO(), conn, clusterName, config, 10*time.Minute); err != nil { - errors = multierror.Append(errors, fmt.Errorf("error waiting for EKS Identity Provider Config %q disassociation: %w", configName, err)) - continue - } + + err := conn.ListIdentityProviderConfigsPagesWithContext(ctx, input, func(page *eks.ListIdentityProviderConfigsOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, identityProviderConfig := range page.IdentityProviderConfigs { + r := resourceAwsEksIdentityProviderConfig() + d := r.Data(nil) + d.SetId(tfeks.IdentityProviderConfigCreateResourceID(aws.StringValue(cluster), aws.StringValue(identityProviderConfig.Name))) + + sweepResources = append(sweepResources, NewTestSweepResource(r, d, client)) } - return true + + return !lastPage }) + if err != nil { - errors = multierror.Append(errors, fmt.Errorf("error listing Identity Provider Configs for EKS Cluster %s: %w", clusterName, err)) + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing EKS Identity Provider Configs (%s): %w", region, err)) } } - return true + return !lastPage }) + if testSweepSkipSweepError(err) { - log.Printf("[WARN] Skipping EKS Clusters sweep for %s: %s", region, err) - return errors // In case we have completed some pages, but had errors + log.Print(fmt.Errorf("[WARN] Skipping EKS Identity Provider Configs sweep for %s: %w", region, err)) + return sweeperErrs // In case we have completed some pages, but had errors + } + + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing EKS Clusters (%s): %w", region, err)) } + + err = testSweepResourceOrchestrator(sweepResources) + if err != nil { - errors = multierror.Append(errors, fmt.Errorf("error retrieving EKS Clusters: %w", err)) + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping EKS Identity Provider Configs (%s): %w", region, err)) } - return errors + return sweeperErrs.ErrorOrNil() } func TestAccAWSEksIdentityProviderConfig_basic(t *testing.T) { - var config eks.IdentityProviderConfig + var config eks.OidcIdentityProviderConfig rName := acctest.RandomWithPrefix("tf-acc-test") eksClusterResourceName := "aws_eks_cluster.test" resourceName := "aws_eks_identity_provider_config.test" + ctx := context.TODO() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t) }, @@ -91,19 +102,24 @@ func TestAccAWSEksIdentityProviderConfig_basic(t *testing.T) { CheckDestroy: testAccCheckAWSEksIdentityProviderConfigDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSEksIdentityProviderConfigProvider_Oidc_IssuerUrl(rName, "http://accounts.google.com/.well-known/openid-configuration"), - ExpectError: regexp.MustCompile(`expected .* to have a url with schema of: "https", got http://accounts.google.com/.well-known/openid-configuration`), + Config: testAccAWSEksIdentityProviderConfigProvider_Oidc_IssuerUrl(rName, "http://example.com"), + ExpectError: regexp.MustCompile(`expected .* to have a url with schema of: "https", got http://example.com`), }, { Config: testAccAWSEksIdentityProviderConfigProviderConfigName(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSEksIdentityProviderConfigExists(resourceName, &config), + testAccCheckAWSEksIdentityProviderConfigExists(ctx, resourceName, &config), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "eks", regexp.MustCompile(fmt.Sprintf("identityproviderconfig/%[1]s/oidc/%[1]s/.+", rName))), resource.TestCheckResourceAttrPair(resourceName, "cluster_name", eksClusterResourceName, "name"), resource.TestCheckResourceAttr(resourceName, "oidc.#", "1"), - resource.TestCheckResourceAttr(resourceName, "oidc.0.client_id", "test-url.apps.googleusercontent.com"), + resource.TestCheckResourceAttr(resourceName, "oidc.0.client_id", "example.net"), + resource.TestCheckResourceAttr(resourceName, "oidc.0.groups_claim", ""), + resource.TestCheckResourceAttr(resourceName, "oidc.0.groups_prefix", ""), resource.TestCheckResourceAttr(resourceName, "oidc.0.identity_provider_config_name", rName), - resource.TestCheckResourceAttr(resourceName, "oidc.0.issuer_url", "https://accounts.google.com/.well-known/openid-configuration"), + resource.TestCheckResourceAttr(resourceName, "oidc.0.issuer_url", "https://example.com"), resource.TestCheckResourceAttr(resourceName, "oidc.0.required_claims.%", "0"), + resource.TestCheckResourceAttr(resourceName, "oidc.0.username_claim", ""), + resource.TestCheckResourceAttr(resourceName, "oidc.0.username_prefix", ""), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, @@ -117,9 +133,10 @@ func TestAccAWSEksIdentityProviderConfig_basic(t *testing.T) { } func TestAccAWSEksIdentityProviderConfig_disappears(t *testing.T) { - var config eks.IdentityProviderConfig + var config eks.OidcIdentityProviderConfig rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_eks_identity_provider_config.test" + ctx := context.TODO() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t) }, @@ -130,8 +147,8 @@ func TestAccAWSEksIdentityProviderConfig_disappears(t *testing.T) { { Config: testAccAWSEksIdentityProviderConfigProviderConfigName(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSEksIdentityProviderConfigExists(resourceName, &config), - testAccCheckAWSEksIdentityProviderConfigDisappears(rName, &config), + testAccCheckAWSEksIdentityProviderConfigExists(ctx, resourceName, &config), + testAccCheckResourceDisappears(testAccProvider, resourceAwsEksIdentityProviderConfig(), resourceName), ), ExpectNonEmptyPlan: true, }, @@ -140,9 +157,10 @@ func TestAccAWSEksIdentityProviderConfig_disappears(t *testing.T) { } func TestAccAWSEksIdentityProviderConfig_Oidc_Group(t *testing.T) { - var config eks.IdentityProviderConfig + var config eks.OidcIdentityProviderConfig rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_eks_identity_provider_config.test" + ctx := context.TODO() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t) }, @@ -153,7 +171,7 @@ func TestAccAWSEksIdentityProviderConfig_Oidc_Group(t *testing.T) { { Config: testAccAWSEksIdentityProviderConfigProvider_Oidc_Groups(rName, "groups", "oidc:"), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSEksIdentityProviderConfigExists(resourceName, &config), + testAccCheckAWSEksIdentityProviderConfigExists(ctx, resourceName, &config), resource.TestCheckResourceAttr(resourceName, "oidc.#", "1"), resource.TestCheckResourceAttr(resourceName, "oidc.0.groups_claim", "groups"), resource.TestCheckResourceAttr(resourceName, "oidc.0.groups_prefix", "oidc:"), @@ -169,9 +187,10 @@ func TestAccAWSEksIdentityProviderConfig_Oidc_Group(t *testing.T) { } func TestAccAWSEksIdentityProviderConfig_Oidc_Username(t *testing.T) { - var config eks.IdentityProviderConfig + var config eks.OidcIdentityProviderConfig rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_eks_identity_provider_config.test" + ctx := context.TODO() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t) }, @@ -182,7 +201,7 @@ func TestAccAWSEksIdentityProviderConfig_Oidc_Username(t *testing.T) { { Config: testAccAWSEksIdentityProviderConfigProvider_Oidc_Username(rName, "email", "-"), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSEksIdentityProviderConfigExists(resourceName, &config), + testAccCheckAWSEksIdentityProviderConfigExists(ctx, resourceName, &config), resource.TestCheckResourceAttr(resourceName, "oidc.#", "1"), resource.TestCheckResourceAttr(resourceName, "oidc.0.username_claim", "email"), resource.TestCheckResourceAttr(resourceName, "oidc.0.username_prefix", "-"), @@ -198,9 +217,10 @@ func TestAccAWSEksIdentityProviderConfig_Oidc_Username(t *testing.T) { } func TestAccAWSEksIdentityProviderConfig_Oidc_RequiredClaims(t *testing.T) { - var config eks.IdentityProviderConfig + var config eks.OidcIdentityProviderConfig rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_eks_identity_provider_config.test" + ctx := context.TODO() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t) }, @@ -219,7 +239,7 @@ func TestAccAWSEksIdentityProviderConfig_Oidc_RequiredClaims(t *testing.T) { { Config: testAccAWSEksIdentityProviderConfig_Oidc_RequiredClaims(rName, "keyOne", "valueOne", "keyTwo", "valueTwo"), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSEksIdentityProviderConfigExists(resourceName, &config), + testAccCheckAWSEksIdentityProviderConfigExists(ctx, resourceName, &config), resource.TestCheckResourceAttr(resourceName, "oidc.0.required_claims.%", "2"), resource.TestCheckResourceAttr(resourceName, "oidc.0.required_claims.keyOne", "valueOne"), resource.TestCheckResourceAttr(resourceName, "oidc.0.required_claims.keyTwo", "valueTwo"), @@ -235,9 +255,10 @@ func TestAccAWSEksIdentityProviderConfig_Oidc_RequiredClaims(t *testing.T) { } func TestAccAWSEksIdentityProviderConfig_Tags(t *testing.T) { - var config eks.IdentityProviderConfig + var config eks.OidcIdentityProviderConfig rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_eks_identity_provider_config.test" + ctx := context.TODO() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t) }, @@ -248,7 +269,7 @@ func TestAccAWSEksIdentityProviderConfig_Tags(t *testing.T) { { Config: testAccAWSEksIdentityProviderConfig_Tags(rName, "keyOne", "valueOne", "keyTwo", "valueTwo"), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSEksIdentityProviderConfigExists(resourceName, &config), + testAccCheckAWSEksIdentityProviderConfigExists(ctx, resourceName, &config), resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), resource.TestCheckResourceAttr(resourceName, "tags.keyOne", "valueOne"), resource.TestCheckResourceAttr(resourceName, "tags.keyTwo", "valueTwo"), @@ -263,7 +284,7 @@ func TestAccAWSEksIdentityProviderConfig_Tags(t *testing.T) { }) } -func testAccCheckAWSEksIdentityProviderConfigExists(resourceName string, config *eks.IdentityProviderConfig) resource.TestCheckFunc { +func testAccCheckAWSEksIdentityProviderConfigExists(ctx context.Context, resourceName string, config *eks.OidcIdentityProviderConfig) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] if !ok { @@ -271,52 +292,31 @@ func testAccCheckAWSEksIdentityProviderConfigExists(resourceName string, config } if rs.Primary.ID == "" { - return fmt.Errorf("No EKS Identity Profile Config is set") + return fmt.Errorf("No EKS Identity Profile Config ID is set") } - clusterName, configName, err := resourceAwsEksIdentityProviderConfigParseId(rs.Primary.ID) + clusterName, configName, err := tfeks.IdentityProviderConfigParseResourceID(rs.Primary.ID) + if err != nil { return err } conn := testAccProvider.Meta().(*AWSClient).eksconn - input := &eks.DescribeIdentityProviderConfigInput{ - ClusterName: aws.String(clusterName), - IdentityProviderConfig: &eks.IdentityProviderConfig{ - Name: aws.String(configName), - Type: aws.String(typeOidc), - }, - } - - output, err := conn.DescribeIdentityProviderConfig(input) + output, err := finder.OidcIdentityProviderConfigByClusterNameAndConfigName(ctx, conn, clusterName, configName) if err != nil { return err } - if output == nil || output.IdentityProviderConfig == nil { - return fmt.Errorf("EKS Identity Provider Config (%s) not found", rs.Primary.ID) - } - - if aws.StringValue(output.IdentityProviderConfig.Oidc.IdentityProviderConfigName) != configName { - return fmt.Errorf("EKS OIDC Identity Provider Config (%s) not found", rs.Primary.ID) - } - - if got, want := aws.StringValue(output.IdentityProviderConfig.Oidc.Status), eks.ConfigStatusActive; got != want { - return fmt.Errorf("EKS OIDC Identity Provider Config (%s) not in %s status, got: %s", rs.Primary.ID, want, got) - } - - *config = eks.IdentityProviderConfig{ - Name: output.IdentityProviderConfig.Oidc.IdentityProviderConfigName, - Type: aws.String(typeOidc), - } + *config = *output return nil } } func testAccCheckAWSEksIdentityProviderConfigDestroy(s *terraform.State) error { + ctx := context.TODO() conn := testAccProvider.Meta().(*AWSClient).eksconn for _, rs := range s.RootModule().Resources { @@ -324,54 +324,26 @@ func testAccCheckAWSEksIdentityProviderConfigDestroy(s *terraform.State) error { continue } - clusterName, configName, err := resourceAwsEksIdentityProviderConfigParseId(rs.Primary.ID) + clusterName, configName, err := tfeks.IdentityProviderConfigParseResourceID(rs.Primary.ID) + if err != nil { return err } - input := &eks.DescribeIdentityProviderConfigInput{ - ClusterName: aws.String(clusterName), - IdentityProviderConfig: &eks.IdentityProviderConfig{ - Name: aws.String(configName), - Type: aws.String(typeOidc), - }, - } + _, err = finder.OidcIdentityProviderConfigByClusterNameAndConfigName(ctx, conn, clusterName, configName) - output, err := conn.DescribeIdentityProviderConfig(input) - - if isAWSErr(err, eks.ErrCodeResourceNotFoundException, "") { + if tfresource.NotFound(err) { continue } - if output != nil && output.IdentityProviderConfig != nil && aws.StringValue(output.IdentityProviderConfig.Oidc.IdentityProviderConfigName) == configName { - return fmt.Errorf("EKS Identity Provider Config (%s) still exists", rs.Primary.ID) - } - } - - return nil -} - -func testAccCheckAWSEksIdentityProviderConfigDisappears(clusterName string, config *eks.IdentityProviderConfig) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).eksconn - - input := &eks.DisassociateIdentityProviderConfigInput{ - ClusterName: aws.String(clusterName), - IdentityProviderConfig: config, - } - - _, err := conn.DisassociateIdentityProviderConfig(input) - - if isAWSErr(err, eks.ErrCodeResourceNotFoundException, "") { - return nil - } - if err != nil { return err } - return waitForEksIdentityProviderConfigDisassociation(context.TODO(), conn, clusterName, config, 25*time.Minute) + return fmt.Errorf("EKS Identity Profile Config %s still exists", rs.Primary.ID) } + + return nil } func testAccAWSEksIdentityProviderConfigBase(rName string) string { @@ -387,8 +359,8 @@ data "aws_availability_zones" "available" { data "aws_partition" "current" {} -resource "aws_iam_role" "cluster" { - name = "%[1]s-cluster" +resource "aws_iam_role" "test" { + name = %[1]q assume_role_policy = jsonencode({ Statement = [{ @@ -404,7 +376,7 @@ resource "aws_iam_role" "cluster" { resource "aws_iam_role_policy_attachment" "cluster-AmazonEKSClusterPolicy" { policy_arn = "arn:${data.aws_partition.current.partition}:iam::aws:policy/AmazonEKSClusterPolicy" - role = aws_iam_role.cluster.name + role = aws_iam_role.test.name } resource "aws_vpc" "test" { @@ -413,7 +385,7 @@ resource "aws_vpc" "test" { enable_dns_support = true tags = { - Name = "tf-acc-test-eks-identity-provider-config" + Name = %[1]q "kubernetes.io/cluster/%[1]s" = "shared" } } @@ -426,14 +398,14 @@ resource "aws_subnet" "test" { vpc_id = aws_vpc.test.id tags = { - Name = "tf-acc-test-eks-identity-provider-config" + Name = %[1]q "kubernetes.io/cluster/%[1]s" = "shared" } } resource "aws_eks_cluster" "test" { name = %[1]q - role_arn = aws_iam_role.cluster.arn + role_arn = aws_iam_role.test.arn vpc_config { subnet_ids = aws_subnet.test[*].id @@ -445,91 +417,98 @@ resource "aws_eks_cluster" "test" { } func testAccAWSEksIdentityProviderConfigProviderConfigName(rName string) string { - return testAccAWSEksIdentityProviderConfigBase(rName) + fmt.Sprintf(` + return composeConfig(testAccAWSEksIdentityProviderConfigBase(rName), fmt.Sprintf(` resource "aws_eks_identity_provider_config" "test" { cluster_name = aws_eks_cluster.test.name + oidc { - client_id = "test-url.apps.googleusercontent.com" + client_id = "example.net" identity_provider_config_name = %[1]q - issuer_url = "https://accounts.google.com/.well-known/openid-configuration" + issuer_url = "https://example.com" } } -`, rName) +`, rName)) } func testAccAWSEksIdentityProviderConfigProvider_Oidc_IssuerUrl(rName, issuerUrl string) string { - return testAccAWSEksIdentityProviderConfigBase(rName) + fmt.Sprintf(` + return composeConfig(testAccAWSEksIdentityProviderConfigBase(rName), fmt.Sprintf(` resource "aws_eks_identity_provider_config" "test" { cluster_name = aws_eks_cluster.test.name + oidc { - client_id = "test-url.apps.googleusercontent.com" + client_id = "example.net" identity_provider_config_name = %[1]q issuer_url = %[2]q } } -`, rName, issuerUrl) +`, rName, issuerUrl)) } func testAccAWSEksIdentityProviderConfigProvider_Oidc_Groups(rName, groupsClaim, groupsPrefix string) string { - return testAccAWSEksIdentityProviderConfigBase(rName) + fmt.Sprintf(` + return composeConfig(testAccAWSEksIdentityProviderConfigBase(rName), fmt.Sprintf(` resource "aws_eks_identity_provider_config" "test" { cluster_name = aws_eks_cluster.test.name + oidc { - client_id = "test-url.apps.googleusercontent.com" + client_id = "example.net" groups_claim = %[2]q groups_prefix = %[3]q identity_provider_config_name = %[1]q - issuer_url = "https://accounts.google.com/.well-known/openid-configuration" + issuer_url = "https://example.com" } } -`, rName, groupsClaim, groupsPrefix) +`, rName, groupsClaim, groupsPrefix)) } func testAccAWSEksIdentityProviderConfigProvider_Oidc_Username(rName, usernameClaim, usernamePrefix string) string { - return testAccAWSEksIdentityProviderConfigBase(rName) + fmt.Sprintf(` + return composeConfig(testAccAWSEksIdentityProviderConfigBase(rName), fmt.Sprintf(` resource "aws_eks_identity_provider_config" "test" { cluster_name = aws_eks_cluster.test.name + oidc { - client_id = "test-url.apps.googleusercontent.com" + client_id = "example.net" identity_provider_config_name = %[1]q - issuer_url = "https://accounts.google.com/.well-known/openid-configuration" + issuer_url = "https://example.com" username_claim = %[2]q username_prefix = %[3]q } } -`, rName, usernameClaim, usernamePrefix) +`, rName, usernameClaim, usernamePrefix)) } func testAccAWSEksIdentityProviderConfig_Oidc_RequiredClaims(rName, claimsKeyOne, claimsValueOne, claimsKeyTwo, claimsValueTwo string) string { - return testAccAWSEksIdentityProviderConfigBase(rName) + fmt.Sprintf(` + return composeConfig(testAccAWSEksIdentityProviderConfigBase(rName), fmt.Sprintf(` resource "aws_eks_identity_provider_config" "test" { cluster_name = aws_eks_cluster.test.name + oidc { - client_id = "test-url.apps.googleusercontent.com" + client_id = "example.net" identity_provider_config_name = %[1]q - issuer_url = "https://accounts.google.com/.well-known/openid-configuration" + issuer_url = "https://example.com" required_claims = { %[2]q = %[3]q %[4]q = %[5]q } } } -`, rName, claimsKeyOne, claimsValueOne, claimsKeyTwo, claimsValueTwo) +`, rName, claimsKeyOne, claimsValueOne, claimsKeyTwo, claimsValueTwo)) } func testAccAWSEksIdentityProviderConfig_Tags(rName, tagsKeyOne, tagsValueOne, tagsKeyTwo, tagsValueTwo string) string { - return testAccAWSEksIdentityProviderConfigBase(rName) + fmt.Sprintf(` + return composeConfig(testAccAWSEksIdentityProviderConfigBase(rName), fmt.Sprintf(` resource "aws_eks_identity_provider_config" "test" { cluster_name = aws_eks_cluster.test.name + oidc { - client_id = "test-url.apps.googleusercontent.com" + client_id = "example.net" identity_provider_config_name = %[1]q - issuer_url = "https://accounts.google.com/.well-known/openid-configuration" + issuer_url = "https://example.com" } + tags = { %[2]q = %[3]q %[4]q = %[5]q } } -`, rName, tagsKeyOne, tagsValueOne, tagsKeyTwo, tagsValueTwo) +`, rName, tagsKeyOne, tagsValueOne, tagsKeyTwo, tagsValueTwo)) } diff --git a/website/docs/r/eks_identity_provider_config.html.markdown b/website/docs/r/eks_identity_provider_config.html.markdown index 1aff5578d6c..ac6840ce8be 100644 --- a/website/docs/r/eks_identity_provider_config.html.markdown +++ b/website/docs/r/eks_identity_provider_config.html.markdown @@ -3,7 +3,7 @@ subcategory: "EKS" layout: "aws" page_title: "AWS: aws_eks_identity_provider_config" description: |- - Manages an EKS Identity Provider Configuration + Manages an EKS Identity Provider Configuration. --- # Resource: aws_eks_identity_provider_config @@ -15,6 +15,7 @@ Manages an EKS Identity Provider Configuration. ```terraform resource "aws_eks_identity_provider_config" "example" { cluster_name = aws_eks_cluster.example.name + oidc { client_id = "your client_id" identity_provider_config_name = "example" @@ -25,27 +26,28 @@ resource "aws_eks_identity_provider_config" "example" { ## Argument Reference -The following arguments are required: +The following arguments are supported: -* `cluster_name` – Name of the EKS Cluster. -* `oidc` - Nested attribute containing [OpenID Connect](https://openid.net/connect/) identity provider information for the cluster. - * `client_id` – Client ID for the OpenID Connect identity provider. - * `identity_provider_config_name` – The name of the identity provider config. - * `issuer_url` - Issuer URL for the OpenID Connect identity provider. +* `cluster_name` – (Required) Name of the EKS Cluster. +* `oidc` - (Required) Nested attribute containing [OpenID Connect](https://openid.net/connect/) identity provider information for the cluster. Detailed below. +* `tags` - (Optional) A map of tags to assign to the resource. -The following arguments are optional: +### oidc Configuration Block -* `oidc` - * `groups_claim` - The JWT claim that the provider will use to return groups. - * `groups_prefix` - A prefix that is prepended to group claims e.g. `oidc:`. - * `required_claims` - The key value pairs that describe required claims in the identity token. - * `username_claim` - The JWT claim that the provider will use as the username. - * `username_prefix` - A prefix that is prepended to username claims. -* `tags` - (Optional) Key-value map of resource tags. +* `client_id` – (Required) Client ID for the OpenID Connect identity provider. +* `groups_claim` - (Optional) The JWT claim that the provider will use to return groups. +* `groups_prefix` - (Optional) A prefix that is prepended to group claims e.g. `oidc:`. +* `identity_provider_config_name` – (Required) The name of the identity provider config. +* `issuer_url` - (Required) Issuer URL for the OpenID Connect identity provider. +* `required_claims` - (Optional) The key value pairs that describe required claims in the identity token. +* `username_claim` - (Optional) The JWT claim that the provider will use as the username. +* `username_prefix` - (Optional) A prefix that is prepended to username claims. ## Attributes Reference + In addition to all arguments above, the following attributes are exported: +* `arn` - Amazon Resource Name (ARN) of the EKS Identity Provider Configuration. * `id` - EKS Cluster name and EKS Identity Provider Configuration name separated by a colon (`:`). * `status` - Status of the EKS Identity Provider Configuration. @@ -58,7 +60,7 @@ In addition to all arguments above, the following attributes are exported: ## Import -EKS Identity Provider Configuration can be imported using the `cluster_name` and `identity_provider_config_name` separated by a colon (`:`), e.g. +EKS Identity Provider Configurations can be imported using the `cluster_name` and `identity_provider_config_name` separated by a colon (`:`), e.g. ``` $ terraform import aws_eks_identity_provider_config.my_identity_provider_config my_cluster:my_identity_provider_config From 1d7e5d1eb62c5c41d66c3b1e31b0974d823c877a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 7 Jul 2021 12:28:35 -0400 Subject: [PATCH 162/398] r/aws_eks_identity_provider_config: Tags can be updated; Add 'tags_all'. --- ...source_aws_eks_identity_provider_config.go | 37 ++++++++++++++++--- ...eks_identity_provider_config.html.markdown | 3 +- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/aws/resource_aws_eks_identity_provider_config.go b/aws/resource_aws_eks_identity_provider_config.go index 6f37517df7f..452fee75d96 100644 --- a/aws/resource_aws_eks_identity_provider_config.go +++ b/aws/resource_aws_eks_identity_provider_config.go @@ -24,12 +24,15 @@ func resourceAwsEksIdentityProviderConfig() *schema.Resource { return &schema.Resource{ CreateContext: resourceAwsEksIdentityProviderConfigCreate, ReadContext: resourceAwsEksIdentityProviderConfigRead, + UpdateContext: resourceAwsEksIdentityProviderConfigUpdate, DeleteContext: resourceAwsEksIdentityProviderConfigDelete, Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, + CustomizeDiff: SetTagsDiff, + Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(25 * time.Minute), Delete: schema.DefaultTimeout(25 * time.Minute), @@ -116,7 +119,8 @@ func resourceAwsEksIdentityProviderConfig() *schema.Resource { Computed: true, }, - "tags": tagsSchemaForceNew(), + "tags": tagsSchema(), + "tags_all": tagsSchemaComputed(), }, } } @@ -134,8 +138,10 @@ func allDiagFunc(validators ...schema.SchemaValidateDiagFunc) schema.SchemaValid func resourceAwsEksIdentityProviderConfigCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*AWSClient).eksconn - clusterName := d.Get("cluster_name").(string) + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig + tags := defaultTagsConfig.MergeTags(keyvaluetags.New(d.Get("tags").(map[string]interface{}))) + clusterName := d.Get("cluster_name").(string) configName, oidc := expandEksOidcIdentityProviderConfigRequest(d.Get("oidc").([]interface{})[0].(map[string]interface{})) id := tfeks.IdentityProviderConfigCreateResourceID(clusterName, configName) @@ -145,8 +151,8 @@ func resourceAwsEksIdentityProviderConfigCreate(ctx context.Context, d *schema.R Oidc: oidc, } - if v := d.Get("tags").(map[string]interface{}); len(v) > 0 { - input.Tags = keyvaluetags.New(v).IgnoreAws().EksTags() + if len(tags) > 0 { + input.Tags = tags.IgnoreAws().EksTags() } _, err := conn.AssociateIdentityProviderConfig(input) @@ -168,6 +174,7 @@ func resourceAwsEksIdentityProviderConfigCreate(ctx context.Context, d *schema.R func resourceAwsEksIdentityProviderConfigRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*AWSClient).eksconn + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig clusterName, configName, err := tfeks.IdentityProviderConfigParseResourceID(d.Id()) @@ -197,13 +204,33 @@ func resourceAwsEksIdentityProviderConfigRead(ctx context.Context, d *schema.Res d.Set("status", oidc.Status) - if err := d.Set("tags", keyvaluetags.EksKeyValueTags(oidc.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + tags := keyvaluetags.EksKeyValueTags(oidc.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig) + + //lintignore:AWSR002 + if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { return diag.Errorf("error setting tags: %s", err) } + if err := d.Set("tags_all", tags.Map()); err != nil { + return diag.Errorf("error setting tags_all: %s", err) + } + return nil } +func resourceAwsEksIdentityProviderConfigUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).eksconn + + if d.HasChange("tags_all") { + o, n := d.GetChange("tags_all") + if err := keyvaluetags.EksUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return diag.Errorf("error updating tags: %s", err) + } + } + + return resourceAwsEksIdentityProviderConfigRead(ctx, d, meta) +} + func resourceAwsEksIdentityProviderConfigDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*AWSClient).eksconn diff --git a/website/docs/r/eks_identity_provider_config.html.markdown b/website/docs/r/eks_identity_provider_config.html.markdown index ac6840ce8be..ffe45bc08f5 100644 --- a/website/docs/r/eks_identity_provider_config.html.markdown +++ b/website/docs/r/eks_identity_provider_config.html.markdown @@ -30,7 +30,7 @@ The following arguments are supported: * `cluster_name` – (Required) Name of the EKS Cluster. * `oidc` - (Required) Nested attribute containing [OpenID Connect](https://openid.net/connect/) identity provider information for the cluster. Detailed below. -* `tags` - (Optional) A map of tags to assign to the resource. +* `tags` - (Optional) Key-value map of resource tags. If configured with a provider [`default_tags` configuration block](https://www.terraform.io/docs/providers/aws/index.html#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. ### oidc Configuration Block @@ -50,6 +50,7 @@ In addition to all arguments above, the following attributes are exported: * `arn` - Amazon Resource Name (ARN) of the EKS Identity Provider Configuration. * `id` - EKS Cluster name and EKS Identity Provider Configuration name separated by a colon (`:`). * `status` - Status of the EKS Identity Provider Configuration. +* `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://www.terraform.io/docs/providers/aws/index.html#default_tags-configuration-block). ## Timeouts From 4125dae3b5ad4f757b4cdb61d685eae522835962 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 7 Jul 2021 12:29:14 -0400 Subject: [PATCH 163/398] r/aws_eks_identity_provider_config: Tweak test configurations. --- ...e_aws_eks_identity_provider_config_test.go | 105 +++++++++++------- 1 file changed, 65 insertions(+), 40 deletions(-) diff --git a/aws/resource_aws_eks_identity_provider_config_test.go b/aws/resource_aws_eks_identity_provider_config_test.go index 72bd5d6b3c2..9d8cbfb7a61 100644 --- a/aws/resource_aws_eks_identity_provider_config_test.go +++ b/aws/resource_aws_eks_identity_provider_config_test.go @@ -102,11 +102,11 @@ func TestAccAWSEksIdentityProviderConfig_basic(t *testing.T) { CheckDestroy: testAccCheckAWSEksIdentityProviderConfigDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSEksIdentityProviderConfigProvider_Oidc_IssuerUrl(rName, "http://example.com"), + Config: testAccAWSEksIdentityProviderConfigConfigIssuerUrl(rName, "http://example.com"), ExpectError: regexp.MustCompile(`expected .* to have a url with schema of: "https", got http://example.com`), }, { - Config: testAccAWSEksIdentityProviderConfigProviderConfigName(rName), + Config: testAccAWSEksIdentityProviderConfigConfigName(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSEksIdentityProviderConfigExists(ctx, resourceName, &config), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "eks", regexp.MustCompile(fmt.Sprintf("identityproviderconfig/%[1]s/oidc/%[1]s/.+", rName))), @@ -145,7 +145,7 @@ func TestAccAWSEksIdentityProviderConfig_disappears(t *testing.T) { CheckDestroy: testAccCheckAWSEksIdentityProviderConfigDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSEksIdentityProviderConfigProviderConfigName(rName), + Config: testAccAWSEksIdentityProviderConfigConfigName(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSEksIdentityProviderConfigExists(ctx, resourceName, &config), testAccCheckResourceDisappears(testAccProvider, resourceAwsEksIdentityProviderConfig(), resourceName), @@ -156,7 +156,7 @@ func TestAccAWSEksIdentityProviderConfig_disappears(t *testing.T) { }) } -func TestAccAWSEksIdentityProviderConfig_Oidc_Group(t *testing.T) { +func TestAccAWSEksIdentityProviderConfig_OIDC_Groups(t *testing.T) { var config eks.OidcIdentityProviderConfig rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_eks_identity_provider_config.test" @@ -169,7 +169,7 @@ func TestAccAWSEksIdentityProviderConfig_Oidc_Group(t *testing.T) { CheckDestroy: testAccCheckAWSEksIdentityProviderConfigDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSEksIdentityProviderConfigProvider_Oidc_Groups(rName, "groups", "oidc:"), + Config: testAccAWSEksIdentityProviderConfigConfigGroups(rName, "groups", "oidc:"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSEksIdentityProviderConfigExists(ctx, resourceName, &config), resource.TestCheckResourceAttr(resourceName, "oidc.#", "1"), @@ -186,7 +186,7 @@ func TestAccAWSEksIdentityProviderConfig_Oidc_Group(t *testing.T) { }) } -func TestAccAWSEksIdentityProviderConfig_Oidc_Username(t *testing.T) { +func TestAccAWSEksIdentityProviderConfig_OIDC_Username(t *testing.T) { var config eks.OidcIdentityProviderConfig rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_eks_identity_provider_config.test" @@ -199,7 +199,7 @@ func TestAccAWSEksIdentityProviderConfig_Oidc_Username(t *testing.T) { CheckDestroy: testAccCheckAWSEksIdentityProviderConfigDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSEksIdentityProviderConfigProvider_Oidc_Username(rName, "email", "-"), + Config: testAccAWSEksIdentityProviderConfigConfigUsername(rName, "email", "-"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSEksIdentityProviderConfigExists(ctx, resourceName, &config), resource.TestCheckResourceAttr(resourceName, "oidc.#", "1"), @@ -216,7 +216,7 @@ func TestAccAWSEksIdentityProviderConfig_Oidc_Username(t *testing.T) { }) } -func TestAccAWSEksIdentityProviderConfig_Oidc_RequiredClaims(t *testing.T) { +func TestAccAWSEksIdentityProviderConfig_OIDC_RequiredClaims(t *testing.T) { var config eks.OidcIdentityProviderConfig rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_eks_identity_provider_config.test" @@ -229,15 +229,15 @@ func TestAccAWSEksIdentityProviderConfig_Oidc_RequiredClaims(t *testing.T) { CheckDestroy: testAccCheckAWSEksIdentityProviderConfigDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSEksIdentityProviderConfig_Oidc_RequiredClaims(rName, "4qkvw9k2RbpSO6wgCbynY10T6Rc2n89PQblyi6bZ5VhfpMr6V7FVvrA12FiJxarh", "valueOne", "keyTwo", "valueTwo"), + Config: testAccAWSEksIdentityProviderConfigConfigRequiredClaims(rName, "4qkvw9k2RbpSO6wgCbynY10T6Rc2n89PQblyi6bZ5VhfpMr6V7FVvrA12FiJxarh", "valueOne", "keyTwo", "valueTwo"), ExpectError: regexp.MustCompile("Bad map key length"), }, { - Config: testAccAWSEksIdentityProviderConfig_Oidc_RequiredClaims(rName, "keyOne", "bUUUiuIXeFGw0M2VwiCVjR8oIIavv0PF49Ba6yNwOOC7IcoLawczSeb6MpEIhqtXKcf9aogW4uc4smLGvdTQ8uTTkVFvQTPyWXQ3F0uZP02YyoSw0d9MZ7laGRjpXSph9oFE2UlT5IyRaXIsTwl1qvItvVXLN40Pd3PDyPa6de4nlYcRNy6YIikZz2P1QUSYuvMGSJxGUzhTKYRUniolIt1vjHsXt3MAsaJtCcWz0tjLWalvG27pQ3Gl5Cs7K1", "keyTwo", "valueTwo"), + Config: testAccAWSEksIdentityProviderConfigConfigRequiredClaims(rName, "keyOne", "bUUUiuIXeFGw0M2VwiCVjR8oIIavv0PF49Ba6yNwOOC7IcoLawczSeb6MpEIhqtXKcf9aogW4uc4smLGvdTQ8uTTkVFvQTPyWXQ3F0uZP02YyoSw0d9MZ7laGRjpXSph9oFE2UlT5IyRaXIsTwl1qvItvVXLN40Pd3PDyPa6de4nlYcRNy6YIikZz2P1QUSYuvMGSJxGUzhTKYRUniolIt1vjHsXt3MAsaJtCcWz0tjLWalvG27pQ3Gl5Cs7K1", "keyTwo", "valueTwo"), ExpectError: regexp.MustCompile("Bad map value length"), }, { - Config: testAccAWSEksIdentityProviderConfig_Oidc_RequiredClaims(rName, "keyOne", "valueOne", "keyTwo", "valueTwo"), + Config: testAccAWSEksIdentityProviderConfigConfigRequiredClaims(rName, "keyOne", "valueOne", "keyTwo", "valueTwo"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSEksIdentityProviderConfigExists(ctx, resourceName, &config), resource.TestCheckResourceAttr(resourceName, "oidc.0.required_claims.%", "2"), @@ -267,12 +267,11 @@ func TestAccAWSEksIdentityProviderConfig_Tags(t *testing.T) { CheckDestroy: testAccCheckAWSEksIdentityProviderConfigDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSEksIdentityProviderConfig_Tags(rName, "keyOne", "valueOne", "keyTwo", "valueTwo"), + Config: testAccAWSEksIdentityProviderConfigConfigTags1(rName, "key1", "value1"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSEksIdentityProviderConfigExists(ctx, resourceName, &config), - resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), - resource.TestCheckResourceAttr(resourceName, "tags.keyOne", "valueOne"), - resource.TestCheckResourceAttr(resourceName, "tags.keyTwo", "valueTwo"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), ), }, { @@ -280,6 +279,23 @@ func TestAccAWSEksIdentityProviderConfig_Tags(t *testing.T) { ImportState: true, ImportStateVerify: true, }, + { + Config: testAccAWSEksIdentityProviderConfigConfigTags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEksIdentityProviderConfigExists(ctx, resourceName, &config), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccAWSEksIdentityProviderConfigConfigTags1(rName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEksIdentityProviderConfigExists(ctx, resourceName, &config), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, }, }) } @@ -346,17 +362,8 @@ func testAccCheckAWSEksIdentityProviderConfigDestroy(s *terraform.State) error { return nil } -func testAccAWSEksIdentityProviderConfigBase(rName string) string { - return fmt.Sprintf(` -data "aws_availability_zones" "available" { - state = "available" - - filter { - name = "opt-in-status" - values = ["opt-in-not-required"] - } -} - +func testAccAWSEksIdentityProviderConfigConfigBase(rName string) string { + return composeConfig(testAccAvailableAZsNoOptInConfig(), fmt.Sprintf(` data "aws_partition" "current" {} resource "aws_iam_role" "test" { @@ -413,11 +420,11 @@ resource "aws_eks_cluster" "test" { depends_on = [aws_iam_role_policy_attachment.cluster-AmazonEKSClusterPolicy] } -`, rName) +`, rName)) } -func testAccAWSEksIdentityProviderConfigProviderConfigName(rName string) string { - return composeConfig(testAccAWSEksIdentityProviderConfigBase(rName), fmt.Sprintf(` +func testAccAWSEksIdentityProviderConfigConfigName(rName string) string { + return composeConfig(testAccAWSEksIdentityProviderConfigConfigBase(rName), fmt.Sprintf(` resource "aws_eks_identity_provider_config" "test" { cluster_name = aws_eks_cluster.test.name @@ -430,8 +437,8 @@ resource "aws_eks_identity_provider_config" "test" { `, rName)) } -func testAccAWSEksIdentityProviderConfigProvider_Oidc_IssuerUrl(rName, issuerUrl string) string { - return composeConfig(testAccAWSEksIdentityProviderConfigBase(rName), fmt.Sprintf(` +func testAccAWSEksIdentityProviderConfigConfigIssuerUrl(rName, issuerUrl string) string { + return composeConfig(testAccAWSEksIdentityProviderConfigConfigBase(rName), fmt.Sprintf(` resource "aws_eks_identity_provider_config" "test" { cluster_name = aws_eks_cluster.test.name @@ -444,8 +451,8 @@ resource "aws_eks_identity_provider_config" "test" { `, rName, issuerUrl)) } -func testAccAWSEksIdentityProviderConfigProvider_Oidc_Groups(rName, groupsClaim, groupsPrefix string) string { - return composeConfig(testAccAWSEksIdentityProviderConfigBase(rName), fmt.Sprintf(` +func testAccAWSEksIdentityProviderConfigConfigGroups(rName, groupsClaim, groupsPrefix string) string { + return composeConfig(testAccAWSEksIdentityProviderConfigConfigBase(rName), fmt.Sprintf(` resource "aws_eks_identity_provider_config" "test" { cluster_name = aws_eks_cluster.test.name @@ -460,8 +467,8 @@ resource "aws_eks_identity_provider_config" "test" { `, rName, groupsClaim, groupsPrefix)) } -func testAccAWSEksIdentityProviderConfigProvider_Oidc_Username(rName, usernameClaim, usernamePrefix string) string { - return composeConfig(testAccAWSEksIdentityProviderConfigBase(rName), fmt.Sprintf(` +func testAccAWSEksIdentityProviderConfigConfigUsername(rName, usernameClaim, usernamePrefix string) string { + return composeConfig(testAccAWSEksIdentityProviderConfigConfigBase(rName), fmt.Sprintf(` resource "aws_eks_identity_provider_config" "test" { cluster_name = aws_eks_cluster.test.name @@ -476,8 +483,8 @@ resource "aws_eks_identity_provider_config" "test" { `, rName, usernameClaim, usernamePrefix)) } -func testAccAWSEksIdentityProviderConfig_Oidc_RequiredClaims(rName, claimsKeyOne, claimsValueOne, claimsKeyTwo, claimsValueTwo string) string { - return composeConfig(testAccAWSEksIdentityProviderConfigBase(rName), fmt.Sprintf(` +func testAccAWSEksIdentityProviderConfigConfigRequiredClaims(rName, claimsKeyOne, claimsValueOne, claimsKeyTwo, claimsValueTwo string) string { + return composeConfig(testAccAWSEksIdentityProviderConfigConfigBase(rName), fmt.Sprintf(` resource "aws_eks_identity_provider_config" "test" { cluster_name = aws_eks_cluster.test.name @@ -494,8 +501,26 @@ resource "aws_eks_identity_provider_config" "test" { `, rName, claimsKeyOne, claimsValueOne, claimsKeyTwo, claimsValueTwo)) } -func testAccAWSEksIdentityProviderConfig_Tags(rName, tagsKeyOne, tagsValueOne, tagsKeyTwo, tagsValueTwo string) string { - return composeConfig(testAccAWSEksIdentityProviderConfigBase(rName), fmt.Sprintf(` +func testAccAWSEksIdentityProviderConfigConfigTags1(rName, tagKey1, tagValue1 string) string { + return composeConfig(testAccAWSEksIdentityProviderConfigConfigBase(rName), fmt.Sprintf(` +resource "aws_eks_identity_provider_config" "test" { + cluster_name = aws_eks_cluster.test.name + + oidc { + client_id = "example.net" + identity_provider_config_name = %[1]q + issuer_url = "https://example.com" + } + + tags = { + %[2]q = %[3]q + } +} +`, rName, tagKey1, tagValue1)) +} + +func testAccAWSEksIdentityProviderConfigConfigTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return composeConfig(testAccAWSEksIdentityProviderConfigConfigBase(rName), fmt.Sprintf(` resource "aws_eks_identity_provider_config" "test" { cluster_name = aws_eks_cluster.test.name @@ -510,5 +535,5 @@ resource "aws_eks_identity_provider_config" "test" { %[4]q = %[5]q } } -`, rName, tagsKeyOne, tagsValueOne, tagsKeyTwo, tagsValueTwo)) +`, rName, tagKey1, tagValue1, tagKey2, tagValue2)) } From 518228c504d812694dcf970ab92830700b080927 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 7 Jul 2021 12:38:17 -0400 Subject: [PATCH 164/398] r/aws_eks_identity_provider_config: Consolidate to 'TestAccAWSEksIdentityProviderConfig_AllOidcOptions'. --- ...e_aws_eks_identity_provider_config_test.go | 117 +++--------------- 1 file changed, 15 insertions(+), 102 deletions(-) diff --git a/aws/resource_aws_eks_identity_provider_config_test.go b/aws/resource_aws_eks_identity_provider_config_test.go index 9d8cbfb7a61..db33da83f0f 100644 --- a/aws/resource_aws_eks_identity_provider_config_test.go +++ b/aws/resource_aws_eks_identity_provider_config_test.go @@ -156,7 +156,7 @@ func TestAccAWSEksIdentityProviderConfig_disappears(t *testing.T) { }) } -func TestAccAWSEksIdentityProviderConfig_OIDC_Groups(t *testing.T) { +func TestAccAWSEksIdentityProviderConfig_AllOidcOptions(t *testing.T) { var config eks.OidcIdentityProviderConfig rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_eks_identity_provider_config.test" @@ -169,80 +169,20 @@ func TestAccAWSEksIdentityProviderConfig_OIDC_Groups(t *testing.T) { CheckDestroy: testAccCheckAWSEksIdentityProviderConfigDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSEksIdentityProviderConfigConfigGroups(rName, "groups", "oidc:"), + Config: testAccAWSEksIdentityProviderConfigAllOidcOptions(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSEksIdentityProviderConfigExists(ctx, resourceName, &config), resource.TestCheckResourceAttr(resourceName, "oidc.#", "1"), + resource.TestCheckResourceAttr(resourceName, "oidc.0.client_id", "example.net"), resource.TestCheckResourceAttr(resourceName, "oidc.0.groups_claim", "groups"), resource.TestCheckResourceAttr(resourceName, "oidc.0.groups_prefix", "oidc:"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func TestAccAWSEksIdentityProviderConfig_OIDC_Username(t *testing.T) { - var config eks.OidcIdentityProviderConfig - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_eks_identity_provider_config.test" - ctx := context.TODO() - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t) }, - ErrorCheck: testAccErrorCheck(t, eks.EndpointsID), - ProviderFactories: testAccProviderFactories, - CheckDestroy: testAccCheckAWSEksIdentityProviderConfigDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSEksIdentityProviderConfigConfigUsername(rName, "email", "-"), - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSEksIdentityProviderConfigExists(ctx, resourceName, &config), - resource.TestCheckResourceAttr(resourceName, "oidc.#", "1"), - resource.TestCheckResourceAttr(resourceName, "oidc.0.username_claim", "email"), - resource.TestCheckResourceAttr(resourceName, "oidc.0.username_prefix", "-"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func TestAccAWSEksIdentityProviderConfig_OIDC_RequiredClaims(t *testing.T) { - var config eks.OidcIdentityProviderConfig - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_eks_identity_provider_config.test" - ctx := context.TODO() - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t) }, - ErrorCheck: testAccErrorCheck(t, eks.EndpointsID), - ProviderFactories: testAccProviderFactories, - CheckDestroy: testAccCheckAWSEksIdentityProviderConfigDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSEksIdentityProviderConfigConfigRequiredClaims(rName, "4qkvw9k2RbpSO6wgCbynY10T6Rc2n89PQblyi6bZ5VhfpMr6V7FVvrA12FiJxarh", "valueOne", "keyTwo", "valueTwo"), - ExpectError: regexp.MustCompile("Bad map key length"), - }, - { - Config: testAccAWSEksIdentityProviderConfigConfigRequiredClaims(rName, "keyOne", "bUUUiuIXeFGw0M2VwiCVjR8oIIavv0PF49Ba6yNwOOC7IcoLawczSeb6MpEIhqtXKcf9aogW4uc4smLGvdTQ8uTTkVFvQTPyWXQ3F0uZP02YyoSw0d9MZ7laGRjpXSph9oFE2UlT5IyRaXIsTwl1qvItvVXLN40Pd3PDyPa6de4nlYcRNy6YIikZz2P1QUSYuvMGSJxGUzhTKYRUniolIt1vjHsXt3MAsaJtCcWz0tjLWalvG27pQ3Gl5Cs7K1", "keyTwo", "valueTwo"), - ExpectError: regexp.MustCompile("Bad map value length"), - }, - { - Config: testAccAWSEksIdentityProviderConfigConfigRequiredClaims(rName, "keyOne", "valueOne", "keyTwo", "valueTwo"), - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSEksIdentityProviderConfigExists(ctx, resourceName, &config), + resource.TestCheckResourceAttr(resourceName, "oidc.0.identity_provider_config_name", rName), + resource.TestCheckResourceAttr(resourceName, "oidc.0.issuer_url", "https://example.com"), resource.TestCheckResourceAttr(resourceName, "oidc.0.required_claims.%", "2"), resource.TestCheckResourceAttr(resourceName, "oidc.0.required_claims.keyOne", "valueOne"), resource.TestCheckResourceAttr(resourceName, "oidc.0.required_claims.keyTwo", "valueTwo"), + resource.TestCheckResourceAttr(resourceName, "oidc.0.username_claim", "email"), + resource.TestCheckResourceAttr(resourceName, "oidc.0.username_prefix", "-"), ), }, { @@ -451,54 +391,27 @@ resource "aws_eks_identity_provider_config" "test" { `, rName, issuerUrl)) } -func testAccAWSEksIdentityProviderConfigConfigGroups(rName, groupsClaim, groupsPrefix string) string { +func testAccAWSEksIdentityProviderConfigAllOidcOptions(rName string) string { return composeConfig(testAccAWSEksIdentityProviderConfigConfigBase(rName), fmt.Sprintf(` resource "aws_eks_identity_provider_config" "test" { cluster_name = aws_eks_cluster.test.name oidc { client_id = "example.net" - groups_claim = %[2]q - groups_prefix = %[3]q + groups_claim = "groups" + groups_prefix = "oidc:" identity_provider_config_name = %[1]q issuer_url = "https://example.com" - } -} -`, rName, groupsClaim, groupsPrefix)) -} + username_claim = "email" + username_prefix = "-" -func testAccAWSEksIdentityProviderConfigConfigUsername(rName, usernameClaim, usernamePrefix string) string { - return composeConfig(testAccAWSEksIdentityProviderConfigConfigBase(rName), fmt.Sprintf(` -resource "aws_eks_identity_provider_config" "test" { - cluster_name = aws_eks_cluster.test.name - - oidc { - client_id = "example.net" - identity_provider_config_name = %[1]q - issuer_url = "https://example.com" - username_claim = %[2]q - username_prefix = %[3]q - } -} -`, rName, usernameClaim, usernamePrefix)) -} - -func testAccAWSEksIdentityProviderConfigConfigRequiredClaims(rName, claimsKeyOne, claimsValueOne, claimsKeyTwo, claimsValueTwo string) string { - return composeConfig(testAccAWSEksIdentityProviderConfigConfigBase(rName), fmt.Sprintf(` -resource "aws_eks_identity_provider_config" "test" { - cluster_name = aws_eks_cluster.test.name - - oidc { - client_id = "example.net" - identity_provider_config_name = %[1]q - issuer_url = "https://example.com" required_claims = { - %[2]q = %[3]q - %[4]q = %[5]q + keyOne = "valueOne" + keyTwo = "valueTwo" } } } -`, rName, claimsKeyOne, claimsValueOne, claimsKeyTwo, claimsValueTwo)) +`, rName)) } func testAccAWSEksIdentityProviderConfigConfigTags1(rName, tagKey1, tagValue1 string) string { From 2e094957c187c7961cf0af65ee8c0716495803e4 Mon Sep 17 00:00:00 2001 From: Dave Kujawski Date: Tue, 18 May 2021 20:49:29 +0000 Subject: [PATCH 165/398] don't report diff when proposal is gone --- aws/resource_aws_dx_gateway_association_proposal.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/aws/resource_aws_dx_gateway_association_proposal.go b/aws/resource_aws_dx_gateway_association_proposal.go index 9716ff0c908..3c626c4a6d5 100644 --- a/aws/resource_aws_dx_gateway_association_proposal.go +++ b/aws/resource_aws_dx_gateway_association_proposal.go @@ -32,7 +32,12 @@ func resourceAwsDxGatewayAssociationProposal() *schema.Resource { return false } - return proposal != nil && aws.StringValue(proposal.ProposalState) == directconnect.GatewayAssociationProposalStateRequested + if proposal == nil { + // Don't report as a diff when the proposal is gone. + return true + } + + return aws.StringValue(proposal.ProposalState) == directconnect.GatewayAssociationProposalStateRequested }), ), From 8c198a13354167c255af4f53d7c986dacdbcf8fb Mon Sep 17 00:00:00 2001 From: Dave Kujawski Date: Tue, 18 May 2021 23:28:28 +0000 Subject: [PATCH 166/398] Populate proposal state using found association Once the proposal has been accepted and deleted, locate the association, populate the proposal state to prevent recreating the proposal unnecessarily. --- ...rce_aws_dx_gateway_association_proposal.go | 94 +++++++++++++++---- 1 file changed, 74 insertions(+), 20 deletions(-) diff --git a/aws/resource_aws_dx_gateway_association_proposal.go b/aws/resource_aws_dx_gateway_association_proposal.go index 3c626c4a6d5..ae52d5c2a50 100644 --- a/aws/resource_aws_dx_gateway_association_proposal.go +++ b/aws/resource_aws_dx_gateway_association_proposal.go @@ -8,6 +8,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/directconnect" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -109,31 +110,52 @@ func resourceAwsDxGatewayAssociationProposalRead(d *schema.ResourceData, meta in } if proposal == nil { - log.Printf("[WARN] Direct Connect Gateway Association Proposal (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } + associatedGatewayId, ok := d.GetOk("associated_gateway_id") - if aws.StringValue(proposal.ProposalState) == directconnect.GatewayAssociationProposalStateDeleted { - log.Printf("[WARN] Direct Connect Gateway Association Proposal (%s) deleted, removing from state", d.Id()) - d.SetId("") - return nil - } + if !ok || associatedGatewayId == nil { + return fmt.Errorf("error reading Direct Connect Associated Gateway Id (%s): %s", d.Id(), err) + } - if proposal.AssociatedGateway == nil { - return fmt.Errorf("error reading Direct Connect Gateway Association Proposal (%s): missing associated gateway information", d.Id()) - } + assocRaw, state, err := getDxGatewayAssociation(conn, associatedGatewayId.(string))() - if err := d.Set("allowed_prefixes", flattenDirectConnectGatewayAssociationProposalAllowedPrefixes(proposal.RequestedAllowedPrefixesToDirectConnectGateway)); err != nil { - return fmt.Errorf("error setting allowed_prefixes: %s", err) - } + if err != nil { + return fmt.Errorf("error reading Direct Connect gateway association (%s): %s", d.Id(), err) + } + + if state == gatewayAssociationStateDeleted { + log.Printf("[WARN] Direct Connect gateway association (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + log.Printf("[INFO] Direct Connect Gateway Association Proposal (%s) has been accepted", d.Id()) + assoc := assocRaw.(*directconnect.GatewayAssociation) + d.Set("associated_gateway_id", assoc.AssociatedGateway.Id) + d.Set("dx_gateway_id", assoc.DirectConnectGatewayId) + d.Set("dx_gateway_owner_account_id", assoc.DirectConnectGatewayOwnerAccount) + } else { + + if aws.StringValue(proposal.ProposalState) == directconnect.GatewayAssociationProposalStateDeleted { + log.Printf("[WARN] Direct Connect Gateway Association Proposal (%s) deleted, removing from state", d.Id()) + d.SetId("") + return nil + } + + if proposal.AssociatedGateway == nil { + return fmt.Errorf("error reading Direct Connect Gateway Association Proposal (%s): missing associated gateway information", d.Id()) + } - d.Set("associated_gateway_id", proposal.AssociatedGateway.Id) - d.Set("associated_gateway_owner_account_id", proposal.AssociatedGateway.OwnerAccount) - d.Set("associated_gateway_type", proposal.AssociatedGateway.Type) - d.Set("dx_gateway_id", proposal.DirectConnectGatewayId) - d.Set("dx_gateway_owner_account_id", proposal.DirectConnectGatewayOwnerAccount) + if err := d.Set("allowed_prefixes", flattenDirectConnectGatewayAssociationProposalAllowedPrefixes(proposal.RequestedAllowedPrefixesToDirectConnectGateway)); err != nil { + return fmt.Errorf("error setting allowed_prefixes: %s", err) + } + + d.Set("associated_gateway_id", proposal.AssociatedGateway.Id) + d.Set("associated_gateway_owner_account_id", proposal.AssociatedGateway.OwnerAccount) + d.Set("associated_gateway_type", proposal.AssociatedGateway.Type) + d.Set("dx_gateway_id", proposal.DirectConnectGatewayId) + d.Set("dx_gateway_owner_account_id", proposal.DirectConnectGatewayOwnerAccount) + } return nil } @@ -228,3 +250,35 @@ func flattenDirectConnectGatewayAssociationProposalAllowedPrefixes(routeFilterPr return allowedPrefixes } + +func getDxGatewayAssociation(conn *directconnect.DirectConnect, associatedGatewayId string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + resp, err := conn.DescribeDirectConnectGatewayAssociations(&directconnect.DescribeDirectConnectGatewayAssociationsInput{ + AssociatedGatewayId: aws.String(associatedGatewayId), + }) + if err != nil { + return nil, "", err + } + + n := len(resp.DirectConnectGatewayAssociations) + switch n { + case 0: + return "", gatewayAssociationStateDeleted, nil + + case 1: + assoc := resp.DirectConnectGatewayAssociations[0] + + if stateChangeError := aws.StringValue(assoc.StateChangeError); stateChangeError != "" { + id := dxGatewayAssociationId( + aws.StringValue(resp.DirectConnectGatewayAssociations[0].DirectConnectGatewayId), + aws.StringValue(resp.DirectConnectGatewayAssociations[0].AssociatedGateway.Id)) + log.Printf("[INFO] Direct Connect gateway association (%s) state change error: %s", id, stateChangeError) + } + + return assoc, aws.StringValue(assoc.AssociationState), nil + + default: + return nil, "", fmt.Errorf("Found %d Direct Connect gateway associations for %s, expected 1", n, associatedGatewayId) + } + } +} From 7f5ae58483e5c737905d66d619d618058792449b Mon Sep 17 00:00:00 2001 From: Dave Kujawski Date: Mon, 24 May 2021 15:43:38 +0000 Subject: [PATCH 167/398] WIP activate debug code path for testing --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index 3f721524f18..b33c29c4b92 100644 --- a/main.go +++ b/main.go @@ -12,7 +12,7 @@ import ( func main() { var debugMode bool - flag.BoolVar(&debugMode, "debug", false, "set to true to run the provider with support for debuggers like delve") + flag.BoolVar(&debugMode, "debug", true, "set to true to run the provider with support for debuggers like delve") flag.Parse() opts := &plugin.ServeOpts{ProviderFunc: aws.Provider} From 19b40437567ca43132bb66a91bc3473e1a88dfd9 Mon Sep 17 00:00:00 2001 From: Dave Kujawski Date: Mon, 24 May 2021 15:45:16 +0000 Subject: [PATCH 168/398] collect state information from associated gateway to determine if proposal needs to be recreated. --- ...ource_aws_dx_gateway_association_proposal.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/aws/resource_aws_dx_gateway_association_proposal.go b/aws/resource_aws_dx_gateway_association_proposal.go index ae52d5c2a50..7764f816f9c 100644 --- a/aws/resource_aws_dx_gateway_association_proposal.go +++ b/aws/resource_aws_dx_gateway_association_proposal.go @@ -34,7 +34,22 @@ func resourceAwsDxGatewayAssociationProposal() *schema.Resource { } if proposal == nil { - // Don't report as a diff when the proposal is gone. + // Don't report as a diff when the proposal is gone unless the association is gone too. + associatedGatewayId, ok := d.GetOk("associated_gateway_id") + + if !ok || associatedGatewayId == nil { + return false + } + + _, state, err := getDxGatewayAssociation(conn, associatedGatewayId.(string))() + + if err != nil { + return false + } + + if state == gatewayAssociationStateDeleted { + return false + } return true } From 363a76288d2e03a38e4d0aa7bab4d1f5ee34f619 Mon Sep 17 00:00:00 2001 From: Dave Kujawski Date: Mon, 24 May 2021 18:36:35 +0000 Subject: [PATCH 169/398] when associated gateway is not found or errors, assume we need the proposal --- aws/resource_aws_dx_gateway_association_proposal.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aws/resource_aws_dx_gateway_association_proposal.go b/aws/resource_aws_dx_gateway_association_proposal.go index 7764f816f9c..01596db393d 100644 --- a/aws/resource_aws_dx_gateway_association_proposal.go +++ b/aws/resource_aws_dx_gateway_association_proposal.go @@ -128,12 +128,14 @@ func resourceAwsDxGatewayAssociationProposalRead(d *schema.ResourceData, meta in associatedGatewayId, ok := d.GetOk("associated_gateway_id") if !ok || associatedGatewayId == nil { + d.SetId("") return fmt.Errorf("error reading Direct Connect Associated Gateway Id (%s): %s", d.Id(), err) } assocRaw, state, err := getDxGatewayAssociation(conn, associatedGatewayId.(string))() if err != nil { + d.SetId("") return fmt.Errorf("error reading Direct Connect gateway association (%s): %s", d.Id(), err) } From 76c016d912c830fc8a6f0cda62f3d2e82260c261 Mon Sep 17 00:00:00 2001 From: Dave Kujawski Date: Wed, 9 Jun 2021 20:38:49 +0000 Subject: [PATCH 170/398] provide some commentary for clarity --- aws/resource_aws_dx_gateway_association_proposal.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aws/resource_aws_dx_gateway_association_proposal.go b/aws/resource_aws_dx_gateway_association_proposal.go index 01596db393d..3ccbf319252 100644 --- a/aws/resource_aws_dx_gateway_association_proposal.go +++ b/aws/resource_aws_dx_gateway_association_proposal.go @@ -145,6 +145,9 @@ func resourceAwsDxGatewayAssociationProposalRead(d *schema.ResourceData, meta in return nil } + // once accepted, AWS will delete the proposal after after some time (days?) + // in this case we don't need to create a new proposal, use metadata from the association + // to artificially populate the missing proposal in state as if it was still there. log.Printf("[INFO] Direct Connect Gateway Association Proposal (%s) has been accepted", d.Id()) assoc := assocRaw.(*directconnect.GatewayAssociation) d.Set("associated_gateway_id", assoc.AssociatedGateway.Id) From 1f410034d67b57c6254b10cf4c45a497eb91232f Mon Sep 17 00:00:00 2001 From: Dave Kujawski Date: Wed, 9 Jun 2021 20:44:21 +0000 Subject: [PATCH 171/398] add test to cover proposal end of life --- ...ws_dx_gateway_association_proposal_test.go | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/aws/resource_aws_dx_gateway_association_proposal_test.go b/aws/resource_aws_dx_gateway_association_proposal_test.go index bf834a4897a..c53cb4276e4 100644 --- a/aws/resource_aws_dx_gateway_association_proposal_test.go +++ b/aws/resource_aws_dx_gateway_association_proposal_test.go @@ -185,6 +185,34 @@ func TestAccAwsDxGatewayAssociationProposal_disappears(t *testing.T) { }) } +func TestAccAwsDxGatewayAssociationProposal_endOfLife(t *testing.T) { + var proposal1 directconnect.GatewayAssociationProposal + var providers []*schema.Provider + rBgpAsn := acctest.RandIntRange(64512, 65534) + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_dx_gateway_association_proposal.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccAlternateAccountPreCheck(t) + }, + ErrorCheck: testAccErrorCheck(t, directconnect.EndpointsID), + ProviderFactories: testAccProviderFactoriesAlternate(&providers), + CheckDestroy: testAccCheckAwsDxGatewayAssociationProposalDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDxGatewayAssociationProposalConfig_endOfLife(rName, rBgpAsn), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsDxGatewayAssociationProposalExists(resourceName, &proposal1), + testAccCheckAwsDxGatewayAssociationProposalAccepted(resourceName), + testAccCheckAwsDxGatewayAssociationProposalDisappears(&proposal1), + ), + }, + }, + }) +} + func TestAccAwsDxGatewayAssociationProposal_AllowedPrefixes(t *testing.T) { var proposal1, proposal2 directconnect.GatewayAssociationProposal var providers []*schema.Provider @@ -336,6 +364,20 @@ resource "aws_dx_gateway_association_proposal" "test" { ` } +func testAccDxGatewayAssociationProposalConfig_endOfLife(rName string, rBgpAsn int) string { + return testAccDxGatewayAssociationProposalConfig_basicVpnGateway(rName, rBgpAsn) + ` +data "aws_caller_identity" "current" {} + +resource "aws_dx_gateway_association" "test" { +provider = "awsalternate" + +proposal_id = aws_dx_gateway_association_proposal.test.id +dx_gateway_id = aws_dx_gateway.test.id +associated_gateway_owner_account_id = data.aws_caller_identity.current.account_id +} +` +} + func testAccDxGatewayAssociationProposalConfig_basicTransitGateway(rName string, rBgpAsn int) string { return testAccAlternateAccountProviderConfig() + fmt.Sprintf(` resource "aws_dx_gateway" "test" { @@ -385,3 +427,29 @@ resource "aws_dx_gateway_association_proposal" "test" { } ` } + +func testAccCheckAwsDxGatewayAssociationProposalAccepted(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + conn := testAccProvider.Meta().(*AWSClient).dxconn + + proposal, err := describeDirectConnectGatewayAssociationProposal(conn, rs.Primary.ID) + + if err != nil { + return err + } + + if proposal == nil { + return fmt.Errorf("Direct Connect Gateway Association Proposal (%s) not found", rs.Primary.ID) + } + + if aws.StringValue(proposal.ProposalState) != "accepted" { + return fmt.Errorf("Direct Connect Gateway Association Proposal (%s) not accepted", rs.Primary.ID) + } + return nil + } +} From 69feb2ffbe1c3ed80e3753ab00e4086560b8bc3d Mon Sep 17 00:00:00 2001 From: Dave Kujawski Date: Wed, 9 Jun 2021 21:55:31 +0000 Subject: [PATCH 172/398] reset debug mode default --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index b33c29c4b92..3f721524f18 100644 --- a/main.go +++ b/main.go @@ -12,7 +12,7 @@ import ( func main() { var debugMode bool - flag.BoolVar(&debugMode, "debug", true, "set to true to run the provider with support for debuggers like delve") + flag.BoolVar(&debugMode, "debug", false, "set to true to run the provider with support for debuggers like delve") flag.Parse() opts := &plugin.ServeOpts{ProviderFunc: aws.Provider} From 69767a47896686ed1f4c4ed3a729009d0cc11035 Mon Sep 17 00:00:00 2001 From: Dave Kujawski Date: Mon, 21 Jun 2021 15:12:03 -0700 Subject: [PATCH 173/398] allow import dx gateway assoc poroposal after EOL accept an import string composed of the proposal id, dx gateway id, and target associated gateway id when the proposal has been removed by AWS due to it being accepted. --- ...rce_aws_dx_gateway_association_proposal.go | 64 +++++++++++++++- ...ws_dx_gateway_association_proposal_test.go | 75 ++++++++++++------- 2 files changed, 112 insertions(+), 27 deletions(-) diff --git a/aws/resource_aws_dx_gateway_association_proposal.go b/aws/resource_aws_dx_gateway_association_proposal.go index 3ccbf319252..18bd1a6184f 100644 --- a/aws/resource_aws_dx_gateway_association_proposal.go +++ b/aws/resource_aws_dx_gateway_association_proposal.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log" + "strings" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/directconnect" @@ -18,7 +19,7 @@ func resourceAwsDxGatewayAssociationProposal() *schema.Resource { Read: resourceAwsDxGatewayAssociationProposalRead, Delete: resourceAwsDxGatewayAssociationProposalDelete, Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, + State: dxGatewayAssociationProposalImport, }, CustomizeDiff: customdiff.Sequence( @@ -271,6 +272,67 @@ func flattenDirectConnectGatewayAssociationProposalAllowedPrefixes(routeFilterPr return allowedPrefixes } +func dxGatewayAssociationProposalImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + // example: c2ede9b4-bbc6-4d33-923c-bc4feEXAMPLE/186c8187-36f4-472e-9268-107aaEXAMPLE/0d95359280EXAMPLE + + errStr := "unexpected format of import string (%q), expected PROPOSALID/DXGATEWAYID/TARGETGATEWAYID]*: %s" + importStr := d.Id() + log.Printf("[DEBUG] Validating import string %s", importStr) + + parts := strings.Split(strings.ToLower(importStr), "/") + if len(parts) < 1 { + return nil, fmt.Errorf(errStr, importStr, "too few parts") + } + var propId, dxgwId, gwId string + propId = parts[0] + + conn := meta.(*AWSClient).dxconn + if propId != "" { + p, err := describeDirectConnectGatewayAssociationProposal(conn, propId) + if err != nil { + return nil, err + } + if p != nil { + // proposal still exists normal import + return schema.ImportStatePassthrough(d, meta) + } + // proposal may not exist, but that's fine + } + d.SetId(propId) + + if len(parts) == 1 { + // requesting just the prop id + return schema.ImportStatePassthrough(d, meta) + } else if len(parts) < 3 { + return nil, fmt.Errorf(errStr, importStr, "too few parts") + } + + dxgwId = parts[1] + gwId = parts[2] + + if gwId != "" && dxgwId != "" { + input := directconnect.DescribeDirectConnectGatewayAssociationsInput{ + AssociatedGatewayId: aws.String(gwId), + DirectConnectGatewayId: aws.String(dxgwId), + } + resp, err := conn.DescribeDirectConnectGatewayAssociations(&input) + if err != nil { + return nil, err + } + + id := dxGatewayAssociationId(dxgwId, gwId) + if n := len(resp.DirectConnectGatewayAssociations); n != 1 { + return nil, fmt.Errorf("Found %d Direct Connect gateway associations for %s, expected 1", n, id) + } + d.Set("associated_gateway_id", gwId) + d.Set("dx_gateway_id", dxgwId) + } else { + return nil, fmt.Errorf(errStr, importStr, "missing parts") + } + + return []*schema.ResourceData{d}, nil +} + func getDxGatewayAssociation(conn *directconnect.DirectConnect, associatedGatewayId string) resource.StateRefreshFunc { return func() (interface{}, string, error) { resp, err := conn.DescribeDirectConnectGatewayAssociations(&directconnect.DescribeDirectConnectGatewayAssociationsInput{ diff --git a/aws/resource_aws_dx_gateway_association_proposal_test.go b/aws/resource_aws_dx_gateway_association_proposal_test.go index c53cb4276e4..cc66d2e9e46 100644 --- a/aws/resource_aws_dx_gateway_association_proposal_test.go +++ b/aws/resource_aws_dx_gateway_association_proposal_test.go @@ -3,6 +3,7 @@ package aws import ( "fmt" "log" + "strings" "testing" "github.com/aws/aws-sdk-go/aws" @@ -209,6 +210,18 @@ func TestAccAwsDxGatewayAssociationProposal_endOfLife(t *testing.T) { testAccCheckAwsDxGatewayAssociationProposalDisappears(&proposal1), ), }, + { + ResourceName: resourceName, + ImportStateIdFunc: func(s *terraform.State) (string, error) { + return strings.Join([]string{ + aws.StringValue(proposal1.ProposalId), + aws.StringValue(proposal1.DirectConnectGatewayId), + aws.StringValue(proposal1.AssociatedGateway.Id), + }, "/"), nil + }, + ImportState: true, + ImportStateVerify: false, // proposal attributes not applicable when it does not exist + }, }, }) } @@ -327,6 +340,42 @@ func testAccCheckAwsDxGatewayAssociationProposalRecreated(i, j *directconnect.Ga } } +func testAccCheckAwsDxGatewayAssociationProposalAccepted(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + conn := testAccProvider.Meta().(*AWSClient).dxconn + + proposal, err := describeDirectConnectGatewayAssociationProposal(conn, rs.Primary.ID) + + if err != nil { + return err + } + + if proposal == nil { + return fmt.Errorf("Direct Connect Gateway Association Proposal (%s) not found", rs.Primary.ID) + } + + if aws.StringValue(proposal.ProposalState) != "accepted" { + return fmt.Errorf("Direct Connect Gateway Association Proposal (%s) not accepted", rs.Primary.ID) + } + return nil + } +} + +func testAccDxGatewayAssociationProposalImportStateIdFunc(proposal *directconnect.GatewayAssociationProposal) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + return strings.Join([]string{ + aws.StringValue(proposal.ProposalId), + aws.StringValue(proposal.DirectConnectGatewayId), + aws.StringValue(proposal.AssociatedGateway.Id), + }, "/"), nil + } +} + func testAccDxGatewayAssociationProposalConfigBase_vpnGateway(rName string, rBgpAsn int) string { return testAccAlternateAccountProviderConfig() + fmt.Sprintf(` resource "aws_dx_gateway" "test" { @@ -427,29 +476,3 @@ resource "aws_dx_gateway_association_proposal" "test" { } ` } - -func testAccCheckAwsDxGatewayAssociationProposalAccepted(resourceName string) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] - if !ok { - return fmt.Errorf("Not found: %s", resourceName) - } - - conn := testAccProvider.Meta().(*AWSClient).dxconn - - proposal, err := describeDirectConnectGatewayAssociationProposal(conn, rs.Primary.ID) - - if err != nil { - return err - } - - if proposal == nil { - return fmt.Errorf("Direct Connect Gateway Association Proposal (%s) not found", rs.Primary.ID) - } - - if aws.StringValue(proposal.ProposalState) != "accepted" { - return fmt.Errorf("Direct Connect Gateway Association Proposal (%s) not accepted", rs.Primary.ID) - } - return nil - } -} From 925c9dff1670a049b001a2b2fc322e4c555be0db Mon Sep 17 00:00:00 2001 From: Dave Kujawski Date: Tue, 22 Jun 2021 15:29:14 -0700 Subject: [PATCH 174/398] defer existance checks to Read function move proposal existance logic out of custom diff function and over into the Read function. add more debug logs to track when proposal id values exist --- ...rce_aws_dx_gateway_association_proposal.go | 78 ++++++++++++------- 1 file changed, 49 insertions(+), 29 deletions(-) diff --git a/aws/resource_aws_dx_gateway_association_proposal.go b/aws/resource_aws_dx_gateway_association_proposal.go index 18bd1a6184f..588c5db09cc 100644 --- a/aws/resource_aws_dx_gateway_association_proposal.go +++ b/aws/resource_aws_dx_gateway_association_proposal.go @@ -26,6 +26,15 @@ func resourceAwsDxGatewayAssociationProposal() *schema.Resource { // Accepting the proposal with overridden prefixes changes the returned RequestedAllowedPrefixesToDirectConnectGateway value (allowed_prefixes attribute). // We only want to force a new resource if this value changes and the current proposal state is "requested". customdiff.ForceNewIf("allowed_prefixes", func(_ context.Context, d *schema.ResourceDiff, meta interface{}) bool { + + log.Printf("[DEBUG] Checking diff for Direct Connect Gateway Association Proposal (%s) allowed_prefixes", d.Id()) + + if len(strings.Join(strings.Fields(d.Id()), "")) < 1 { + log.Printf("[WARN] Direct Connect Gateway Association Proposal Id not available (%s)", d.Id()) + // assume proposal is end-of-life, rely on Read func to test + return false + } + conn := meta.(*AWSClient).dxconn proposal, err := describeDirectConnectGatewayAssociationProposal(conn, d.Id()) @@ -35,23 +44,8 @@ func resourceAwsDxGatewayAssociationProposal() *schema.Resource { } if proposal == nil { - // Don't report as a diff when the proposal is gone unless the association is gone too. - associatedGatewayId, ok := d.GetOk("associated_gateway_id") - - if !ok || associatedGatewayId == nil { - return false - } - - _, state, err := getDxGatewayAssociation(conn, associatedGatewayId.(string))() - - if err != nil { - return false - } - - if state == gatewayAssociationStateDeleted { - return false - } - return true + // proposal maybe end-of-life and removed by AWS, existence checked in Read func + return false } return aws.StringValue(proposal.ProposalState) == directconnect.GatewayAssociationProposalStateRequested @@ -117,27 +111,50 @@ func resourceAwsDxGatewayAssociationProposalCreate(d *schema.ResourceData, meta } func resourceAwsDxGatewayAssociationProposalRead(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Read Direct Connect Gateway Association Proposal: %s", d.Id()) + + var proposal *directconnect.GatewayAssociationProposal + conn := meta.(*AWSClient).dxconn - proposal, err := describeDirectConnectGatewayAssociationProposal(conn, d.Id()) + trimmedId := strings.Join(strings.Fields(d.Id()), "") + if len(trimmedId) > 0 { + var err error + proposal, err = describeDirectConnectGatewayAssociationProposal(conn, d.Id()) - if err != nil { - return fmt.Errorf("error reading Direct Connect Gateway Association Proposal (%s): %s", d.Id(), err) + if err != nil { + return fmt.Errorf("error reading Direct Connect Gateway Association Proposal (%s): %s", d.Id(), err) + } + } else { + log.Printf("[WARN] Direct Connect Gateway Association Proposal Id not available (%s)", d.Id()) + d.SetId("0xda5e") // placeholder value } - if proposal == nil { - associatedGatewayId, ok := d.GetOk("associated_gateway_id") + if proposal == nil || len(trimmedId) < 1 { + log.Printf("[WARN] Direct Connect Gateway Association Proposal (%s) not found, checking for associated gateway", d.Id()) - if !ok || associatedGatewayId == nil { + var dxGatewayId string + if rawDGId, ok := d.GetOk("dx_gateway_id"); ok { + dxGatewayId = rawDGId.(string) + } else if rawDGId == nil { d.SetId("") - return fmt.Errorf("error reading Direct Connect Associated Gateway Id (%s): %s", d.Id(), err) + return fmt.Errorf("error reading dx_gateway_id (%s) from Proposal state", d.Id()) } - assocRaw, state, err := getDxGatewayAssociation(conn, associatedGatewayId.(string))() + var associatedGatewayId string + if rawAGId, ok := d.GetOk("associated_gateway_id"); ok { + associatedGatewayId = rawAGId.(string) + } else if rawAGId == nil { + d.SetId("") + return fmt.Errorf("error reading associated_gateway_id (%s) from Proposal state", d.Id()) + } + + log.Printf("[DEBUG] looking for Direct Connect Gateway Association using dx_gateway_id (%s) and associated_gateway_id (%s) to validate Proposal state data", dxGatewayId, associatedGatewayId) + assocRaw, state, err := getDxGatewayAssociation(conn, dxGatewayId, associatedGatewayId)() if err != nil { d.SetId("") - return fmt.Errorf("error reading Direct Connect gateway association (%s): %s", d.Id(), err) + return fmt.Errorf("error reading Direct Connect gateway association (%s) from Proposal state: %s", d.Id(), err) } if state == gatewayAssociationStateDeleted { @@ -149,7 +166,7 @@ func resourceAwsDxGatewayAssociationProposalRead(d *schema.ResourceData, meta in // once accepted, AWS will delete the proposal after after some time (days?) // in this case we don't need to create a new proposal, use metadata from the association // to artificially populate the missing proposal in state as if it was still there. - log.Printf("[INFO] Direct Connect Gateway Association Proposal (%s) has been accepted", d.Id()) + log.Printf("[INFO] Direct Connect Gateway Association Proposal (%s) has reached end-of-life and has been removed by AWS.", d.Id()) assoc := assocRaw.(*directconnect.GatewayAssociation) d.Set("associated_gateway_id", assoc.AssociatedGateway.Id) d.Set("dx_gateway_id", assoc.DirectConnectGatewayId) @@ -333,11 +350,14 @@ func dxGatewayAssociationProposalImport(d *schema.ResourceData, meta interface{} return []*schema.ResourceData{d}, nil } -func getDxGatewayAssociation(conn *directconnect.DirectConnect, associatedGatewayId string) resource.StateRefreshFunc { +func getDxGatewayAssociation(conn *directconnect.DirectConnect, dxGatewayId, associatedGatewayId string) resource.StateRefreshFunc { return func() (interface{}, string, error) { + resp, err := conn.DescribeDirectConnectGatewayAssociations(&directconnect.DescribeDirectConnectGatewayAssociationsInput{ - AssociatedGatewayId: aws.String(associatedGatewayId), + AssociatedGatewayId: &associatedGatewayId, + DirectConnectGatewayId: &dxGatewayId, }) + if err != nil { return nil, "", err } From 2710bca6c2c97790632aa87b8b3c6d989314b2dd Mon Sep 17 00:00:00 2001 From: Dave Kujawski Date: Tue, 22 Jun 2021 15:31:35 -0700 Subject: [PATCH 175/398] remove test function no longer needed --- ...esource_aws_dx_gateway_association_proposal_test.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/aws/resource_aws_dx_gateway_association_proposal_test.go b/aws/resource_aws_dx_gateway_association_proposal_test.go index cc66d2e9e46..ef7e36705e3 100644 --- a/aws/resource_aws_dx_gateway_association_proposal_test.go +++ b/aws/resource_aws_dx_gateway_association_proposal_test.go @@ -366,16 +366,6 @@ func testAccCheckAwsDxGatewayAssociationProposalAccepted(resourceName string) re } } -func testAccDxGatewayAssociationProposalImportStateIdFunc(proposal *directconnect.GatewayAssociationProposal) resource.ImportStateIdFunc { - return func(s *terraform.State) (string, error) { - return strings.Join([]string{ - aws.StringValue(proposal.ProposalId), - aws.StringValue(proposal.DirectConnectGatewayId), - aws.StringValue(proposal.AssociatedGateway.Id), - }, "/"), nil - } -} - func testAccDxGatewayAssociationProposalConfigBase_vpnGateway(rName string, rBgpAsn int) string { return testAccAlternateAccountProviderConfig() + fmt.Sprintf(` resource "aws_dx_gateway" "test" { From f7501bea86460ea0200d8760d4336ea8ab1bbbf8 Mon Sep 17 00:00:00 2001 From: Dave Kujawski Date: Wed, 23 Jun 2021 11:04:01 -0700 Subject: [PATCH 176/398] include allowed_prefixes when atrificially populating proposal --- aws/resource_aws_dx_gateway_association_proposal.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/aws/resource_aws_dx_gateway_association_proposal.go b/aws/resource_aws_dx_gateway_association_proposal.go index 588c5db09cc..249ad9ff0bb 100644 --- a/aws/resource_aws_dx_gateway_association_proposal.go +++ b/aws/resource_aws_dx_gateway_association_proposal.go @@ -31,6 +31,7 @@ func resourceAwsDxGatewayAssociationProposal() *schema.Resource { if len(strings.Join(strings.Fields(d.Id()), "")) < 1 { log.Printf("[WARN] Direct Connect Gateway Association Proposal Id not available (%s)", d.Id()) + log.Printf("[DEBUG] Direct Connect Gateway Association Proposal UpdatedKeys (%s)", strings.Join(d.UpdatedKeys(), "/")) // assume proposal is end-of-life, rely on Read func to test return false } @@ -127,7 +128,6 @@ func resourceAwsDxGatewayAssociationProposalRead(d *schema.ResourceData, meta in } } else { log.Printf("[WARN] Direct Connect Gateway Association Proposal Id not available (%s)", d.Id()) - d.SetId("0xda5e") // placeholder value } if proposal == nil || len(trimmedId) < 1 { @@ -158,7 +158,7 @@ func resourceAwsDxGatewayAssociationProposalRead(d *schema.ResourceData, meta in } if state == gatewayAssociationStateDeleted { - log.Printf("[WARN] Direct Connect gateway association (%s) not found, removing from state", d.Id()) + log.Printf("[WARN] Direct Connect gateway association (%s/%s/%s) not found, removing from state", d.Id(), dxGatewayId, associatedGatewayId) d.SetId("") return nil } @@ -168,10 +168,17 @@ func resourceAwsDxGatewayAssociationProposalRead(d *schema.ResourceData, meta in // to artificially populate the missing proposal in state as if it was still there. log.Printf("[INFO] Direct Connect Gateway Association Proposal (%s) has reached end-of-life and has been removed by AWS.", d.Id()) assoc := assocRaw.(*directconnect.GatewayAssociation) + + err = d.Set("allowed_prefixes", flattenDxRouteFilterPrefixes(assoc.AllowedPrefixesToDirectConnectGateway)) + if err != nil { + return fmt.Errorf("error setting allowed_prefixes: %s", err) + } + d.Set("associated_gateway_id", assoc.AssociatedGateway.Id) d.Set("dx_gateway_id", assoc.DirectConnectGatewayId) d.Set("dx_gateway_owner_account_id", assoc.DirectConnectGatewayOwnerAccount) } else { + log.Printf("[DEBUG] Direct Connect Gateway Association Proposal (%s) found, continuing as normal: %s", d.Id(), proposal.String()) if aws.StringValue(proposal.ProposalState) == directconnect.GatewayAssociationProposalStateDeleted { log.Printf("[WARN] Direct Connect Gateway Association Proposal (%s) deleted, removing from state", d.Id()) @@ -294,7 +301,7 @@ func dxGatewayAssociationProposalImport(d *schema.ResourceData, meta interface{} errStr := "unexpected format of import string (%q), expected PROPOSALID/DXGATEWAYID/TARGETGATEWAYID]*: %s" importStr := d.Id() - log.Printf("[DEBUG] Validating import string %s", importStr) + log.Printf("[DEBUG] Validating import string (%s) for Direct Connect Gateway Association Proposal", importStr) parts := strings.Split(strings.ToLower(importStr), "/") if len(parts) < 1 { From e8b8e03a18686dcfaac58fdafcd0053ce157f872 Mon Sep 17 00:00:00 2001 From: Dave Kujawski Date: Wed, 23 Jun 2021 11:04:19 -0700 Subject: [PATCH 177/398] include tgw for acc tests --- ...ws_dx_gateway_association_proposal_test.go | 60 ++++++++++++++++++- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/aws/resource_aws_dx_gateway_association_proposal_test.go b/aws/resource_aws_dx_gateway_association_proposal_test.go index ef7e36705e3..3ef3932720c 100644 --- a/aws/resource_aws_dx_gateway_association_proposal_test.go +++ b/aws/resource_aws_dx_gateway_association_proposal_test.go @@ -186,7 +186,7 @@ func TestAccAwsDxGatewayAssociationProposal_disappears(t *testing.T) { }) } -func TestAccAwsDxGatewayAssociationProposal_endOfLife(t *testing.T) { +func TestAccAwsDxGatewayAssociationProposal_endOfLifeVpn(t *testing.T) { var proposal1 directconnect.GatewayAssociationProposal var providers []*schema.Provider rBgpAsn := acctest.RandIntRange(64512, 65534) @@ -203,7 +203,47 @@ func TestAccAwsDxGatewayAssociationProposal_endOfLife(t *testing.T) { CheckDestroy: testAccCheckAwsDxGatewayAssociationProposalDestroy, Steps: []resource.TestStep{ { - Config: testAccDxGatewayAssociationProposalConfig_endOfLife(rName, rBgpAsn), + Config: testAccDxGatewayAssociationProposalConfig_endOfLifeVpn(rName, rBgpAsn), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsDxGatewayAssociationProposalExists(resourceName, &proposal1), + testAccCheckAwsDxGatewayAssociationProposalAccepted(resourceName), + testAccCheckAwsDxGatewayAssociationProposalDisappears(&proposal1), + ), + }, + { + ResourceName: resourceName, + ImportStateIdFunc: func(s *terraform.State) (string, error) { + return strings.Join([]string{ + aws.StringValue(proposal1.ProposalId), + aws.StringValue(proposal1.DirectConnectGatewayId), + aws.StringValue(proposal1.AssociatedGateway.Id), + }, "/"), nil + }, + ImportState: true, + ImportStateVerify: false, // proposal attributes not applicable when it does not exist + }, + }, + }) +} + +func TestAccAwsDxGatewayAssociationProposal_endOfLifeTgw(t *testing.T) { + var proposal1 directconnect.GatewayAssociationProposal + var providers []*schema.Provider + rBgpAsn := acctest.RandIntRange(64512, 65534) + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_dx_gateway_association_proposal.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccAlternateAccountPreCheck(t) + }, + ErrorCheck: testAccErrorCheck(t, directconnect.EndpointsID), + ProviderFactories: testAccProviderFactoriesAlternate(&providers), + CheckDestroy: testAccCheckAwsDxGatewayAssociationProposalDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDxGatewayAssociationProposalConfig_endOfLifeTgw(rName, rBgpAsn), Check: resource.ComposeTestCheckFunc( testAccCheckAwsDxGatewayAssociationProposalExists(resourceName, &proposal1), testAccCheckAwsDxGatewayAssociationProposalAccepted(resourceName), @@ -403,7 +443,7 @@ resource "aws_dx_gateway_association_proposal" "test" { ` } -func testAccDxGatewayAssociationProposalConfig_endOfLife(rName string, rBgpAsn int) string { +func testAccDxGatewayAssociationProposalConfig_endOfLifeVpn(rName string, rBgpAsn int) string { return testAccDxGatewayAssociationProposalConfig_basicVpnGateway(rName, rBgpAsn) + ` data "aws_caller_identity" "current" {} @@ -417,6 +457,20 @@ associated_gateway_owner_account_id = data.aws_caller_identity.current.account_i ` } +func testAccDxGatewayAssociationProposalConfig_endOfLifeTgw(rName string, rBgpAsn int) string { + return testAccDxGatewayAssociationProposalConfig_basicTransitGateway(rName, rBgpAsn) + ` +data "aws_caller_identity" "current" {} + +resource "aws_dx_gateway_association" "test" { +provider = "awsalternate" + +proposal_id = aws_dx_gateway_association_proposal.test.id +dx_gateway_id = aws_dx_gateway.test.id +associated_gateway_owner_account_id = data.aws_caller_identity.current.account_id +} +` +} + func testAccDxGatewayAssociationProposalConfig_basicTransitGateway(rName string, rBgpAsn int) string { return testAccAlternateAccountProviderConfig() + fmt.Sprintf(` resource "aws_dx_gateway" "test" { From eab40d6986bfb68b3a94a74ce0d42672262b4e07 Mon Sep 17 00:00:00 2001 From: changelogbot Date: Wed, 7 Jul 2021 17:54:08 +0000 Subject: [PATCH 178/398] Update CHANGELOG.md for #19986 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index daf1257a6f6..4eade413bc5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ FEATURES: +* **New Resource:** `aws_eks_identity_provider_config` ([#17959](https://github.com/hashicorp/terraform-provider-aws/issues/17959)) * **New Resource:** `aws_rds_cluster_role_association` ([#12370](https://github.com/hashicorp/terraform-provider-aws/issues/12370)) ENHANCEMENTS: @@ -9,6 +10,10 @@ ENHANCEMENTS: * aws_rds_cluster: Set `iam_roles` as Computed to prevent drift when the `aws_rds_cluster_role_association` resource is used ([#12370](https://github.com/hashicorp/terraform-provider-aws/issues/12370)) * resource/aws_transfer_server: Add `security_group_ids` argument to `endpoint_details` configuration block. ([#17539](https://github.com/hashicorp/terraform-provider-aws/issues/17539)) +BUG FIXES: + +* resource/aws_eks_cluster: Don't associate an `encryption_config` if there's already one ([#19986](https://github.com/hashicorp/terraform-provider-aws/issues/19986)) + ## 3.48.0 (July 02, 2021) FEATURES: From 10830b084b3822ff84d301bfe5ac3097c260b84c Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 7 Jul 2021 14:11:26 -0400 Subject: [PATCH 179/398] Add DirectConnect internal finder and waiter packages. --- .../service/directconnect/finder/finder.go | 44 ++++++++++++ .../service/directconnect/waiter/status.go | 25 +++++++ .../service/directconnect/waiter/waiter.go | 68 +++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 aws/internal/service/directconnect/finder/finder.go create mode 100644 aws/internal/service/directconnect/waiter/status.go create mode 100644 aws/internal/service/directconnect/waiter/waiter.go diff --git a/aws/internal/service/directconnect/finder/finder.go b/aws/internal/service/directconnect/finder/finder.go new file mode 100644 index 00000000000..559ce5ef3ac --- /dev/null +++ b/aws/internal/service/directconnect/finder/finder.go @@ -0,0 +1,44 @@ +package finder + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func GatewayAssociationByID(conn *directconnect.DirectConnect, id string) (*directconnect.GatewayAssociation, error) { + input := &directconnect.DescribeDirectConnectGatewayAssociationsInput{ + AssociationId: aws.String(id), + } + + output, err := conn.DescribeDirectConnectGatewayAssociations(input) + + if err != nil { + return nil, err + } + + if output == nil || len(output.DirectConnectGatewayAssociations) == 0 || output.DirectConnectGatewayAssociations[0] == nil { + return nil, &resource.NotFoundError{ + Message: "Empty result", + LastRequest: input, + } + } + + gatewayAssociation := output.DirectConnectGatewayAssociations[0] + + if state := aws.StringValue(gatewayAssociation.AssociationState); state == directconnect.GatewayAssociationStateDisassociated { + return nil, &resource.NotFoundError{ + Message: state, + LastRequest: input, + } + } + + // Eventual consistency check. + if aws.StringValue(gatewayAssociation.AssociationId) != id { + return nil, &resource.NotFoundError{ + LastRequest: input, + } + } + + return gatewayAssociation, nil +} diff --git a/aws/internal/service/directconnect/waiter/status.go b/aws/internal/service/directconnect/waiter/status.go new file mode 100644 index 00000000000..98e90d4097b --- /dev/null +++ b/aws/internal/service/directconnect/waiter/status.go @@ -0,0 +1,25 @@ +package waiter + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/directconnect/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" +) + +func GatewayAssociationState(conn *directconnect.DirectConnect, id string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := finder.GatewayAssociationByID(conn, id) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, aws.StringValue(output.AssociationState), nil + } +} diff --git a/aws/internal/service/directconnect/waiter/waiter.go b/aws/internal/service/directconnect/waiter/waiter.go new file mode 100644 index 00000000000..ab54c49cb62 --- /dev/null +++ b/aws/internal/service/directconnect/waiter/waiter.go @@ -0,0 +1,68 @@ +package waiter + +import ( + "errors" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" +) + +func GatewayAssociationCreated(conn *directconnect.DirectConnect, id string, timeout time.Duration) (*directconnect.GatewayAssociation, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{directconnect.GatewayAssociationStateAssociating}, + Target: []string{directconnect.GatewayAssociationStateAssociated}, + Refresh: GatewayAssociationState(conn, id), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*directconnect.GatewayAssociation); ok { + tfresource.SetLastError(err, errors.New(aws.StringValue(output.StateChangeError))) + + return output, err + } + + return nil, err +} + +func GatewayAssociationUpdated(conn *directconnect.DirectConnect, id string, timeout time.Duration) (*directconnect.GatewayAssociation, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{directconnect.GatewayAssociationStateUpdating}, + Target: []string{directconnect.GatewayAssociationStateAssociated}, + Refresh: GatewayAssociationState(conn, id), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*directconnect.GatewayAssociation); ok { + tfresource.SetLastError(err, errors.New(aws.StringValue(output.StateChangeError))) + + return output, err + } + + return nil, err +} + +func GatewayAssociationDeleted(conn *directconnect.DirectConnect, id string, timeout time.Duration) (*directconnect.GatewayAssociation, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{directconnect.GatewayAssociationStateDisassociating}, + Target: []string{}, + Refresh: GatewayAssociationState(conn, id), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*directconnect.GatewayAssociation); ok { + tfresource.SetLastError(err, errors.New(aws.StringValue(output.StateChangeError))) + + return output, err + } + + return nil, err +} From c28188296fde0f2e8cf0c5985822296573899985 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 7 Jul 2021 14:49:07 -0400 Subject: [PATCH 180/398] Use finder package in aws_dx_gateway_association & aws_dx_gateway_association_proposal acceptance tests. --- .../service/directconnect/finder/finder.go | 48 +++++++++++++++++-- ...ws_dx_gateway_association_proposal_test.go | 26 +++++----- ...esource_aws_dx_gateway_association_test.go | 37 +++++++------- 3 files changed, 79 insertions(+), 32 deletions(-) diff --git a/aws/internal/service/directconnect/finder/finder.go b/aws/internal/service/directconnect/finder/finder.go index 559ce5ef3ac..f8af3c5a4bd 100644 --- a/aws/internal/service/directconnect/finder/finder.go +++ b/aws/internal/service/directconnect/finder/finder.go @@ -11,6 +11,19 @@ func GatewayAssociationByID(conn *directconnect.DirectConnect, id string) (*dire AssociationId: aws.String(id), } + return GatewayAssociation(conn, input) +} + +func GatewayAssociationByAssociatedGatewayIDAndDirectConnectGatewayID(conn *directconnect.DirectConnect, associatedGatewayID, directConnectGatewayID string) (*directconnect.GatewayAssociation, error) { + input := &directconnect.DescribeDirectConnectGatewayAssociationsInput{ + AssociatedGatewayId: aws.String(associatedGatewayID), + DirectConnectGatewayId: aws.String(directConnectGatewayID), + } + + return GatewayAssociation(conn, input) +} + +func GatewayAssociation(conn *directconnect.DirectConnect, input *directconnect.DescribeDirectConnectGatewayAssociationsInput) (*directconnect.GatewayAssociation, error) { output, err := conn.DescribeDirectConnectGatewayAssociations(input) if err != nil { @@ -24,6 +37,9 @@ func GatewayAssociationByID(conn *directconnect.DirectConnect, id string) (*dire } } + // TODO Check for multiple results. + // TODO https://github.com/hashicorp/terraform-provider-aws/pull/17613. + gatewayAssociation := output.DirectConnectGatewayAssociations[0] if state := aws.StringValue(gatewayAssociation.AssociationState); state == directconnect.GatewayAssociationStateDisassociated { @@ -33,12 +49,38 @@ func GatewayAssociationByID(conn *directconnect.DirectConnect, id string) (*dire } } - // Eventual consistency check. - if aws.StringValue(gatewayAssociation.AssociationId) != id { + return gatewayAssociation, nil +} + +func GatewayAssociationProposalByID(conn *directconnect.DirectConnect, id string) (*directconnect.GatewayAssociationProposal, error) { + input := &directconnect.DescribeDirectConnectGatewayAssociationProposalsInput{ + ProposalId: aws.String(id), + } + + output, err := conn.DescribeDirectConnectGatewayAssociationProposals(input) + + if err != nil { + return nil, err + } + + if output == nil || len(output.DirectConnectGatewayAssociationProposals) == 0 || output.DirectConnectGatewayAssociationProposals[0] == nil { + return nil, &resource.NotFoundError{ + Message: "Empty result", + LastRequest: input, + } + } + + // TODO Check for multiple results. + // TODO https://github.com/hashicorp/terraform-provider-aws/pull/17613. + + proposal := output.DirectConnectGatewayAssociationProposals[0] + + if state := aws.StringValue(proposal.ProposalState); state == directconnect.GatewayAssociationProposalStateDeleted { return nil, &resource.NotFoundError{ + Message: state, LastRequest: input, } } - return gatewayAssociation, nil + return proposal, nil } diff --git a/aws/resource_aws_dx_gateway_association_proposal_test.go b/aws/resource_aws_dx_gateway_association_proposal_test.go index 3ef3932720c..2368ef63528 100644 --- a/aws/resource_aws_dx_gateway_association_proposal_test.go +++ b/aws/resource_aws_dx_gateway_association_proposal_test.go @@ -12,6 +12,8 @@ import ( "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/terraform" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/directconnect/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func init() { @@ -315,17 +317,17 @@ func testAccCheckAwsDxGatewayAssociationProposalDestroy(s *terraform.State) erro continue } - proposal, err := describeDirectConnectGatewayAssociationProposal(conn, rs.Primary.ID) + _, err := finder.GatewayAssociationProposalByID(conn, rs.Primary.ID) - if err != nil { - return err + if tfresource.NotFound(err) { + continue } - if proposal == nil { - continue + if err != nil { + return err } - return fmt.Errorf("Direct Connect Gateway Association Proposal (%s) still exists", rs.Primary.ID) + return fmt.Errorf("Direct Connect Gateway Association Proposal %s still exists", rs.Primary.ID) } return nil @@ -338,19 +340,19 @@ func testAccCheckAwsDxGatewayAssociationProposalExists(resourceName string, gate return fmt.Errorf("Not found: %s", resourceName) } + if rs.Primary.ID == "" { + return fmt.Errorf("No Direct Connect Gateway Association Proposal ID is set") + } + conn := testAccProvider.Meta().(*AWSClient).dxconn - proposal, err := describeDirectConnectGatewayAssociationProposal(conn, rs.Primary.ID) + output, err := finder.GatewayAssociationProposalByID(conn, rs.Primary.ID) if err != nil { return err } - if proposal == nil { - return fmt.Errorf("Direct Connect Gateway Association Proposal (%s) not found", rs.Primary.ID) - } - - *gatewayAssociationProposal = *proposal + *gatewayAssociationProposal = *output return nil } diff --git a/aws/resource_aws_dx_gateway_association_test.go b/aws/resource_aws_dx_gateway_association_test.go index 0a4355f702a..22be438e410 100644 --- a/aws/resource_aws_dx_gateway_association_test.go +++ b/aws/resource_aws_dx_gateway_association_test.go @@ -14,6 +14,8 @@ import ( "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/terraform" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/directconnect/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func init() { @@ -543,18 +545,17 @@ func testAccCheckAwsDxGatewayAssociationDestroy(s *terraform.State) error { continue } - resp, err := conn.DescribeDirectConnectGatewayAssociations(&directconnect.DescribeDirectConnectGatewayAssociationsInput{ - AssociationId: aws.String(rs.Primary.Attributes["dx_gateway_association_id"]), - }) + _, err := finder.GatewayAssociationByID(conn, rs.Primary.Attributes["dx_gateway_association_id"]) + + if tfresource.NotFound(err) { + continue + } + if err != nil { return err } - if len(resp.DirectConnectGatewayAssociations) > 0 { - return fmt.Errorf("Direct Connect Gateway (%s) is not dissociated from GW %s", - aws.StringValue(resp.DirectConnectGatewayAssociations[0].DirectConnectGatewayId), - aws.StringValue(resp.DirectConnectGatewayAssociations[0].AssociatedGateway.Id)) - } + return fmt.Errorf("Direct Connect Gateway Association %s still exists", rs.Primary.ID) } return nil } @@ -565,29 +566,31 @@ func testAccCheckAwsDxGatewayAssociationExists(name string, ga *directconnect.Ga if !ok { return fmt.Errorf("Not found: %s", name) } - if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") + + if rs.Primary.Attributes["dx_gateway_association_id"] == "" { + return fmt.Errorf("No Direct Connect Gateway Association ID is set") } conn := testAccProvider.Meta().(*AWSClient).dxconn - resp, err := conn.DescribeDirectConnectGatewayAssociations(&directconnect.DescribeDirectConnectGatewayAssociationsInput{ - AssociationId: aws.String(rs.Primary.Attributes["dx_gateway_association_id"]), - }) + + output, err := finder.GatewayAssociationByID(conn, rs.Primary.Attributes["dx_gateway_association_id"]) + if err != nil { return err } - *ga = *resp.DirectConnectGatewayAssociations[0] + if proposalID := rs.Primary.Attributes["proposal_id"]; proposalID != "" { + output, err := finder.GatewayAssociationProposalByID(conn, proposalID) - if proposalId := rs.Primary.Attributes["proposal_id"]; proposalId != "" && gap != nil { - v, err := describeDirectConnectGatewayAssociationProposal(conn, proposalId) if err != nil { return err } - *gap = *v + *gap = *output } + *ga = *output + return nil } } From 9a5b53ce89fc6fd9a107ba1d1a65646a08fe1f13 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 7 Jul 2021 15:31:11 -0400 Subject: [PATCH 181/398] Add DirectConnect internal lister package and use in (some) test sweepers. --- aws/internal/service/directconnect/id.go | 9 ++ .../service/directconnect/lister/list.go | 3 + .../directconnect/lister/list_pages_gen.go | 73 +++++++++ ...ws_dx_gateway_association_proposal_test.go | 67 ++++---- ...esource_aws_dx_gateway_association_test.go | 144 ++++++++---------- 5 files changed, 181 insertions(+), 115 deletions(-) create mode 100644 aws/internal/service/directconnect/id.go create mode 100644 aws/internal/service/directconnect/lister/list.go create mode 100644 aws/internal/service/directconnect/lister/list_pages_gen.go diff --git a/aws/internal/service/directconnect/id.go b/aws/internal/service/directconnect/id.go new file mode 100644 index 00000000000..d1cfd0dd061 --- /dev/null +++ b/aws/internal/service/directconnect/id.go @@ -0,0 +1,9 @@ +package directconnect + +import ( + "fmt" +) + +func GatewayAssociationCreateResourceID(directConnectGatewayID, associatedGatewayID string) string { + return fmt.Sprintf("ga-%s%s", directConnectGatewayID, associatedGatewayID) +} diff --git a/aws/internal/service/directconnect/lister/list.go b/aws/internal/service/directconnect/lister/list.go new file mode 100644 index 00000000000..5fa5e04b341 --- /dev/null +++ b/aws/internal/service/directconnect/lister/list.go @@ -0,0 +1,3 @@ +//go:generate go run ../../../generators/listpages/main.go -function=DescribeDirectConnectGateways,DescribeDirectConnectGatewayAssociations,DescribeDirectConnectGatewayAssociationProposals -paginator=NextToken github.com/aws/aws-sdk-go/service/directconnect + +package lister diff --git a/aws/internal/service/directconnect/lister/list_pages_gen.go b/aws/internal/service/directconnect/lister/list_pages_gen.go new file mode 100644 index 00000000000..e28a109e6c2 --- /dev/null +++ b/aws/internal/service/directconnect/lister/list_pages_gen.go @@ -0,0 +1,73 @@ +// Code generated by "aws/internal/generators/listpages/main.go -function=DescribeDirectConnectGateways,DescribeDirectConnectGatewayAssociations,DescribeDirectConnectGatewayAssociationProposals -paginator=NextToken github.com/aws/aws-sdk-go/service/directconnect"; DO NOT EDIT. + +package lister + +import ( + "context" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/directconnect" +) + +func DescribeDirectConnectGatewayAssociationProposalsPages(conn *directconnect.DirectConnect, input *directconnect.DescribeDirectConnectGatewayAssociationProposalsInput, fn func(*directconnect.DescribeDirectConnectGatewayAssociationProposalsOutput, bool) bool) error { + return DescribeDirectConnectGatewayAssociationProposalsPagesWithContext(context.Background(), conn, input, fn) +} + +func DescribeDirectConnectGatewayAssociationProposalsPagesWithContext(ctx context.Context, conn *directconnect.DirectConnect, input *directconnect.DescribeDirectConnectGatewayAssociationProposalsInput, fn func(*directconnect.DescribeDirectConnectGatewayAssociationProposalsOutput, bool) bool) error { + for { + output, err := conn.DescribeDirectConnectGatewayAssociationProposalsWithContext(ctx, input) + if err != nil { + return err + } + + lastPage := aws.StringValue(output.NextToken) == "" + if !fn(output, lastPage) || lastPage { + break + } + + input.NextToken = output.NextToken + } + return nil +} + +func DescribeDirectConnectGatewayAssociationsPages(conn *directconnect.DirectConnect, input *directconnect.DescribeDirectConnectGatewayAssociationsInput, fn func(*directconnect.DescribeDirectConnectGatewayAssociationsOutput, bool) bool) error { + return DescribeDirectConnectGatewayAssociationsPagesWithContext(context.Background(), conn, input, fn) +} + +func DescribeDirectConnectGatewayAssociationsPagesWithContext(ctx context.Context, conn *directconnect.DirectConnect, input *directconnect.DescribeDirectConnectGatewayAssociationsInput, fn func(*directconnect.DescribeDirectConnectGatewayAssociationsOutput, bool) bool) error { + for { + output, err := conn.DescribeDirectConnectGatewayAssociationsWithContext(ctx, input) + if err != nil { + return err + } + + lastPage := aws.StringValue(output.NextToken) == "" + if !fn(output, lastPage) || lastPage { + break + } + + input.NextToken = output.NextToken + } + return nil +} + +func DescribeDirectConnectGatewaysPages(conn *directconnect.DirectConnect, input *directconnect.DescribeDirectConnectGatewaysInput, fn func(*directconnect.DescribeDirectConnectGatewaysOutput, bool) bool) error { + return DescribeDirectConnectGatewaysPagesWithContext(context.Background(), conn, input, fn) +} + +func DescribeDirectConnectGatewaysPagesWithContext(ctx context.Context, conn *directconnect.DirectConnect, input *directconnect.DescribeDirectConnectGatewaysInput, fn func(*directconnect.DescribeDirectConnectGatewaysOutput, bool) bool) error { + for { + output, err := conn.DescribeDirectConnectGatewaysWithContext(ctx, input) + if err != nil { + return err + } + + lastPage := aws.StringValue(output.NextToken) == "" + if !fn(output, lastPage) || lastPage { + break + } + + input.NextToken = output.NextToken + } + return nil +} diff --git a/aws/resource_aws_dx_gateway_association_proposal_test.go b/aws/resource_aws_dx_gateway_association_proposal_test.go index 2368ef63528..2f30084a2c0 100644 --- a/aws/resource_aws_dx_gateway_association_proposal_test.go +++ b/aws/resource_aws_dx_gateway_association_proposal_test.go @@ -8,11 +8,13 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/directconnect" + multierror "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "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/terraform" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/directconnect/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/directconnect/lister" "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) @@ -25,59 +27,58 @@ func init() { func testSweepDirectConnectGatewayAssociationProposals(region string) error { client, err := sharedClientForRegion(region) - if err != nil { - return fmt.Errorf("error getting client: %s", err) + return fmt.Errorf("error getting client: %w", err) } - conn := client.(*AWSClient).dxconn input := &directconnect.DescribeDirectConnectGatewayAssociationProposalsInput{} + var sweeperErrs *multierror.Error + sweepResources := make([]*testSweepResource, 0) - for { - output, err := conn.DescribeDirectConnectGatewayAssociationProposals(input) - - if testSweepSkipSweepError(err) { - log.Printf("[WARN] Skipping Direct Connect Gateway sweep for %s: %s", region, err) - return nil - } - - if err != nil { - return fmt.Errorf("error retrieving Direct Connect Gateway Association Proposals: %s", err) + lister.DescribeDirectConnectGatewayAssociationProposalsPages(conn, input, func(page *directconnect.DescribeDirectConnectGatewayAssociationProposalsOutput, lastPage bool) bool { + if page == nil { + return !lastPage } - for _, gatewayAssociationProposal := range output.DirectConnectGatewayAssociationProposals { - proposalID := aws.StringValue(gatewayAssociationProposal.ProposalId) + for _, proposal := range page.DirectConnectGatewayAssociationProposals { + proposalID := aws.StringValue(proposal.ProposalId) - if aws.StringValue(gatewayAssociationProposal.AssociatedGateway.Region) != region { - log.Printf("[INFO] Skipping Direct Connect Gateway Association Proposal (%s) in different home region: %s", proposalID, aws.StringValue(gatewayAssociationProposal.AssociatedGateway.Region)) + if proposalRegion := aws.StringValue(proposal.AssociatedGateway.Region); proposalRegion != region { + log.Printf("[INFO] Skipping Direct Connect Gateway Association Proposal (%s) in different home region: %s", proposalID, proposalRegion) continue } - if aws.StringValue(gatewayAssociationProposal.ProposalState) != directconnect.GatewayAssociationProposalStateAccepted { - log.Printf("[INFO] Skipping Direct Connect Gateway Association Proposal (%s) in non-accepted (%s) state", proposalID, aws.StringValue(gatewayAssociationProposal.ProposalState)) + if state := aws.StringValue(proposal.ProposalState); state != directconnect.GatewayAssociationProposalStateAccepted { + log.Printf("[INFO] Skipping Direct Connect Gateway Association Proposal (%s) in non-accepted (%s) state", proposalID, state) continue } - input := &directconnect.DeleteDirectConnectGatewayAssociationProposalInput{ - ProposalId: gatewayAssociationProposal.ProposalId, - } - - log.Printf("[INFO] Deleting Direct Connect Gateway Association Proposal: %s", proposalID) - _, err := conn.DeleteDirectConnectGatewayAssociationProposal(input) + r := resourceAwsDxGatewayAssociationProposal() + d := r.Data(nil) + d.SetId(proposalID) - if err != nil { - return fmt.Errorf("error deleting Direct Connect Gateway Association Proposal (%s): %s", proposalID, err) - } + sweepResources = append(sweepResources, NewTestSweepResource(r, d, client)) } - if aws.StringValue(output.NextToken) == "" { - break - } + return !lastPage + }) - input.NextToken = output.NextToken + if testSweepSkipSweepError(err) { + log.Print(fmt.Errorf("[WARN] Skipping Direct Connect Gateway Association Proposal sweep for %s: %w", region, err)) + return sweeperErrs // In case we have completed some pages, but had errors } - return nil + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing Direct Connect Gateway Association Proposals (%s): %w", region, err)) + } + + err = testSweepResourceOrchestrator(sweepResources) + + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping Direct Connect Gateway Association Proposals (%s): %w", region, err)) + } + + return sweeperErrs.ErrorOrNil() } func TestAccAwsDxGatewayAssociationProposal_basicVpnGateway(t *testing.T) { diff --git a/aws/resource_aws_dx_gateway_association_test.go b/aws/resource_aws_dx_gateway_association_test.go index 22be438e410..d85ed68d5ba 100644 --- a/aws/resource_aws_dx_gateway_association_test.go +++ b/aws/resource_aws_dx_gateway_association_test.go @@ -5,16 +5,18 @@ import ( "fmt" "log" "testing" - "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/directconnect" "github.com/aws/aws-sdk-go/service/ec2" + multierror "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "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/terraform" + tfdirectconnect "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/directconnect" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/directconnect/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/directconnect/lister" "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) @@ -31,38 +33,31 @@ func init() { func testSweepDirectConnectGatewayAssociations(region string) error { client, err := sharedClientForRegion(region) if err != nil { - return fmt.Errorf("error getting client: %s", err) + return fmt.Errorf("error getting client: %w", err) } conn := client.(*AWSClient).dxconn - gatewayInput := &directconnect.DescribeDirectConnectGatewaysInput{} + input := &directconnect.DescribeDirectConnectGatewaysInput{} + var sweeperErrs *multierror.Error + sweepResources := make([]*testSweepResource, 0) - for { - gatewayOutput, err := conn.DescribeDirectConnectGateways(gatewayInput) - - if testSweepSkipSweepError(err) { - log.Printf("[WARN] Skipping Direct Connect Gateway sweep for %s: %s", region, err) - return nil - } - - if err != nil { - return fmt.Errorf("error retrieving Direct Connect Gateways: %s", err) + lister.DescribeDirectConnectGatewaysPages(conn, input, func(page *directconnect.DescribeDirectConnectGatewaysOutput, lastPage bool) bool { + if page == nil { + return !lastPage } - for _, gateway := range gatewayOutput.DirectConnectGateways { + for _, gateway := range page.DirectConnectGateways { directConnectGatewayID := aws.StringValue(gateway.DirectConnectGatewayId) - associationInput := &directconnect.DescribeDirectConnectGatewayAssociationsInput{ - DirectConnectGatewayId: gateway.DirectConnectGatewayId, + input := &directconnect.DescribeDirectConnectGatewayAssociationsInput{ + DirectConnectGatewayId: aws.String(directConnectGatewayID), } - for { - associationOutput, err := conn.DescribeDirectConnectGatewayAssociations(associationInput) - - if err != nil { - return fmt.Errorf("error retrieving Direct Connect Gateway (%s) Associations: %s", directConnectGatewayID, err) + err := lister.DescribeDirectConnectGatewayAssociationsPages(conn, input, func(page *directconnect.DescribeDirectConnectGatewayAssociationsOutput, lastPage bool) bool { + if page == nil { + return !lastPage } - for _, association := range associationOutput.DirectConnectGatewayAssociations { + for _, association := range page.DirectConnectGatewayAssociations { gatewayID := aws.StringValue(association.AssociatedGateway.Id) if aws.StringValue(association.AssociatedGateway.Region) != region { @@ -70,44 +65,36 @@ func testSweepDirectConnectGatewayAssociations(region string) error { continue } - if aws.StringValue(association.AssociationState) != directconnect.GatewayAssociationStateAssociated { - log.Printf("[INFO] Skipping Direct Connect Gateway (%s) Association in non-available (%s) state: %s", directConnectGatewayID, aws.StringValue(association.AssociationState), gatewayID) + if state := aws.StringValue(association.AssociationState); state != directconnect.GatewayAssociationStateAssociated { + log.Printf("[INFO] Skipping Direct Connect Gateway (%s) Association in non-available (%s) state: %s", directConnectGatewayID, state, gatewayID) continue } - input := &directconnect.DeleteDirectConnectGatewayAssociationInput{ - AssociationId: association.AssociationId, - } - - log.Printf("[INFO] Deleting Direct Connect Gateway (%s) Association: %s", directConnectGatewayID, gatewayID) - _, err := conn.DeleteDirectConnectGatewayAssociation(input) + r := resourceAwsDxGatewayAssociation() + d := r.Data(nil) + d.SetId(tfdirectconnect.GatewayAssociationCreateResourceID(directConnectGatewayID, gatewayID)) - if isAWSErr(err, directconnect.ErrCodeClientException, "No association exists") { - continue - } - - if err != nil { - return fmt.Errorf("error deleting Direct Connect Gateway (%s) Association (%s): %s", directConnectGatewayID, gatewayID, err) - } - - if err := waitForDirectConnectGatewayAssociationDeletion(conn, aws.StringValue(association.AssociationId), 20*time.Minute); err != nil { - return fmt.Errorf("error waiting for Direct Connect Gateway (%s) Association (%s) to be deleted: %s", directConnectGatewayID, gatewayID, err) - } + sweepResources = append(sweepResources, NewTestSweepResource(r, d, client)) } - if aws.StringValue(associationOutput.NextToken) == "" { - break - } + return !lastPage + }) - associationInput.NextToken = associationOutput.NextToken + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing Direct Connect Gateway Associations (%s): %w", region, err)) } } - if aws.StringValue(gatewayOutput.NextToken) == "" { - break - } + return !lastPage + }) - gatewayInput.NextToken = gatewayOutput.NextToken + if testSweepSkipSweepError(err) { + log.Print(fmt.Errorf("[WARN] Skipping Direct Connect Gateway Association sweep for %s: %w", region, err)) + return sweeperErrs // In case we have completed some pages, but had errors + } + + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing Direct Connect Gateways (%s): %w", region, err)) } // Handle cross-account EC2 Transit Gateway associations. @@ -127,61 +114,54 @@ func testSweepDirectConnectGatewayAssociations(region string) error { continue } - associationInput := &directconnect.DescribeDirectConnectGatewayAssociationsInput{ - AssociatedGatewayId: transitGateway.TransitGatewayId, - } transitGatewayID := aws.StringValue(transitGateway.TransitGatewayId) - associationOutput, err := conn.DescribeDirectConnectGatewayAssociations(associationInput) - - if err != nil { - log.Printf("[ERROR] error retrieving EC2 Transit Gateway (%s) Direct Connect Gateway Associations: %s", transitGatewayID, err) - continue + input := &directconnect.DescribeDirectConnectGatewayAssociationsInput{ + AssociatedGatewayId: aws.String(transitGatewayID), } - for _, association := range associationOutput.DirectConnectGatewayAssociations { - associationID := aws.StringValue(association.AssociationId) - - if aws.StringValue(association.AssociationState) != directconnect.GatewayAssociationStateAssociated { - log.Printf("[INFO] Skipping EC2 Transit Gateway (%s) Direct Connect Gateway Association (%s) in non-available state: %s", transitGatewayID, associationID, aws.StringValue(association.AssociationState)) - continue + err := lister.DescribeDirectConnectGatewayAssociationsPages(conn, input, func(page *directconnect.DescribeDirectConnectGatewayAssociationsOutput, lastPage bool) bool { + if page == nil { + return !lastPage } - input := &directconnect.DeleteDirectConnectGatewayAssociationInput{ - AssociationId: association.AssociationId, - } + for _, association := range page.DirectConnectGatewayAssociations { + directConnectGatewayID := aws.StringValue(association.DirectConnectGatewayId) + + if state := aws.StringValue(association.AssociationState); state != directconnect.GatewayAssociationStateAssociated { + log.Printf("[INFO] Skipping Direct Connect Gateway (%s) Association in non-available (%s) state: %s", directConnectGatewayID, state, transitGatewayID) + continue + } - log.Printf("[INFO] Deleting EC2 Transit Gateway (%s) Direct Connect Gateway Association: %s", transitGatewayID, associationID) - _, err := conn.DeleteDirectConnectGatewayAssociation(input) + r := resourceAwsDxGatewayAssociation() + d := r.Data(nil) + d.SetId(tfdirectconnect.GatewayAssociationCreateResourceID(directConnectGatewayID, transitGatewayID)) - if isAWSErr(err, directconnect.ErrCodeClientException, "No association exists") { - continue + sweepResources = append(sweepResources, NewTestSweepResource(r, d, client)) } - if err != nil { - log.Printf("[ERROR] error deleting EC2 Transit Gateway (%s) Direct Connect Gateway Association (%s): %s", transitGatewayID, associationID, err) - continue - } + return !lastPage + }) - if err := waitForDirectConnectGatewayAssociationDeletion(conn, associationID, 30*time.Minute); err != nil { - log.Printf("[ERROR] error waiting for EC2 Transit Gateway (%s) Direct Connect Gateway Association (%s) to be deleted: %s", transitGatewayID, associationID, err) - } + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing Direct Connect Gateway Associations (%s): %w", region, err)) } } return !lastPage }) - if testSweepSkipSweepError(err) { - log.Printf("[WARN] Skipping EC2 Transit Gateway Direct Connect Gateway Association sweep for %s: %s", region, err) - return nil + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing EC2 Transit Gateways (%s): %w", region, err)) } + err = testSweepResourceOrchestrator(sweepResources) + if err != nil { - return fmt.Errorf("error retrieving EC2 Transit Gateways: %s", err) + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping Direct Connect Gateway Associations (%s): %w", region, err)) } - return nil + return sweeperErrs.ErrorOrNil() } // V0 state upgrade testing must be done via acceptance testing due to API call From 5d7606d0149d13d45156a7c574b88332e822f6fb Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 7 Jul 2021 16:53:43 -0400 Subject: [PATCH 182/398] r/aws_dx_gateway_association_proposal: Document alternate resource import format. --- .../service/directconnect/finder/finder.go | 2 +- ...rce_aws_dx_gateway_association_proposal.go | 87 +++++++++---------- ...gateway_association_proposal.html.markdown | 11 ++- 3 files changed, 52 insertions(+), 48 deletions(-) diff --git a/aws/internal/service/directconnect/finder/finder.go b/aws/internal/service/directconnect/finder/finder.go index f8af3c5a4bd..9bcf768f73e 100644 --- a/aws/internal/service/directconnect/finder/finder.go +++ b/aws/internal/service/directconnect/finder/finder.go @@ -14,7 +14,7 @@ func GatewayAssociationByID(conn *directconnect.DirectConnect, id string) (*dire return GatewayAssociation(conn, input) } -func GatewayAssociationByAssociatedGatewayIDAndDirectConnectGatewayID(conn *directconnect.DirectConnect, associatedGatewayID, directConnectGatewayID string) (*directconnect.GatewayAssociation, error) { +func GatewayAssociationByDirectConnectGatewayIDAndAssociatedGatewayID(conn *directconnect.DirectConnect, directConnectGatewayID, associatedGatewayID string) (*directconnect.GatewayAssociation, error) { input := &directconnect.DescribeDirectConnectGatewayAssociationsInput{ AssociatedGatewayId: aws.String(associatedGatewayID), DirectConnectGatewayId: aws.String(directConnectGatewayID), diff --git a/aws/resource_aws_dx_gateway_association_proposal.go b/aws/resource_aws_dx_gateway_association_proposal.go index 249ad9ff0bb..6218cde43ea 100644 --- a/aws/resource_aws_dx_gateway_association_proposal.go +++ b/aws/resource_aws_dx_gateway_association_proposal.go @@ -11,6 +11,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/directconnect/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func resourceAwsDxGatewayAssociationProposal() *schema.Resource { @@ -18,8 +20,9 @@ func resourceAwsDxGatewayAssociationProposal() *schema.Resource { Create: resourceAwsDxGatewayAssociationProposalCreate, Read: resourceAwsDxGatewayAssociationProposalRead, Delete: resourceAwsDxGatewayAssociationProposalDelete, + Importer: &schema.ResourceImporter{ - State: dxGatewayAssociationProposalImport, + State: resourceAwsDxGatewayAssociationProposalImport, }, CustomizeDiff: customdiff.Sequence( @@ -296,64 +299,56 @@ func flattenDirectConnectGatewayAssociationProposalAllowedPrefixes(routeFilterPr return allowedPrefixes } -func dxGatewayAssociationProposalImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - // example: c2ede9b4-bbc6-4d33-923c-bc4feEXAMPLE/186c8187-36f4-472e-9268-107aaEXAMPLE/0d95359280EXAMPLE +func resourceAwsDxGatewayAssociationProposalImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + parts := strings.Split(strings.ToLower(d.Id()), "/") - errStr := "unexpected format of import string (%q), expected PROPOSALID/DXGATEWAYID/TARGETGATEWAYID]*: %s" - importStr := d.Id() - log.Printf("[DEBUG] Validating import string (%s) for Direct Connect Gateway Association Proposal", importStr) + var proposalID, directConnectGatewayID, associatedGatewayID string + switch n := len(parts); n { + case 1: + return []*schema.ResourceData{d}, nil - parts := strings.Split(strings.ToLower(importStr), "/") - if len(parts) < 1 { - return nil, fmt.Errorf(errStr, importStr, "too few parts") - } - var propId, dxgwId, gwId string - propId = parts[0] + case 3: + proposalID = parts[0] + directConnectGatewayID = parts[1] + associatedGatewayID = parts[2] - conn := meta.(*AWSClient).dxconn - if propId != "" { - p, err := describeDirectConnectGatewayAssociationProposal(conn, propId) - if err != nil { - return nil, err + if directConnectGatewayID == "" || associatedGatewayID == "" { + return nil, fmt.Errorf("Incorrect resource ID format: %q. DXGATEWAYID and TARGETGATEWAYID must not be empty strings", d.Id()) } - if p != nil { - // proposal still exists normal import - return schema.ImportStatePassthrough(d, meta) - } - // proposal may not exist, but that's fine - } - d.SetId(propId) - if len(parts) == 1 { - // requesting just the prop id - return schema.ImportStatePassthrough(d, meta) - } else if len(parts) < 3 { - return nil, fmt.Errorf(errStr, importStr, "too few parts") + break + + default: + return nil, fmt.Errorf("Incorrect resource ID format: %q. Expected PROPOSALID or PROPOSALID/DXGATEWAYID/TARGETGATEWAYID", d.Id()) } - dxgwId = parts[1] - gwId = parts[2] + conn := meta.(*AWSClient).dxconn - if gwId != "" && dxgwId != "" { - input := directconnect.DescribeDirectConnectGatewayAssociationsInput{ - AssociatedGatewayId: aws.String(gwId), - DirectConnectGatewayId: aws.String(dxgwId), - } - resp, err := conn.DescribeDirectConnectGatewayAssociations(&input) - if err != nil { + if proposalID != "" { + _, err := finder.GatewayAssociationProposalByID(conn, proposalID) + + if tfresource.NotFound(err) { + // Proposal not found. + } else if err != nil { return nil, err - } + } else { + // Proposal still exists. + d.SetId(proposalID) - id := dxGatewayAssociationId(dxgwId, gwId) - if n := len(resp.DirectConnectGatewayAssociations); n != 1 { - return nil, fmt.Errorf("Found %d Direct Connect gateway associations for %s, expected 1", n, id) + return []*schema.ResourceData{d}, nil } - d.Set("associated_gateway_id", gwId) - d.Set("dx_gateway_id", dxgwId) - } else { - return nil, fmt.Errorf(errStr, importStr, "missing parts") } + _, err := finder.GatewayAssociationByDirectConnectGatewayIDAndAssociatedGatewayID(conn, directConnectGatewayID, associatedGatewayID) + + if err != nil { + return nil, err + } + + d.SetId(proposalID) + d.Set("associated_gateway_id", associatedGatewayID) + d.Set("dx_gateway_id", directConnectGatewayID) + return []*schema.ResourceData{d}, nil } diff --git a/website/docs/r/dx_gateway_association_proposal.html.markdown b/website/docs/r/dx_gateway_association_proposal.html.markdown index a64c7e4608f..5701772d2b4 100644 --- a/website/docs/r/dx_gateway_association_proposal.html.markdown +++ b/website/docs/r/dx_gateway_association_proposal.html.markdown @@ -41,8 +41,17 @@ In addition to all arguments above, the following attributes are exported: ## Import -Direct Connect Gateway Association Proposals can be imported using the proposal ID, e.g. +Direct Connect Gateway Association Proposals can be imported using either a proposal ID or proposal ID, Direct Connect Gateway ID and associated gateway ID separated by `/`, e.g. ``` $ terraform import aws_dx_gateway_association_proposal.example ac90e981-b718-4364-872d-65478c84fafe ``` + +or + +``` +$ terraform import aws_dx_gateway_association_proposal.example ac90e981-b718-4364-872d-65478c84fafe/abcd1234-dcba-5678-be23-cdef9876ab45/vgw-12345678 +``` + +The latter case is useful when a previous proposal has been accepted and deleted by AWS. +The `aws_dx_gateway_association_proposal` resource will then represent a pseudo-proposal for the same Direct Connect Gateway and associated gateway. From a86577a1a686a023e6c958295aa00eeabbf22d10 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Wed, 7 Jul 2021 17:09:32 -0700 Subject: [PATCH 183/398] Allows specifying replica count for secondary members of a global cluster --- ...asticache_global_replication_group_test.go | 97 ++++-- ...ource_aws_elasticache_replication_group.go | 35 ++- ..._aws_elasticache_replication_group_test.go | 295 ++++++++++++++---- ...lasticache_replication_group.html.markdown | 6 +- 4 files changed, 325 insertions(+), 108 deletions(-) diff --git a/aws/resource_aws_elasticache_global_replication_group_test.go b/aws/resource_aws_elasticache_global_replication_group_test.go index 12836f6667c..a3f630399f5 100644 --- a/aws/resource_aws_elasticache_global_replication_group_test.go +++ b/aws/resource_aws_elasticache_global_replication_group_test.go @@ -281,6 +281,38 @@ func TestAccAWSElasticacheGlobalReplicationGroup_ReplaceSecondary_DifferentRegio }) } +func TestAccAWSElasticacheGlobalReplicationGroup_ClusterMode(t *testing.T) { + var globalReplicationGroup elasticache.GlobalReplicationGroup + var primaryReplicationGroup elasticache.ReplicationGroup + + rName := acctest.RandomWithPrefix("tf-acc-test") + + resourceName := "aws_elasticache_global_replication_group.test" + primaryReplicationGroupResourceName := "aws_elasticache_replication_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSElasticacheGlobalReplicationGroup(t) }, + ErrorCheck: testAccErrorCheck(t, elasticache.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSElasticacheGlobalReplicationGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSElasticacheGlobalReplicationGroupConfig_ClusterMode(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSElasticacheGlobalReplicationGroupExists(resourceName, &globalReplicationGroup), + testAccCheckAWSElasticacheReplicationGroupExists(primaryReplicationGroupResourceName, &primaryReplicationGroup), + resource.TestCheckResourceAttr(resourceName, "cluster_enabled", "true"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testAccCheckAWSElasticacheGlobalReplicationGroupExists(resourceName string, v *elasticache.GlobalReplicationGroup) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] @@ -388,9 +420,9 @@ resource "aws_elasticache_replication_group" "test" { func testAccAWSElasticacheGlobalReplicationGroupConfig_MultipleSecondaries(rName string) string { return composeConfig( testAccMultipleRegionProviderConfig(3), - testAccElasticacheVpcBaseWithProvider(rName, "primary", ProviderNameAws), - testAccElasticacheVpcBaseWithProvider(rName, "alternate", ProviderNameAwsAlternate), - testAccElasticacheVpcBaseWithProvider(rName, "third", ProviderNameAwsThird), + testAccElasticacheVpcBaseWithProvider(rName, "primary", ProviderNameAws, 1), + testAccElasticacheVpcBaseWithProvider(rName, "alternate", ProviderNameAwsAlternate, 1), + testAccElasticacheVpcBaseWithProvider(rName, "third", ProviderNameAwsThird, 1), fmt.Sprintf(` resource "aws_elasticache_global_replication_group" "test" { provider = aws @@ -443,9 +475,9 @@ resource "aws_elasticache_replication_group" "third" { func testAccAWSElasticacheReplicationGroupConfig_ReplaceSecondary_DifferentRegion_Setup(rName string) string { return composeConfig( testAccMultipleRegionProviderConfig(3), - testAccElasticacheVpcBaseWithProvider(rName, "primary", ProviderNameAws), - testAccElasticacheVpcBaseWithProvider(rName, "secondary", ProviderNameAwsAlternate), - testAccElasticacheVpcBaseWithProvider(rName, "third", ProviderNameAwsThird), + testAccElasticacheVpcBaseWithProvider(rName, "primary", ProviderNameAws, 1), + testAccElasticacheVpcBaseWithProvider(rName, "secondary", ProviderNameAwsAlternate, 1), + testAccElasticacheVpcBaseWithProvider(rName, "third", ProviderNameAwsThird, 1), fmt.Sprintf(` resource "aws_elasticache_global_replication_group" "test" { provider = aws @@ -486,9 +518,9 @@ resource "aws_elasticache_replication_group" "secondary" { func testAccAWSElasticacheReplicationGroupConfig_ReplaceSecondary_DifferentRegion_Move(rName string) string { return composeConfig( testAccMultipleRegionProviderConfig(3), - testAccElasticacheVpcBaseWithProvider(rName, "primary", ProviderNameAws), - testAccElasticacheVpcBaseWithProvider(rName, "secondary", ProviderNameAwsAlternate), - testAccElasticacheVpcBaseWithProvider(rName, "third", ProviderNameAwsThird), + testAccElasticacheVpcBaseWithProvider(rName, "primary", ProviderNameAws, 1), + testAccElasticacheVpcBaseWithProvider(rName, "secondary", ProviderNameAwsAlternate, 1), + testAccElasticacheVpcBaseWithProvider(rName, "third", ProviderNameAwsThird, 1), fmt.Sprintf(` resource "aws_elasticache_global_replication_group" "test" { provider = aws @@ -526,7 +558,32 @@ resource "aws_elasticache_replication_group" "third" { `, rName)) } -func testAccElasticacheVpcBaseWithProvider(rName, name, provider string) string { +func testAccAWSElasticacheGlobalReplicationGroupConfig_ClusterMode(rName string) string { + return fmt.Sprintf(` +resource "aws_elasticache_global_replication_group" "test" { + global_replication_group_id_suffix = %[1]q + primary_replication_group_id = aws_elasticache_replication_group.test.id +} + +resource "aws_elasticache_replication_group" "test" { + replication_group_id = %[1]q + replication_group_description = "test" + + engine = "redis" + engine_version = "6.x" + node_type = "cache.m5.large" + + parameter_group_name = "default.redis6.x.cluster.on" + automatic_failover_enabled = true + cluster_mode { + num_node_groups = 2 + replicas_per_node_group = 1 + } +} +`, rName) +} + +func testAccElasticacheVpcBaseWithProvider(rName, name, provider string, subnetCount int) string { return composeConfig( testAccAvailableAZsNoOptInConfigWithProvider(name, provider), fmt.Sprintf(` @@ -538,27 +595,21 @@ resource "aws_vpc" "%[1]s" { resource "aws_subnet" "%[1]s" { provider = %[2]s + + count = %[4]d vpc_id = aws_vpc.%[1]s.id - cidr_block = "192.168.0.0/20" - availability_zone = data.aws_availability_zones.%[1]s.names[0] - - tags = { - Name = "tf-acc-elasticache-replication-group-at-rest-encryption" - } + cidr_block = "192.168.${count.index}.0/24" + availability_zone = data.aws_availability_zones.%[1]s.names[count.index] } resource "aws_elasticache_subnet_group" "%[1]s" { provider = %[2]s - name = %[3]q - description = "tf-test-cache-subnet-group-descr" - - subnet_ids = [ - aws_subnet.%[1]s.id, - ] + name = %[3]q + subnet_ids = aws_subnet.%[1]s[*].id } -`, name, provider, rName), +`, name, provider, rName, subnetCount), ) } diff --git a/aws/resource_aws_elasticache_replication_group.go b/aws/resource_aws_elasticache_replication_group.go index ebcade4f541..831ec9bfd30 100644 --- a/aws/resource_aws_elasticache_replication_group.go +++ b/aws/resource_aws_elasticache_replication_group.go @@ -81,18 +81,19 @@ func resourceAwsElasticacheReplicationGroup() *schema.Resource { Computed: true, }, "cluster_mode": { - Type: schema.TypeList, - Optional: true, - Computed: true, - MaxItems: 1, - ExactlyOneOf: []string{"cluster_mode", "number_cache_clusters"}, + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "replicas_per_node_group": { - Type: schema.TypeInt, - Required: true, - }, "num_node_groups": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ConflictsWith: []string{"number_cache_clusters", "global_replication_group_id"}, + }, + "replicas_per_node_group": { Type: schema.TypeInt, Required: true, }, @@ -126,8 +127,7 @@ func resourceAwsElasticacheReplicationGroup() *schema.Resource { ForceNew: true, Computed: true, ConflictsWith: []string{ - "automatic_failover_enabled", - "cluster_mode", // should/will be "num_node_groups" + "cluster_mode.0.num_node_groups", // should/will be top-level "num_node_groups" "parameter_group_name", "engine", "engine_version", @@ -171,15 +171,18 @@ func resourceAwsElasticacheReplicationGroup() *schema.Resource { ValidateFunc: validateArn, }, "number_cache_clusters": { - Type: schema.TypeInt, - Computed: true, - Optional: true, - ExactlyOneOf: []string{"cluster_mode", "number_cache_clusters"}, + Type: schema.TypeInt, + Computed: true, + Optional: true, + ConflictsWith: []string{"cluster_mode.0.num_node_groups"}, }, "parameter_group_name": { Type: schema.TypeString, Optional: true, Computed: true, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + return strings.HasPrefix(old, "global-datastore-") + }, }, "port": { Type: schema.TypeInt, @@ -412,7 +415,7 @@ func resourceAwsElasticacheReplicationGroupCreate(d *schema.ResourceData, meta i clusterModeList := clusterMode.([]interface{}) attributes := clusterModeList[0].(map[string]interface{}) - if v, ok := attributes["num_node_groups"]; ok { + if v, ok := attributes["num_node_groups"]; ok && v != 0 { params.NumNodeGroups = aws.Int64(int64(v.(int))) } diff --git a/aws/resource_aws_elasticache_replication_group_test.go b/aws/resource_aws_elasticache_replication_group_test.go index 398a5acb3c5..5822a2db70d 100644 --- a/aws/resource_aws_elasticache_replication_group_test.go +++ b/aws/resource_aws_elasticache_replication_group_test.go @@ -95,19 +95,19 @@ func TestAccAWSElasticacheReplicationGroup_basic(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccAWSElasticacheReplicationGroupConfig(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSElasticacheReplicationGroupExists(resourceName, &rg), resource.TestCheckResourceAttr(resourceName, "engine", "redis"), testAccCheckResourceAttrRegionalARN(resourceName, "arn", "elasticache", fmt.Sprintf("replicationgroup:%s", rName)), - resource.TestCheckResourceAttr(resourceName, "number_cache_clusters", "2"), + resource.TestCheckResourceAttr(resourceName, "number_cache_clusters", "1"), resource.TestCheckResourceAttr(resourceName, "multi_az_enabled", "false"), resource.TestCheckResourceAttr(resourceName, "automatic_failover_enabled", "false"), - resource.TestCheckResourceAttr(resourceName, "member_clusters.#", "2"), + resource.TestCheckResourceAttr(resourceName, "member_clusters.#", "1"), resource.TestCheckResourceAttr(resourceName, "auto_minor_version_upgrade", "false"), resource.TestCheckResourceAttr(resourceName, "parameter_group_name", "default.redis6.x"), resource.TestCheckResourceAttr(resourceName, "cluster_mode.#", "1"), - resource.TestCheckResourceAttr(resourceName, "cluster_mode.0.replicas_per_node_group", "1"), resource.TestCheckResourceAttr(resourceName, "cluster_mode.0.num_node_groups", "1"), + resource.TestCheckResourceAttr(resourceName, "cluster_mode.0.replicas_per_node_group", "0"), resource.TestCheckResourceAttr(resourceName, "cluster_enabled", "false"), resource.TestCheckResourceAttr(resourceName, "engine_version", "6.x"), resource.TestMatchResourceAttr(resourceName, "engine_version_actual", regexp.MustCompile(`^6\.[[:digit:]]+\.[[:digit:]]+$`)), @@ -138,8 +138,7 @@ func TestAccAWSElasticacheReplicationGroup_Uppercase(t *testing.T) { Config: testAccAWSElasticacheReplicationGroupConfig_Uppercase(strings.ToUpper(rName)), Check: resource.ComposeTestCheckFunc( testAccCheckAWSElasticacheReplicationGroupExists(resourceName, &rg), - resource.TestCheckResourceAttr( - resourceName, "replication_group_id", rName), + resource.TestCheckResourceAttr(resourceName, "replication_group_id", rName), ), }, { @@ -230,7 +229,7 @@ func TestAccAWSElasticacheReplicationGroup_disappears(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccAWSElasticacheReplicationGroupConfig(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSElasticacheReplicationGroupExists(resourceName, &rg), testAccCheckResourceDisappears(testAccProvider, resourceAwsElasticacheReplicationGroup(), resourceName), ), @@ -253,14 +252,11 @@ func TestAccAWSElasticacheReplicationGroup_updateDescription(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccAWSElasticacheReplicationGroupConfig(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSElasticacheReplicationGroupExists(resourceName, &rg), - resource.TestCheckResourceAttr( - resourceName, "number_cache_clusters", "2"), - resource.TestCheckResourceAttr( - resourceName, "replication_group_description", "test description"), - resource.TestCheckResourceAttr( - resourceName, "auto_minor_version_upgrade", "false"), + resource.TestCheckResourceAttr(resourceName, "number_cache_clusters", "1"), + resource.TestCheckResourceAttr(resourceName, "replication_group_description", "test description"), + resource.TestCheckResourceAttr(resourceName, "auto_minor_version_upgrade", "false"), ), }, { @@ -271,14 +267,11 @@ func TestAccAWSElasticacheReplicationGroup_updateDescription(t *testing.T) { }, { Config: testAccAWSElasticacheReplicationGroupConfigUpdatedDescription(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSElasticacheReplicationGroupExists(resourceName, &rg), - resource.TestCheckResourceAttr( - resourceName, "number_cache_clusters", "2"), - resource.TestCheckResourceAttr( - resourceName, "replication_group_description", "updated description"), - resource.TestCheckResourceAttr( - resourceName, "auto_minor_version_upgrade", "true"), + resource.TestCheckResourceAttr(resourceName, "number_cache_clusters", "1"), + resource.TestCheckResourceAttr(resourceName, "replication_group_description", "updated description"), + resource.TestCheckResourceAttr(resourceName, "auto_minor_version_upgrade", "true"), ), }, }, @@ -298,10 +291,9 @@ func TestAccAWSElasticacheReplicationGroup_updateMaintenanceWindow(t *testing.T) Steps: []resource.TestStep{ { Config: testAccAWSElasticacheReplicationGroupConfig(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSElasticacheReplicationGroupExists(resourceName, &rg), - resource.TestCheckResourceAttr( - resourceName, "maintenance_window", "tue:06:30-tue:07:30"), + resource.TestCheckResourceAttr(resourceName, "maintenance_window", "tue:06:30-tue:07:30"), ), }, { @@ -312,10 +304,9 @@ func TestAccAWSElasticacheReplicationGroup_updateMaintenanceWindow(t *testing.T) }, { Config: testAccAWSElasticacheReplicationGroupConfigUpdatedMaintenanceWindow(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSElasticacheReplicationGroupExists(resourceName, &rg), - resource.TestCheckResourceAttr( - resourceName, "maintenance_window", "wed:03:00-wed:06:00"), + resource.TestCheckResourceAttr(resourceName, "maintenance_window", "wed:03:00-wed:06:00"), ), }, }, @@ -335,12 +326,10 @@ func TestAccAWSElasticacheReplicationGroup_updateNodeSize(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccAWSElasticacheReplicationGroupConfig(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSElasticacheReplicationGroupExists(resourceName, &rg), - resource.TestCheckResourceAttr( - resourceName, "number_cache_clusters", "2"), - resource.TestCheckResourceAttr( - resourceName, "node_type", "cache.t3.small"), + resource.TestCheckResourceAttr(resourceName, "number_cache_clusters", "1"), + resource.TestCheckResourceAttr(resourceName, "node_type", "cache.t3.small"), ), }, { @@ -351,12 +340,10 @@ func TestAccAWSElasticacheReplicationGroup_updateNodeSize(t *testing.T) { }, { Config: testAccAWSElasticacheReplicationGroupConfigUpdatedNodeSize(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSElasticacheReplicationGroupExists(resourceName, &rg), - resource.TestCheckResourceAttr( - resourceName, "number_cache_clusters", "2"), - resource.TestCheckResourceAttr( - resourceName, "node_type", "cache.t3.medium"), + resource.TestCheckResourceAttr(resourceName, "number_cache_clusters", "1"), + resource.TestCheckResourceAttr(resourceName, "node_type", "cache.t3.medium"), ), }, }, @@ -910,7 +897,7 @@ func TestAccAWSElasticacheReplicationGroup_clusteringAndCacheNodesCausesError(t Steps: []resource.TestStep{ { Config: testAccAWSElasticacheReplicationGroupNativeRedisClusterErrorConfig(rInt, rName), - ExpectError: regexp.MustCompile("only one of `cluster_mode,number_cache_clusters` can be\\s+specified, but `cluster_mode,number_cache_clusters` were specified."), + ExpectError: regexp.MustCompile(`"cluster_mode.0.num_node_groups": conflicts with number_cache_clusters`), }, }, }) @@ -929,7 +916,7 @@ func TestAccAWSElasticacheReplicationGroup_enableSnapshotting(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccAWSElasticacheReplicationGroupConfig(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSElasticacheReplicationGroupExists(resourceName, &rg), resource.TestCheckResourceAttr(resourceName, "snapshot_retention_limit", "0"), ), @@ -942,7 +929,7 @@ func TestAccAWSElasticacheReplicationGroup_enableSnapshotting(t *testing.T) { }, { Config: testAccAWSElasticacheReplicationGroupConfigEnableSnapshotting(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSElasticacheReplicationGroupExists(resourceName, &rg), resource.TestCheckResourceAttr(resourceName, "snapshot_retention_limit", "2"), ), @@ -1557,13 +1544,13 @@ func TestAccAWSElasticacheReplicationGroup_GlobalReplicationGroupId_Basic(t *tes Steps: []resource.TestStep{ { Config: testAccAWSElasticacheReplicationGroupConfig_GlobalReplicationGroupId_Basic(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSElasticacheReplicationGroupExists(resourceName, &rg), resource.TestCheckResourceAttrPair(resourceName, "global_replication_group_id", "aws_elasticache_global_replication_group.test", "global_replication_group_id"), resource.TestCheckResourceAttrPair(resourceName, "node_type", primaryGroupResourceName, "node_type"), resource.TestCheckResourceAttrPair(resourceName, "engine", primaryGroupResourceName, "engine"), resource.TestCheckResourceAttrPair(resourceName, "engine_version", primaryGroupResourceName, "engine_version"), - resource.TestCheckResourceAttrPair(resourceName, "parameter_group_name", primaryGroupResourceName, "parameter_group_name"), + resource.TestMatchResourceAttr(resourceName, "parameter_group_name", regexp.MustCompile(fmt.Sprintf("^global-datastore-%s-", rName))), ), }, { @@ -1595,13 +1582,17 @@ func TestAccAWSElasticacheReplicationGroup_GlobalReplicationGroupId_Full(t *test Steps: []resource.TestStep{ { Config: testAccAWSElasticacheReplicationGroupConfig_GlobalReplicationGroupId_Full(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSElasticacheReplicationGroupExists(resourceName, &rg), resource.TestCheckResourceAttrPair(resourceName, "global_replication_group_id", "aws_elasticache_global_replication_group.test", "global_replication_group_id"), resource.TestCheckResourceAttrPair(resourceName, "node_type", primaryGroupResourceName, "node_type"), resource.TestCheckResourceAttrPair(resourceName, "engine", primaryGroupResourceName, "engine"), resource.TestCheckResourceAttrPair(resourceName, "engine_version", primaryGroupResourceName, "engine_version"), - resource.TestCheckResourceAttrPair(resourceName, "parameter_group_name", primaryGroupResourceName, "parameter_group_name"), + resource.TestMatchResourceAttr(resourceName, "parameter_group_name", regexp.MustCompile(fmt.Sprintf("^global-datastore-%s-", rName))), + + resource.TestCheckResourceAttrPair(resourceName, "number_cache_clusters", primaryGroupResourceName, "number_cache_clusters"), + resource.TestCheckResourceAttrPair(resourceName, "multi_az_enabled", primaryGroupResourceName, "multi_az_enabled"), + resource.TestCheckResourceAttrPair(resourceName, "automatic_failover_enabled", primaryGroupResourceName, "automatic_failover_enabled"), resource.TestCheckResourceAttr(resourceName, "port", "16379"), @@ -1620,6 +1611,61 @@ func TestAccAWSElasticacheReplicationGroup_GlobalReplicationGroupId_Full(t *test }) } +func TestAccAWSElasticacheReplicationGroup_GlobalReplicationGroupId_ClusterMode_Basic(t *testing.T) { + var providers []*schema.Provider + var rg1, rg2 elasticache.ReplicationGroup + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_elasticache_replication_group.test" + primaryGroupResourceName := "aws_elasticache_replication_group.primary" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccMultipleRegionPreCheck(t, 2) + }, + ErrorCheck: testAccErrorCheck(t, elasticache.EndpointsID), + ProviderFactories: testAccProviderFactoriesMultipleRegion(&providers, 2), + CheckDestroy: testAccCheckAWSElasticacheReplicationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSElasticacheReplicationGroupConfig_GlobalReplicationGroupId_ClusterMode(rName, 2, 1), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSElasticacheReplicationGroupExists(resourceName, &rg1), + resource.TestCheckResourceAttr(resourceName, "cluster_mode.#", "1"), + resource.TestCheckResourceAttr(resourceName, "cluster_mode.0.num_node_groups", "2"), + resource.TestCheckResourceAttr(resourceName, "cluster_mode.0.replicas_per_node_group", "1"), + resource.TestCheckResourceAttr(resourceName, "automatic_failover_enabled", "true"), + resource.TestMatchResourceAttr(resourceName, "parameter_group_name", regexp.MustCompile(fmt.Sprintf("^global-datastore-%s-", rName))), + + resource.TestCheckResourceAttr(primaryGroupResourceName, "cluster_mode.#", "1"), + resource.TestCheckResourceAttr(primaryGroupResourceName, "cluster_mode.0.num_node_groups", "2"), + resource.TestCheckResourceAttr(primaryGroupResourceName, "cluster_mode.0.replicas_per_node_group", "2"), + ), + }, + { + Config: testAccAWSElasticacheReplicationGroupConfig_GlobalReplicationGroupId_Basic(rName), + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"apply_immediately"}, + }, + { + Config: testAccAWSElasticacheReplicationGroupConfig_GlobalReplicationGroupId_ClusterMode(rName, 1, 3), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSElasticacheReplicationGroupExists(resourceName, &rg2), + resource.TestCheckResourceAttr(resourceName, "cluster_mode.#", "1"), + resource.TestCheckResourceAttr(resourceName, "cluster_mode.0.num_node_groups", "2"), + resource.TestCheckResourceAttr(resourceName, "cluster_mode.0.replicas_per_node_group", "3"), + + resource.TestCheckResourceAttr(primaryGroupResourceName, "cluster_mode.#", "1"), + resource.TestCheckResourceAttr(primaryGroupResourceName, "cluster_mode.0.num_node_groups", "2"), + resource.TestCheckResourceAttr(primaryGroupResourceName, "cluster_mode.0.replicas_per_node_group", "1"), + ), + }, + }, + }) +} + // Test for out-of-band deletion // Naming to allow grouping all TestAccAWSElasticacheReplicationGroup_GlobalReplicationGroupId_* tests func TestAccAWSElasticacheReplicationGroup_GlobalReplicationGroupId_disappears(t *testing.T) { // nosemgrep: acceptance-test-naming-parent-disappears @@ -1639,7 +1685,7 @@ func TestAccAWSElasticacheReplicationGroup_GlobalReplicationGroupId_disappears(t Steps: []resource.TestStep{ { Config: testAccAWSElasticacheReplicationGroupConfig_GlobalReplicationGroupId_Basic(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSElasticacheReplicationGroupExists(resourceName, &rg), testAccCheckResourceDisappears(testAccProvider, resourceAwsElasticacheReplicationGroup(), resourceName), ), @@ -1649,6 +1695,27 @@ func TestAccAWSElasticacheReplicationGroup_GlobalReplicationGroupId_disappears(t }) } +func TestAccAWSElasticacheReplicationGroup_GlobalReplicationGroupId_ClusterMode_Validation_NumNodeGroupsOnSecondary(t *testing.T) { + var providers []*schema.Provider + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccMultipleRegionPreCheck(t, 2) + }, + ErrorCheck: testAccErrorCheck(t, elasticache.EndpointsID), + ProviderFactories: testAccProviderFactoriesMultipleRegion(&providers, 2), + CheckDestroy: testAccCheckAWSElasticacheReplicationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSElasticacheReplicationGroupConfig_GlobalReplicationGroupId_ClusterMode_NumNodeGroupsOnSecondary(rName), + ExpectError: regexp.MustCompile(`"global_replication_group_id": conflicts with cluster_mode.0.num_node_groups`), + }, + }, + }) +} + func testAccCheckAWSElasticacheReplicationGroupExists(n string, v *elasticache.ReplicationGroup) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -1758,7 +1825,6 @@ resource "aws_elasticache_replication_group" "test" { replication_group_id = %[1]q replication_group_description = "test description" node_type = "cache.t3.small" - number_cache_clusters = 2 port = 6379 apply_immediately = true auto_minor_version_upgrade = false @@ -1838,7 +1904,6 @@ resource "aws_elasticache_replication_group" "test" { replication_group_id = %[1]q replication_group_description = "test description" node_type = "cache.t3.small" - number_cache_clusters = 2 port = 6379 apply_immediately = true auto_minor_version_upgrade = false @@ -1883,7 +1948,6 @@ resource "aws_elasticache_replication_group" "test" { replication_group_id = %[1]q replication_group_description = "updated description" node_type = "cache.t3.small" - number_cache_clusters = 2 port = 6379 apply_immediately = true auto_minor_version_upgrade = true @@ -1897,7 +1961,6 @@ resource "aws_elasticache_replication_group" "test" { replication_group_id = %[1]q replication_group_description = "updated description" node_type = "cache.t3.small" - number_cache_clusters = 2 port = 6379 apply_immediately = true auto_minor_version_upgrade = true @@ -1913,7 +1976,6 @@ resource "aws_elasticache_replication_group" "test" { replication_group_id = %[1]q replication_group_description = "updated description" node_type = "cache.t3.medium" - number_cache_clusters = 2 port = 6379 apply_immediately = true } @@ -2227,7 +2289,7 @@ resource "aws_subnet" "test2" { } resource "aws_elasticache_subnet_group" "test" { - name = "tf-test-cache-subnet-%03d" + name = "tf-test-cache-subnet-%03[1]d" description = "tf-test-cache-subnet-group-descr" subnet_ids = [ @@ -2237,7 +2299,7 @@ resource "aws_elasticache_subnet_group" "test" { } resource "aws_security_group" "test" { - name = "tf-test-security-group-%03d" + name = "tf-test-security-group-%03[1]d" description = "tf-test-security-group-descr" vpc_id = aws_vpc.test.id @@ -2250,7 +2312,7 @@ resource "aws_security_group" "test" { } resource "aws_elasticache_replication_group" "test" { - replication_group_id = "tf-%s" + replication_group_id = %[2]q replication_group_description = "test description" node_type = "cache.t2.micro" port = 6379 @@ -2265,7 +2327,7 @@ resource "aws_elasticache_replication_group" "test" { number_cache_clusters = 3 } -`, rInt, rInt, rName) +`, rInt, rName) } func testAccAWSElasticacheReplicationGroupNativeRedisClusterConfig(rName string, numNodeGroups, replicasPerNodeGroup int) string { @@ -2754,8 +2816,8 @@ resource "aws_elasticache_replication_group" "test" { func testAccAWSElasticacheReplicationGroupConfig_Validation_GlobalReplicationGroupIdAndNodeType(rName string) string { return composeConfig( testAccMultipleRegionProviderConfig(2), - testAccElasticacheVpcBaseWithProvider(rName, "test", ProviderNameAws), - testAccElasticacheVpcBaseWithProvider(rName, "primary", ProviderNameAwsAlternate), + testAccElasticacheVpcBaseWithProvider(rName, "test", ProviderNameAws, 1), + testAccElasticacheVpcBaseWithProvider(rName, "primary", ProviderNameAwsAlternate, 1), fmt.Sprintf(` resource "aws_elasticache_replication_group" "test" { provider = aws @@ -2798,8 +2860,8 @@ resource "aws_elasticache_replication_group" "primary" { func testAccAWSElasticacheReplicationGroupConfig_GlobalReplicationGroupId_Basic(rName string) string { return composeConfig( testAccMultipleRegionProviderConfig(2), - testAccElasticacheVpcBaseWithProvider(rName, "test", ProviderNameAws), - testAccElasticacheVpcBaseWithProvider(rName, "primary", ProviderNameAwsAlternate), + testAccElasticacheVpcBaseWithProvider(rName, "test", ProviderNameAws, 1), + testAccElasticacheVpcBaseWithProvider(rName, "primary", ProviderNameAwsAlternate, 1), fmt.Sprintf(` resource "aws_elasticache_replication_group" "test" { replication_group_id = "%[1]s-s" @@ -2807,8 +2869,6 @@ resource "aws_elasticache_replication_group" "test" { global_replication_group_id = aws_elasticache_global_replication_group.test.global_replication_group_id subnet_group_name = aws_elasticache_subnet_group.test.name - - number_cache_clusters = 1 } resource "aws_elasticache_global_replication_group" "test" { @@ -2838,8 +2898,8 @@ resource "aws_elasticache_replication_group" "primary" { func testAccAWSElasticacheReplicationGroupConfig_GlobalReplicationGroupId_Full(rName string) string { return composeConfig( testAccMultipleRegionProviderConfig(2), - testAccElasticacheVpcBaseWithProvider(rName, "test", ProviderNameAws), - testAccElasticacheVpcBaseWithProvider(rName, "primary", ProviderNameAwsAlternate), + testAccElasticacheVpcBaseWithProvider(rName, "test", ProviderNameAws, 2), + testAccElasticacheVpcBaseWithProvider(rName, "primary", ProviderNameAwsAlternate, 2), fmt.Sprintf(` resource "aws_elasticache_replication_group" "test" { replication_group_id = "%[1]s-s" @@ -2848,7 +2908,9 @@ resource "aws_elasticache_replication_group" "test" { subnet_group_name = aws_elasticache_subnet_group.test.name - number_cache_clusters = 1 + number_cache_clusters = 2 + automatic_failover_enabled = true + multi_az_enabled = true port = 16379 } @@ -2870,9 +2932,11 @@ resource "aws_elasticache_replication_group" "primary" { node_type = "cache.m5.large" - engine = "redis" - engine_version = "5.0.6" - number_cache_clusters = 1 + engine = "redis" + engine_version = "5.0.6" + number_cache_clusters = 2 + automatic_failover_enabled = true + multi_az_enabled = true port = 6379 @@ -2882,6 +2946,105 @@ resource "aws_elasticache_replication_group" "primary" { `, rName)) } +func testAccAWSElasticacheReplicationGroupConfig_GlobalReplicationGroupId_ClusterMode(rName string, primaryReplicaCount, secondaryReplicaCount int) string { + return composeConfig( + testAccMultipleRegionProviderConfig(2), + testAccElasticacheVpcBaseWithProvider(rName, "test", ProviderNameAws, 2), + testAccElasticacheVpcBaseWithProvider(rName, "primary", ProviderNameAwsAlternate, 2), + fmt.Sprintf(` +resource "aws_elasticache_replication_group" "test" { + replication_group_id = "%[1]s-s" + replication_group_description = "secondary" + global_replication_group_id = aws_elasticache_global_replication_group.test.global_replication_group_id + + subnet_group_name = aws_elasticache_subnet_group.test.name + + automatic_failover_enabled = true + cluster_mode { + replicas_per_node_group = %[3]d + } +} + +resource "aws_elasticache_global_replication_group" "test" { + provider = awsalternate + + global_replication_group_id_suffix = %[1]q + primary_replication_group_id = aws_elasticache_replication_group.primary.id +} + +resource "aws_elasticache_replication_group" "primary" { + provider = awsalternate + + replication_group_id = "%[1]s-p" + replication_group_description = "primary" + + subnet_group_name = aws_elasticache_subnet_group.primary.name + + engine = "redis" + engine_version = "6.x" + node_type = "cache.m5.large" + + parameter_group_name = "default.redis6.x.cluster.on" + + automatic_failover_enabled = true + cluster_mode { + num_node_groups = 2 + replicas_per_node_group = %[2]d + } +} +`, rName, primaryReplicaCount, secondaryReplicaCount)) +} + +func testAccAWSElasticacheReplicationGroupConfig_GlobalReplicationGroupId_ClusterMode_NumNodeGroupsOnSecondary(rName string) string { + return composeConfig( + testAccMultipleRegionProviderConfig(2), + testAccElasticacheVpcBaseWithProvider(rName, "test", ProviderNameAws, 2), + testAccElasticacheVpcBaseWithProvider(rName, "primary", ProviderNameAwsAlternate, 2), + fmt.Sprintf(` +resource "aws_elasticache_replication_group" "test" { + replication_group_id = "%[1]s-s" + replication_group_description = "secondary" + global_replication_group_id = aws_elasticache_global_replication_group.test.global_replication_group_id + + subnet_group_name = aws_elasticache_subnet_group.test.name + + automatic_failover_enabled = true + cluster_mode { + num_node_groups = 2 + replicas_per_node_group = 1 + } +} + +resource "aws_elasticache_global_replication_group" "test" { + provider = awsalternate + + global_replication_group_id_suffix = %[1]q + primary_replication_group_id = aws_elasticache_replication_group.primary.id +} + +resource "aws_elasticache_replication_group" "primary" { + provider = awsalternate + + replication_group_id = "%[1]s-p" + replication_group_description = "primary" + + subnet_group_name = aws_elasticache_subnet_group.primary.name + + engine = "redis" + engine_version = "6.x" + node_type = "cache.m5.large" + + parameter_group_name = "default.redis6.x.cluster.on" + + automatic_failover_enabled = true + cluster_mode { + num_node_groups = 2 + replicas_per_node_group = 1 + } +} +`, rName)) +} + func resourceAwsElasticacheReplicationGroupDisableAutomaticFailover(conn *elasticache.ElastiCache, replicationGroupID string, timeout time.Duration) error { return resourceAwsElasticacheReplicationGroupModify(conn, timeout, &elasticache.ModifyReplicationGroupInput{ ReplicationGroupId: aws.String(replicationGroupID), diff --git a/website/docs/r/elasticache_replication_group.html.markdown b/website/docs/r/elasticache_replication_group.html.markdown index 4ad29149a35..b41ac354c23 100644 --- a/website/docs/r/elasticache_replication_group.html.markdown +++ b/website/docs/r/elasticache_replication_group.html.markdown @@ -152,11 +152,11 @@ The following arguments are optional: * `auto_minor_version_upgrade` - (Optional) Specifies whether a minor engine upgrades will be applied automatically to the underlying Cache Cluster instances during the maintenance window. This parameter is currently not supported by the AWS API. Defaults to `true`. * `automatic_failover_enabled` - (Optional) Specifies whether a read-only replica will be automatically promoted to read/write primary if the existing primary fails. If enabled, `number_cache_clusters` must be greater than 1. Must be enabled for Redis (cluster mode enabled) replication groups. Defaults to `false`. * `availability_zones` - (Optional) A list of EC2 availability zones in which the replication group's cache clusters will be created. The order of the availability zones in the list is not important. -* `cluster_mode` - (Optional) Create a native Redis cluster. `automatic_failover_enabled` must be set to true. Cluster Mode documented below. Only 1 `cluster_mode` block is allowed. One of `number_cache_clusters` or `cluster_mode` is required. Note that configuring this block does not enable cluster mode, i.e. data sharding, this requires using a parameter group that has the parameter `cluster-enabled` set to true. +* `cluster_mode` - (Optional) Create a native Redis cluster. `automatic_failover_enabled` must be set to true. Cluster Mode documented below. Only 1 `cluster_mode` block is allowed. Note that configuring this block does not enable cluster mode, i.e. data sharding, this requires using a parameter group that has the parameter `cluster-enabled` set to true. * `engine` - (Optional) The name of the cache engine to be used for the clusters in this replication group. The only valid value is `redis`. * `engine_version` - (Optional) The version number of the cache engine to be used for the cache clusters in this replication group. If the version is 6 or higher, only the major version can be set, e.g. `6.x`, otherwise, specify the full version desired, e.g. `5.0.6`. The actual engine version used is returned in the attribute `engine_version_actual`, [defined below](#engine_version_actual). * `final_snapshot_identifier` - (Optional) The name of your final node group (shard) snapshot. ElastiCache creates the snapshot from the primary node in the cluster. If omitted, no final snapshot will be made. -* `global_replication_group_id` - (Optional) The ID of the global replication group to which this replication group should belong. If this parameter is specified, the replication group is added to the specified global replication group as a secondary replication group; otherwise, the replication group is not part of any global replication group. +* `global_replication_group_id` - (Optional) The ID of the global replication group to which this replication group should belong. If this parameter is specified, the replication group is added to the specified global replication group as a secondary replication group; otherwise, the replication group is not part of any global replication group. If `global_replication_group_id` is set, the `num_node_groups` parameter of the `cluster_mode` block cannot be set. * `kms_key_id` - (Optional) The ARN of the key that you wish to use if encrypting at rest. If not supplied, uses service managed encryption. Can be specified only if `at_rest_encryption_enabled = true`. * `maintenance_window` – (Optional) Specifies the weekly time range for when maintenance on the cache cluster is performed. The format is `ddd:hh24:mi-ddd:hh24:mi` (24H Clock UTC). The minimum maintenance window is a 60 minute period. Example: `sun:05:00-sun:09:00` * `multi_az_enabled` - (Optional) Specifies whether to enable Multi-AZ Support for the replication group. If `true`, `automatic_failover_enabled` must also be enabled. Defaults to `false`. @@ -177,7 +177,7 @@ The following arguments are optional: ### cluster_mode -* `num_node_groups` - (Required) Number of node groups (shards) for this Redis replication group. Changing this number will trigger an online resizing operation before other settings modifications. +* `num_node_groups` - (Optional) Number of node groups (shards) for this Redis replication group. Changing this number will trigger an online resizing operation before other settings modifications. Required unless `global_replication_group_id` is set. * `replicas_per_node_group` - (Required) Number of replica nodes in each node group. Valid values are 0 to 5. Changing this number will trigger an online resizing operation before other settings modifications. ## Attributes Reference From f5674848e0381f26796d32b5ea25df8798107dc9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Jul 2021 06:10:24 +0000 Subject: [PATCH 184/398] build(deps): bump github.com/aws/aws-sdk-go from 1.39.0 to 1.39.2 Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.39.0 to 1.39.2. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Changelog](https://github.com/aws/aws-sdk-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.39.0...v1.39.2) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 0bdea89223d..f8902716b7c 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.16 require ( github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect - github.com/aws/aws-sdk-go v1.39.0 + github.com/aws/aws-sdk-go v1.39.2 github.com/beevik/etree v1.1.0 github.com/fatih/color v1.9.0 // indirect github.com/hashicorp/aws-sdk-go-base v0.7.1 diff --git a/go.sum b/go.sum index 009b6a2c58d..d87b7997d6f 100644 --- a/go.sum +++ b/go.sum @@ -66,8 +66,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= github.com/aws/aws-sdk-go v1.25.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.31.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/aws/aws-sdk-go v1.39.0 h1:74BBwkEmiqBbi2CGflEh34l0YNtIibTjZsibGarkNjo= -github.com/aws/aws-sdk-go v1.39.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go v1.39.2 h1:t+n2j0QfAmGqSQVb1VIGulhSMjfaZ/RqSGlcRKGED9Y= +github.com/aws/aws-sdk-go v1.39.2/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= @@ -411,10 +411,10 @@ golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210326060303-6b1517762897 h1:KrsHThm5nFk34YtATK1LsThyGhGbGe1olrte/HInHvs= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= +golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= +golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -460,10 +460,10 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79 h1:RX8C8PRZc2hTIod4ds8ij+/4RQX3AqhYj3uOHmyaz4E= golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= @@ -473,8 +473,9 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From 0e1a17e22d3cbc1e1931e02072a4662791afe7c5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Jul 2021 06:10:43 +0000 Subject: [PATCH 185/398] build(deps): bump github.com/aws/aws-sdk-go in /awsproviderlint Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.39.0 to 1.39.2. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Changelog](https://github.com/aws/aws-sdk-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.39.0...v1.39.2) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- awsproviderlint/go.mod | 2 +- awsproviderlint/go.sum | 11 +- .../aws/aws-sdk-go/aws/endpoints/defaults.go | 7 + .../github.com/aws/aws-sdk-go/aws/version.go | 2 +- .../aws/aws-sdk-go/service/sts/api.go | 632 +++++++++--------- .../aws/aws-sdk-go/service/sts/doc.go | 10 +- .../aws/aws-sdk-go/service/sts/errors.go | 18 +- .../golang.org/x/net/http/httpguts/httplex.go | 10 +- .../vendor/golang.org/x/net/http2/Dockerfile | 2 +- .../vendor/golang.org/x/net/http2/ascii.go | 53 ++ .../x/net/http2/client_conn_pool.go | 79 ++- .../vendor/golang.org/x/net/http2/go115.go | 27 + .../golang.org/x/net/http2/headermap.go | 7 +- .../golang.org/x/net/http2/not_go115.go | 31 + .../vendor/golang.org/x/net/http2/server.go | 38 +- .../golang.org/x/net/http2/transport.go | 165 +++-- .../vendor/golang.org/x/net/http2/write.go | 7 +- .../golang.org/x/net/idna/idna10.0.0.go | 113 ++-- .../vendor/golang.org/x/net/idna/idna9.0.0.go | 93 ++- .../x/text/secure/bidirule/bidirule10.0.0.go | 1 + .../x/text/secure/bidirule/bidirule9.0.0.go | 1 + .../x/text/unicode/bidi/tables10.0.0.go | 1 + .../x/text/unicode/bidi/tables11.0.0.go | 1 + .../x/text/unicode/bidi/tables12.0.0.go | 1 + .../x/text/unicode/bidi/tables13.0.0.go | 1 + .../x/text/unicode/bidi/tables9.0.0.go | 1 + .../x/text/unicode/norm/tables10.0.0.go | 1 + .../x/text/unicode/norm/tables11.0.0.go | 1 + .../x/text/unicode/norm/tables12.0.0.go | 1 + .../x/text/unicode/norm/tables13.0.0.go | 1 + .../x/text/unicode/norm/tables9.0.0.go | 1 + awsproviderlint/vendor/modules.txt | 6 +- 32 files changed, 796 insertions(+), 529 deletions(-) create mode 100644 awsproviderlint/vendor/golang.org/x/net/http2/ascii.go create mode 100644 awsproviderlint/vendor/golang.org/x/net/http2/go115.go create mode 100644 awsproviderlint/vendor/golang.org/x/net/http2/not_go115.go diff --git a/awsproviderlint/go.mod b/awsproviderlint/go.mod index fca5bc82fc9..bdd8e849828 100644 --- a/awsproviderlint/go.mod +++ b/awsproviderlint/go.mod @@ -3,7 +3,7 @@ module github.com/terraform-providers/terraform-provider-aws/awsproviderlint go 1.16 require ( - github.com/aws/aws-sdk-go v1.39.0 + github.com/aws/aws-sdk-go v1.39.2 github.com/bflad/tfproviderlint v0.27.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.7.0 golang.org/x/tools v0.0.0-20201028111035-eafbe7b904eb diff --git a/awsproviderlint/go.sum b/awsproviderlint/go.sum index cc485a97a22..834e806f661 100644 --- a/awsproviderlint/go.sum +++ b/awsproviderlint/go.sum @@ -70,8 +70,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= github.com/aws/aws-sdk-go v1.25.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.39.0 h1:74BBwkEmiqBbi2CGflEh34l0YNtIibTjZsibGarkNjo= -github.com/aws/aws-sdk-go v1.39.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go v1.39.2 h1:t+n2j0QfAmGqSQVb1VIGulhSMjfaZ/RqSGlcRKGED9Y= +github.com/aws/aws-sdk-go v1.39.2/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/bflad/gopaniccheck v0.1.0 h1:tJftp+bv42ouERmUMWLoUn/5bi/iQZjHPznM00cP/bU= github.com/bflad/gopaniccheck v0.1.0/go.mod h1:ZCj2vSr7EqVeDaqVsWN4n2MwdROx1YL+LFo47TSWtsA= github.com/bflad/tfproviderlint v0.27.0 h1:KXF+dYaWJ/OSVyWIrk2NIYgQBMDDSOC4VQB/P+P5nhI= @@ -452,8 +452,9 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210326060303-6b1517762897 h1:KrsHThm5nFk34YtATK1LsThyGhGbGe1olrte/HInHvs= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= +golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= +golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -507,6 +508,7 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79 h1:RX8C8PRZc2hTIod4ds8ij+/4RQX3AqhYj3uOHmyaz4E= golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -515,8 +517,9 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go index 27797a72e98..ec4a25cd3f3 100644 --- a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go +++ b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go @@ -862,6 +862,7 @@ var awsPartition = partition{ "ap-southeast-2": endpoint{}, "eu-central-1": endpoint{}, "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, "fips": endpoint{ Hostname: "appstream2-fips.us-west-2.amazonaws.com", CredentialScope: credentialScope{ @@ -4813,6 +4814,12 @@ var awsPartition = partition{ Region: "eu-west-2", }, }, + "eu-west-3": endpoint{ + Hostname: "oidc.eu-west-3.amazonaws.com", + CredentialScope: credentialScope{ + Region: "eu-west-3", + }, + }, "us-east-1": endpoint{ Hostname: "oidc.us-east-1.amazonaws.com", CredentialScope: credentialScope{ diff --git a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/version.go b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/version.go index 62fbbf77404..3fb3be1d54d 100644 --- a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/version.go +++ b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/version.go @@ -5,4 +5,4 @@ package aws const SDKName = "aws-sdk-go" // SDKVersion is the version of this SDK -const SDKVersion = "1.39.0" +const SDKVersion = "1.39.2" diff --git a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/service/sts/api.go b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/service/sts/api.go index 17c46378899..3cffd533d91 100644 --- a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/service/sts/api.go +++ b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/service/sts/api.go @@ -57,19 +57,20 @@ func (c *STS) AssumeRoleRequest(input *AssumeRoleInput) (req *request.Request, o // AssumeRole API operation for AWS Security Token Service. // // Returns a set of temporary security credentials that you can use to access -// AWS resources that you might not normally have access to. These temporary -// credentials consist of an access key ID, a secret access key, and a security -// token. Typically, you use AssumeRole within your account or for cross-account -// access. For a comparison of AssumeRole with other API operations that produce -// temporary credentials, see Requesting Temporary Security Credentials (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html) -// and Comparing the AWS STS API operations (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#stsapi_comparison) +// Amazon Web Services resources that you might not normally have access to. +// These temporary credentials consist of an access key ID, a secret access +// key, and a security token. Typically, you use AssumeRole within your account +// or for cross-account access. For a comparison of AssumeRole with other API +// operations that produce temporary credentials, see Requesting Temporary Security +// Credentials (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html) +// and Comparing the STS API operations (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#stsapi_comparison) // in the IAM User Guide. // // Permissions // // The temporary security credentials created by AssumeRole can be used to make -// API calls to any AWS service with the following exception: You cannot call -// the AWS STS GetFederationToken or GetSessionToken API operations. +// API calls to any Amazon Web Services service with the following exception: +// You cannot call the STS GetFederationToken or GetSessionToken API operations. // // (Optional) You can pass inline or managed session policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) // to this operation. You can pass a single JSON policy document to use as an @@ -79,15 +80,15 @@ func (c *STS) AssumeRoleRequest(input *AssumeRoleInput) (req *request.Request, o // to this operation returns new temporary credentials. The resulting session's // permissions are the intersection of the role's identity-based policy and // the session policies. You can use the role's temporary credentials in subsequent -// AWS API calls to access resources in the account that owns the role. You -// cannot use session policies to grant more permissions than those allowed -// by the identity-based policy of the role that is being assumed. For more -// information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) +// Amazon Web Services API calls to access resources in the account that owns +// the role. You cannot use session policies to grant more permissions than +// those allowed by the identity-based policy of the role that is being assumed. +// For more information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) // in the IAM User Guide. // -// To assume a role from a different account, your AWS account must be trusted -// by the role. The trust relationship is defined in the role's trust policy -// when the role is created. That trust policy states which accounts are allowed +// To assume a role from a different account, your account must be trusted by +// the role. The trust relationship is defined in the role's trust policy when +// the role is created. That trust policy states which accounts are allowed // to delegate that access to users in the account. // // A user who wants to access a role in a different account must also have permissions @@ -129,12 +130,12 @@ func (c *STS) AssumeRoleRequest(input *AssumeRoleInput) (req *request.Request, o // // (Optional) You can include multi-factor authentication (MFA) information // when you call AssumeRole. This is useful for cross-account scenarios to ensure -// that the user that assumes the role has been authenticated with an AWS MFA -// device. In that scenario, the trust policy of the role being assumed includes -// a condition that tests for MFA authentication. If the caller does not include -// valid MFA information, the request to assume the role is denied. The condition -// in a trust policy that tests for MFA authentication might look like the following -// example. +// that the user that assumes the role has been authenticated with an Amazon +// Web Services MFA device. In that scenario, the trust policy of the role being +// assumed includes a condition that tests for MFA authentication. If the caller +// does not include valid MFA information, the request to assume the role is +// denied. The condition in a trust policy that tests for MFA authentication +// might look like the following example. // // "Condition": {"Bool": {"aws:MultiFactorAuthPresent": true}} // @@ -160,11 +161,11 @@ func (c *STS) AssumeRoleRequest(input *AssumeRoleInput) (req *request.Request, o // // * ErrCodePackedPolicyTooLargeException "PackedPolicyTooLarge" // The request was rejected because the total packed size of the session policies -// and session tags combined was too large. An AWS conversion compresses the -// session policy document, session policy ARNs, and session tags into a packed -// binary format that has a separate limit. The error message indicates by percentage -// how close the policies and tags are to the upper size limit. For more information, -// see Passing Session Tags in STS (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) +// and session tags combined was too large. An Amazon Web Services conversion +// compresses the session policy document, session policy ARNs, and session +// tags into a packed binary format that has a separate limit. The error message +// indicates by percentage how close the policies and tags are to the upper +// size limit. For more information, see Passing Session Tags in STS (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) // in the IAM User Guide. // // You could receive this error even though you meet other defined session policy @@ -176,7 +177,8 @@ func (c *STS) AssumeRoleRequest(input *AssumeRoleInput) (req *request.Request, o // STS is not activated in the requested region for the account that is being // asked to generate credentials. The account administrator must use the IAM // console to activate STS in that region. For more information, see Activating -// and Deactivating AWS STS in an AWS Region (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) +// and Deactivating Amazon Web Services STS in an Amazon Web Services Region +// (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) // in the IAM User Guide. // // * ErrCodeExpiredTokenException "ExpiredTokenException" @@ -252,16 +254,17 @@ func (c *STS) AssumeRoleWithSAMLRequest(input *AssumeRoleWithSAMLInput) (req *re // // Returns a set of temporary security credentials for users who have been authenticated // via a SAML authentication response. This operation provides a mechanism for -// tying an enterprise identity store or directory to role-based AWS access -// without user-specific credentials or configuration. For a comparison of AssumeRoleWithSAML -// with the other API operations that produce temporary credentials, see Requesting -// Temporary Security Credentials (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html) -// and Comparing the AWS STS API operations (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#stsapi_comparison) +// tying an enterprise identity store or directory to role-based Amazon Web +// Services access without user-specific credentials or configuration. For a +// comparison of AssumeRoleWithSAML with the other API operations that produce +// temporary credentials, see Requesting Temporary Security Credentials (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html) +// and Comparing the STS API operations (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#stsapi_comparison) // in the IAM User Guide. // // The temporary security credentials returned by this operation consist of // an access key ID, a secret access key, and a security token. Applications -// can use these temporary security credentials to sign calls to AWS services. +// can use these temporary security credentials to sign calls to Amazon Web +// Services services. // // Session Duration // @@ -281,19 +284,19 @@ func (c *STS) AssumeRoleWithSAMLRequest(input *AssumeRoleWithSAMLInput) (req *re // in the IAM User Guide. // // Role chaining (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_terms-and-concepts.html#iam-term-role-chaining) -// limits your AWS CLI or AWS API role session to a maximum of one hour. When -// you use the AssumeRole API operation to assume a role, you can specify the -// duration of your role session with the DurationSeconds parameter. You can -// specify a parameter value of up to 43200 seconds (12 hours), depending on -// the maximum session duration setting for your role. However, if you assume +// limits your CLI or Amazon Web Services API role session to a maximum of one +// hour. When you use the AssumeRole API operation to assume a role, you can +// specify the duration of your role session with the DurationSeconds parameter. +// You can specify a parameter value of up to 43200 seconds (12 hours), depending +// on the maximum session duration setting for your role. However, if you assume // a role using role chaining and provide a DurationSeconds parameter value // greater than one hour, the operation fails. // // Permissions // // The temporary security credentials created by AssumeRoleWithSAML can be used -// to make API calls to any AWS service with the following exception: you cannot -// call the STS GetFederationToken or GetSessionToken API operations. +// to make API calls to any Amazon Web Services service with the following exception: +// you cannot call the STS GetFederationToken or GetSessionToken API operations. // // (Optional) You can pass inline or managed session policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) // to this operation. You can pass a single JSON policy document to use as an @@ -303,18 +306,19 @@ func (c *STS) AssumeRoleWithSAMLRequest(input *AssumeRoleWithSAMLInput) (req *re // to this operation returns new temporary credentials. The resulting session's // permissions are the intersection of the role's identity-based policy and // the session policies. You can use the role's temporary credentials in subsequent -// AWS API calls to access resources in the account that owns the role. You -// cannot use session policies to grant more permissions than those allowed -// by the identity-based policy of the role that is being assumed. For more -// information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) +// Amazon Web Services API calls to access resources in the account that owns +// the role. You cannot use session policies to grant more permissions than +// those allowed by the identity-based policy of the role that is being assumed. +// For more information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) // in the IAM User Guide. // -// Calling AssumeRoleWithSAML does not require the use of AWS security credentials. -// The identity of the caller is validated by using keys in the metadata document -// that is uploaded for the SAML provider entity for your identity provider. +// Calling AssumeRoleWithSAML does not require the use of Amazon Web Services +// security credentials. The identity of the caller is validated by using keys +// in the metadata document that is uploaded for the SAML provider entity for +// your identity provider. // -// Calling AssumeRoleWithSAML can result in an entry in your AWS CloudTrail -// logs. The entry includes the value in the NameID element of the SAML assertion. +// Calling AssumeRoleWithSAML can result in an entry in your CloudTrail logs. +// The entry includes the value in the NameID element of the SAML assertion. // We recommend that you use a NameIDType that is not associated with any personally // identifiable information (PII). For example, you could instead use the persistent // identifier (urn:oasis:names:tc:SAML:2.0:nameid-format:persistent). @@ -332,11 +336,11 @@ func (c *STS) AssumeRoleWithSAMLRequest(input *AssumeRoleWithSAMLInput) (req *re // and additional limits, see IAM and STS Character Limits (https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-limits.html#reference_iam-limits-entity-length) // in the IAM User Guide. // -// An AWS conversion compresses the passed session policies and session tags -// into a packed binary format that has a separate limit. Your request can fail -// for this limit even if your plaintext meets the other requirements. The PackedPolicySize -// response element indicates by percentage how close the policies and tags -// for your request are to the upper size limit. +// An Amazon Web Services conversion compresses the passed session policies +// and session tags into a packed binary format that has a separate limit. Your +// request can fail for this limit even if your plaintext meets the other requirements. +// The PackedPolicySize response element indicates by percentage how close the +// policies and tags for your request are to the upper size limit. // // You can pass a session tag with the same key as a tag that is attached to // the role. When you do, session tags override the role's tags with the same @@ -356,10 +360,11 @@ func (c *STS) AssumeRoleWithSAMLRequest(input *AssumeRoleWithSAMLInput) (req *re // SAML Configuration // // Before your application can call AssumeRoleWithSAML, you must configure your -// SAML identity provider (IdP) to issue the claims required by AWS. Additionally, -// you must use AWS Identity and Access Management (IAM) to create a SAML provider -// entity in your AWS account that represents your identity provider. You must -// also create an IAM role that specifies this SAML provider in its trust policy. +// SAML identity provider (IdP) to issue the claims required by Amazon Web Services. +// Additionally, you must use Identity and Access Management (IAM) to create +// a SAML provider entity in your Amazon Web Services account that represents +// your identity provider. You must also create an IAM role that specifies this +// SAML provider in its trust policy. // // For more information, see the following resources: // @@ -389,11 +394,11 @@ func (c *STS) AssumeRoleWithSAMLRequest(input *AssumeRoleWithSAMLInput) (req *re // // * ErrCodePackedPolicyTooLargeException "PackedPolicyTooLarge" // The request was rejected because the total packed size of the session policies -// and session tags combined was too large. An AWS conversion compresses the -// session policy document, session policy ARNs, and session tags into a packed -// binary format that has a separate limit. The error message indicates by percentage -// how close the policies and tags are to the upper size limit. For more information, -// see Passing Session Tags in STS (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) +// and session tags combined was too large. An Amazon Web Services conversion +// compresses the session policy document, session policy ARNs, and session +// tags into a packed binary format that has a separate limit. The error message +// indicates by percentage how close the policies and tags are to the upper +// size limit. For more information, see Passing Session Tags in STS (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) // in the IAM User Guide. // // You could receive this error even though you meet other defined session policy @@ -409,8 +414,9 @@ func (c *STS) AssumeRoleWithSAMLRequest(input *AssumeRoleWithSAMLInput) (req *re // can also mean that the claim has expired or has been explicitly revoked. // // * ErrCodeInvalidIdentityTokenException "InvalidIdentityToken" -// The web identity token that was passed could not be validated by AWS. Get -// a new identity token from the identity provider and then retry the request. +// The web identity token that was passed could not be validated by Amazon Web +// Services. Get a new identity token from the identity provider and then retry +// the request. // // * ErrCodeExpiredTokenException "ExpiredTokenException" // The web identity token that was passed is expired or is not valid. Get a @@ -420,7 +426,8 @@ func (c *STS) AssumeRoleWithSAMLRequest(input *AssumeRoleWithSAMLInput) (req *re // STS is not activated in the requested region for the account that is being // asked to generate credentials. The account administrator must use the IAM // console to activate STS in that region. For more information, see Activating -// and Deactivating AWS STS in an AWS Region (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) +// and Deactivating Amazon Web Services STS in an Amazon Web Services Region +// (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) // in the IAM User Guide. // // See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/AssumeRoleWithSAML @@ -496,30 +503,33 @@ func (c *STS) AssumeRoleWithWebIdentityRequest(input *AssumeRoleWithWebIdentityI // Connect-compatible identity provider. // // For mobile applications, we recommend that you use Amazon Cognito. You can -// use Amazon Cognito with the AWS SDK for iOS Developer Guide (http://aws.amazon.com/sdkforios/) -// and the AWS SDK for Android Developer Guide (http://aws.amazon.com/sdkforandroid/) -// to uniquely identify a user. You can also supply the user with a consistent -// identity throughout the lifetime of an application. +// use Amazon Cognito with the Amazon Web Services SDK for iOS Developer Guide +// (http://aws.amazon.com/sdkforios/) and the Amazon Web Services SDK for Android +// Developer Guide (http://aws.amazon.com/sdkforandroid/) to uniquely identify +// a user. You can also supply the user with a consistent identity throughout +// the lifetime of an application. // // To learn more about Amazon Cognito, see Amazon Cognito Overview (https://docs.aws.amazon.com/mobile/sdkforandroid/developerguide/cognito-auth.html#d0e840) -// in AWS SDK for Android Developer Guide and Amazon Cognito Overview (https://docs.aws.amazon.com/mobile/sdkforios/developerguide/cognito-auth.html#d0e664) -// in the AWS SDK for iOS Developer Guide. -// -// Calling AssumeRoleWithWebIdentity does not require the use of AWS security -// credentials. Therefore, you can distribute an application (for example, on -// mobile devices) that requests temporary security credentials without including -// long-term AWS credentials in the application. You also don't need to deploy -// server-based proxy services that use long-term AWS credentials. Instead, -// the identity of the caller is validated by using a token from the web identity -// provider. For a comparison of AssumeRoleWithWebIdentity with the other API -// operations that produce temporary credentials, see Requesting Temporary Security -// Credentials (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html) -// and Comparing the AWS STS API operations (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#stsapi_comparison) +// in Amazon Web Services SDK for Android Developer Guide and Amazon Cognito +// Overview (https://docs.aws.amazon.com/mobile/sdkforios/developerguide/cognito-auth.html#d0e664) +// in the Amazon Web Services SDK for iOS Developer Guide. +// +// Calling AssumeRoleWithWebIdentity does not require the use of Amazon Web +// Services security credentials. Therefore, you can distribute an application +// (for example, on mobile devices) that requests temporary security credentials +// without including long-term Amazon Web Services credentials in the application. +// You also don't need to deploy server-based proxy services that use long-term +// Amazon Web Services credentials. Instead, the identity of the caller is validated +// by using a token from the web identity provider. For a comparison of AssumeRoleWithWebIdentity +// with the other API operations that produce temporary credentials, see Requesting +// Temporary Security Credentials (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html) +// and Comparing the STS API operations (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#stsapi_comparison) // in the IAM User Guide. // // The temporary security credentials returned by this API consist of an access // key ID, a secret access key, and a security token. Applications can use these -// temporary security credentials to sign calls to AWS service API operations. +// temporary security credentials to sign calls to Amazon Web Services service +// API operations. // // Session Duration // @@ -539,8 +549,9 @@ func (c *STS) AssumeRoleWithWebIdentityRequest(input *AssumeRoleWithWebIdentityI // Permissions // // The temporary security credentials created by AssumeRoleWithWebIdentity can -// be used to make API calls to any AWS service with the following exception: -// you cannot call the STS GetFederationToken or GetSessionToken API operations. +// be used to make API calls to any Amazon Web Services service with the following +// exception: you cannot call the STS GetFederationToken or GetSessionToken +// API operations. // // (Optional) You can pass inline or managed session policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) // to this operation. You can pass a single JSON policy document to use as an @@ -550,10 +561,10 @@ func (c *STS) AssumeRoleWithWebIdentityRequest(input *AssumeRoleWithWebIdentityI // to this operation returns new temporary credentials. The resulting session's // permissions are the intersection of the role's identity-based policy and // the session policies. You can use the role's temporary credentials in subsequent -// AWS API calls to access resources in the account that owns the role. You -// cannot use session policies to grant more permissions than those allowed -// by the identity-based policy of the role that is being assumed. For more -// information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) +// Amazon Web Services API calls to access resources in the account that owns +// the role. You cannot use session policies to grant more permissions than +// those allowed by the identity-based policy of the role that is being assumed. +// For more information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) // in the IAM User Guide. // // Tags @@ -569,11 +580,11 @@ func (c *STS) AssumeRoleWithWebIdentityRequest(input *AssumeRoleWithWebIdentityI // and additional limits, see IAM and STS Character Limits (https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-limits.html#reference_iam-limits-entity-length) // in the IAM User Guide. // -// An AWS conversion compresses the passed session policies and session tags -// into a packed binary format that has a separate limit. Your request can fail -// for this limit even if your plaintext meets the other requirements. The PackedPolicySize -// response element indicates by percentage how close the policies and tags -// for your request are to the upper size limit. +// An Amazon Web Services conversion compresses the passed session policies +// and session tags into a packed binary format that has a separate limit. Your +// request can fail for this limit even if your plaintext meets the other requirements. +// The PackedPolicySize response element indicates by percentage how close the +// policies and tags for your request are to the upper size limit. // // You can pass a session tag with the same key as a tag that is attached to // the role. When you do, the session tag overrides the role tag with the same @@ -598,7 +609,7 @@ func (c *STS) AssumeRoleWithWebIdentityRequest(input *AssumeRoleWithWebIdentityI // the identity provider that is associated with the identity token. In other // words, the identity provider must be specified in the role's trust policy. // -// Calling AssumeRoleWithWebIdentity can result in an entry in your AWS CloudTrail +// Calling AssumeRoleWithWebIdentity can result in an entry in your CloudTrail // logs. The entry includes the Subject (http://openid.net/specs/openid-connect-core-1_0.html#Claims) // of the provided web identity token. We recommend that you avoid using any // personally identifiable information (PII) in this field. For example, you @@ -614,10 +625,10 @@ func (c *STS) AssumeRoleWithWebIdentityRequest(input *AssumeRoleWithWebIdentityI // * Web Identity Federation Playground (https://aws.amazon.com/blogs/aws/the-aws-web-identity-federation-playground/). // Walk through the process of authenticating through Login with Amazon, // Facebook, or Google, getting temporary security credentials, and then -// using those credentials to make a request to AWS. +// using those credentials to make a request to Amazon Web Services. // -// * AWS SDK for iOS Developer Guide (http://aws.amazon.com/sdkforios/) and -// AWS SDK for Android Developer Guide (http://aws.amazon.com/sdkforandroid/). +// * Amazon Web Services SDK for iOS Developer Guide (http://aws.amazon.com/sdkforios/) +// and Amazon Web Services SDK for Android Developer Guide (http://aws.amazon.com/sdkforandroid/). // These toolkits contain sample apps that show how to invoke the identity // providers. The toolkits then show how to use the information from these // providers to get and use temporary security credentials. @@ -641,11 +652,11 @@ func (c *STS) AssumeRoleWithWebIdentityRequest(input *AssumeRoleWithWebIdentityI // // * ErrCodePackedPolicyTooLargeException "PackedPolicyTooLarge" // The request was rejected because the total packed size of the session policies -// and session tags combined was too large. An AWS conversion compresses the -// session policy document, session policy ARNs, and session tags into a packed -// binary format that has a separate limit. The error message indicates by percentage -// how close the policies and tags are to the upper size limit. For more information, -// see Passing Session Tags in STS (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) +// and session tags combined was too large. An Amazon Web Services conversion +// compresses the session policy document, session policy ARNs, and session +// tags into a packed binary format that has a separate limit. The error message +// indicates by percentage how close the policies and tags are to the upper +// size limit. For more information, see Passing Session Tags in STS (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) // in the IAM User Guide. // // You could receive this error even though you meet other defined session policy @@ -668,8 +679,9 @@ func (c *STS) AssumeRoleWithWebIdentityRequest(input *AssumeRoleWithWebIdentityI // error persists, the identity provider might be down or not responding. // // * ErrCodeInvalidIdentityTokenException "InvalidIdentityToken" -// The web identity token that was passed could not be validated by AWS. Get -// a new identity token from the identity provider and then retry the request. +// The web identity token that was passed could not be validated by Amazon Web +// Services. Get a new identity token from the identity provider and then retry +// the request. // // * ErrCodeExpiredTokenException "ExpiredTokenException" // The web identity token that was passed is expired or is not valid. Get a @@ -679,7 +691,8 @@ func (c *STS) AssumeRoleWithWebIdentityRequest(input *AssumeRoleWithWebIdentityI // STS is not activated in the requested region for the account that is being // asked to generate credentials. The account administrator must use the IAM // console to activate STS in that region. For more information, see Activating -// and Deactivating AWS STS in an AWS Region (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) +// and Deactivating Amazon Web Services STS in an Amazon Web Services Region +// (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) // in the IAM User Guide. // // See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/AssumeRoleWithWebIdentity @@ -749,16 +762,18 @@ func (c *STS) DecodeAuthorizationMessageRequest(input *DecodeAuthorizationMessag // DecodeAuthorizationMessage API operation for AWS Security Token Service. // // Decodes additional information about the authorization status of a request -// from an encoded message returned in response to an AWS request. +// from an encoded message returned in response to an Amazon Web Services request. // // For example, if a user is not authorized to perform an operation that he // or she has requested, the request returns a Client.UnauthorizedOperation -// response (an HTTP 403 response). Some AWS operations additionally return -// an encoded message that can provide details about this authorization failure. +// response (an HTTP 403 response). Some Amazon Web Services operations additionally +// return an encoded message that can provide details about this authorization +// failure. // -// Only certain AWS operations return an encoded authorization message. The -// documentation for an individual operation indicates whether that operation -// returns an encoded message in addition to returning an HTTP code. +// Only certain Amazon Web Services operations return an encoded authorization +// message. The documentation for an individual operation indicates whether +// that operation returns an encoded message in addition to returning an HTTP +// code. // // The message is encoded because the details of the authorization status can // constitute privileged information that the user who requested the operation @@ -869,12 +884,12 @@ func (c *STS) GetAccessKeyInfoRequest(input *GetAccessKeyInfoInput) (req *reques // in the IAM User Guide. // // When you pass an access key ID to this operation, it returns the ID of the -// AWS account to which the keys belong. Access key IDs beginning with AKIA -// are long-term credentials for an IAM user or the AWS account root user. Access -// key IDs beginning with ASIA are temporary credentials that are created using -// STS operations. If the account in the response belongs to you, you can sign -// in as the root user and review your root user access keys. Then, you can -// pull a credentials report (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_getting-report.html) +// Amazon Web Services account to which the keys belong. Access key IDs beginning +// with AKIA are long-term credentials for an IAM user or the Amazon Web Services +// account root user. Access key IDs beginning with ASIA are temporary credentials +// that are created using STS operations. If the account in the response belongs +// to you, you can sign in as the root user and review your root user access +// keys. Then, you can pull a credentials report (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_getting-report.html) // to learn which IAM user owns the keys. To learn who requested the temporary // credentials for an ASIA access key, view the STS events in your CloudTrail // logs (https://docs.aws.amazon.com/IAM/latest/UserGuide/cloudtrail-integration.html) @@ -1050,7 +1065,7 @@ func (c *STS) GetFederationTokenRequest(input *GetFederationTokenInput) (req *re // For a comparison of GetFederationToken with the other API operations that // produce temporary credentials, see Requesting Temporary Security Credentials // (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html) -// and Comparing the AWS STS API operations (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#stsapi_comparison) +// and Comparing the STS API operations (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#stsapi_comparison) // in the IAM User Guide. // // You can create a mobile-based or browser-based app that can authenticate @@ -1062,11 +1077,11 @@ func (c *STS) GetFederationTokenRequest(input *GetFederationTokenInput) (req *re // in the IAM User Guide. // // You can also call GetFederationToken using the security credentials of an -// AWS account root user, but we do not recommend it. Instead, we recommend -// that you create an IAM user for the purpose of the proxy application. Then -// attach a policy to the IAM user that limits federated users to only the actions -// and resources that they need to access. For more information, see IAM Best -// Practices (https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html) +// Amazon Web Services account root user, but we do not recommend it. Instead, +// we recommend that you create an IAM user for the purpose of the proxy application. +// Then attach a policy to the IAM user that limits federated users to only +// the actions and resources that they need to access. For more information, +// see IAM Best Practices (https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html) // in the IAM User Guide. // // Session duration @@ -1074,15 +1089,16 @@ func (c *STS) GetFederationTokenRequest(input *GetFederationTokenInput) (req *re // The temporary credentials are valid for the specified duration, from 900 // seconds (15 minutes) up to a maximum of 129,600 seconds (36 hours). The default // session duration is 43,200 seconds (12 hours). Temporary credentials that -// are obtained by using AWS account root user credentials have a maximum duration -// of 3,600 seconds (1 hour). +// are obtained by using Amazon Web Services account root user credentials have +// a maximum duration of 3,600 seconds (1 hour). // // Permissions // // You can use the temporary credentials created by GetFederationToken in any -// AWS service except the following: +// Amazon Web Services service except the following: // -// * You cannot call any IAM operations using the AWS CLI or the AWS API. +// * You cannot call any IAM operations using the CLI or the Amazon Web Services +// API. // // * You cannot call any STS operations except GetCallerIdentity. // @@ -1126,11 +1142,11 @@ func (c *STS) GetFederationTokenRequest(input *GetFederationTokenInput) (req *re // in the IAM User Guide. // // You can also call GetFederationToken using the security credentials of an -// AWS account root user, but we do not recommend it. Instead, we recommend -// that you create an IAM user for the purpose of the proxy application. Then -// attach a policy to the IAM user that limits federated users to only the actions -// and resources that they need to access. For more information, see IAM Best -// Practices (https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html) +// Amazon Web Services account root user, but we do not recommend it. Instead, +// we recommend that you create an IAM user for the purpose of the proxy application. +// Then attach a policy to the IAM user that limits federated users to only +// the actions and resources that they need to access. For more information, +// see IAM Best Practices (https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html) // in the IAM User Guide. // // Session duration @@ -1138,15 +1154,16 @@ func (c *STS) GetFederationTokenRequest(input *GetFederationTokenInput) (req *re // The temporary credentials are valid for the specified duration, from 900 // seconds (15 minutes) up to a maximum of 129,600 seconds (36 hours). The default // session duration is 43,200 seconds (12 hours). Temporary credentials that -// are obtained by using AWS account root user credentials have a maximum duration -// of 3,600 seconds (1 hour). +// are obtained by using Amazon Web Services account root user credentials have +// a maximum duration of 3,600 seconds (1 hour). // // Permissions // // You can use the temporary credentials created by GetFederationToken in any -// AWS service except the following: +// Amazon Web Services service except the following: // -// * You cannot call any IAM operations using the AWS CLI or the AWS API. +// * You cannot call any IAM operations using the CLI or the Amazon Web Services +// API. // // * You cannot call any STS operations except GetCallerIdentity. // @@ -1208,11 +1225,11 @@ func (c *STS) GetFederationTokenRequest(input *GetFederationTokenInput) (req *re // // * ErrCodePackedPolicyTooLargeException "PackedPolicyTooLarge" // The request was rejected because the total packed size of the session policies -// and session tags combined was too large. An AWS conversion compresses the -// session policy document, session policy ARNs, and session tags into a packed -// binary format that has a separate limit. The error message indicates by percentage -// how close the policies and tags are to the upper size limit. For more information, -// see Passing Session Tags in STS (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) +// and session tags combined was too large. An Amazon Web Services conversion +// compresses the session policy document, session policy ARNs, and session +// tags into a packed binary format that has a separate limit. The error message +// indicates by percentage how close the policies and tags are to the upper +// size limit. For more information, see Passing Session Tags in STS (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) // in the IAM User Guide. // // You could receive this error even though you meet other defined session policy @@ -1224,7 +1241,8 @@ func (c *STS) GetFederationTokenRequest(input *GetFederationTokenInput) (req *re // STS is not activated in the requested region for the account that is being // asked to generate credentials. The account administrator must use the IAM // console to activate STS in that region. For more information, see Activating -// and Deactivating AWS STS in an AWS Region (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) +// and Deactivating Amazon Web Services STS in an Amazon Web Services Region +// (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) // in the IAM User Guide. // // See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/GetFederationToken @@ -1293,51 +1311,53 @@ func (c *STS) GetSessionTokenRequest(input *GetSessionTokenInput) (req *request. // GetSessionToken API operation for AWS Security Token Service. // -// Returns a set of temporary credentials for an AWS account or IAM user. The -// credentials consist of an access key ID, a secret access key, and a security -// token. Typically, you use GetSessionToken if you want to use MFA to protect -// programmatic calls to specific AWS API operations like Amazon EC2 StopInstances. -// MFA-enabled IAM users would need to call GetSessionToken and submit an MFA -// code that is associated with their MFA device. Using the temporary security -// credentials that are returned from the call, IAM users can then make programmatic -// calls to API operations that require MFA authentication. If you do not supply -// a correct MFA code, then the API returns an access denied error. For a comparison -// of GetSessionToken with the other API operations that produce temporary credentials, -// see Requesting Temporary Security Credentials (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html) -// and Comparing the AWS STS API operations (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#stsapi_comparison) +// Returns a set of temporary credentials for an Amazon Web Services account +// or IAM user. The credentials consist of an access key ID, a secret access +// key, and a security token. Typically, you use GetSessionToken if you want +// to use MFA to protect programmatic calls to specific Amazon Web Services +// API operations like Amazon EC2 StopInstances. MFA-enabled IAM users would +// need to call GetSessionToken and submit an MFA code that is associated with +// their MFA device. Using the temporary security credentials that are returned +// from the call, IAM users can then make programmatic calls to API operations +// that require MFA authentication. If you do not supply a correct MFA code, +// then the API returns an access denied error. For a comparison of GetSessionToken +// with the other API operations that produce temporary credentials, see Requesting +// Temporary Security Credentials (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html) +// and Comparing the STS API operations (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#stsapi_comparison) // in the IAM User Guide. // // Session Duration // -// The GetSessionToken operation must be called by using the long-term AWS security -// credentials of the AWS account root user or an IAM user. Credentials that -// are created by IAM users are valid for the duration that you specify. This -// duration can range from 900 seconds (15 minutes) up to a maximum of 129,600 -// seconds (36 hours), with a default of 43,200 seconds (12 hours). Credentials -// based on account credentials can range from 900 seconds (15 minutes) up to -// 3,600 seconds (1 hour), with a default of 1 hour. +// The GetSessionToken operation must be called by using the long-term Amazon +// Web Services security credentials of the Amazon Web Services account root +// user or an IAM user. Credentials that are created by IAM users are valid +// for the duration that you specify. This duration can range from 900 seconds +// (15 minutes) up to a maximum of 129,600 seconds (36 hours), with a default +// of 43,200 seconds (12 hours). Credentials based on account credentials can +// range from 900 seconds (15 minutes) up to 3,600 seconds (1 hour), with a +// default of 1 hour. // // Permissions // // The temporary security credentials created by GetSessionToken can be used -// to make API calls to any AWS service with the following exceptions: +// to make API calls to any Amazon Web Services service with the following exceptions: // // * You cannot call any IAM API operations unless MFA authentication information // is included in the request. // // * You cannot call any STS API except AssumeRole or GetCallerIdentity. // -// We recommend that you do not call GetSessionToken with AWS account root user -// credentials. Instead, follow our best practices (https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#create-iam-users) +// We recommend that you do not call GetSessionToken with Amazon Web Services +// account root user credentials. Instead, follow our best practices (https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#create-iam-users) // by creating one or more IAM users, giving them the necessary permissions, -// and using IAM users for everyday interaction with AWS. +// and using IAM users for everyday interaction with Amazon Web Services. // // The credentials that are returned by GetSessionToken are based on permissions // associated with the user whose credentials were used to call the operation. -// If GetSessionToken is called using AWS account root user credentials, the -// temporary credentials have root user permissions. Similarly, if GetSessionToken -// is called using the credentials of an IAM user, the temporary credentials -// have the same permissions as the IAM user. +// If GetSessionToken is called using Amazon Web Services account root user +// credentials, the temporary credentials have root user permissions. Similarly, +// if GetSessionToken is called using the credentials of an IAM user, the temporary +// credentials have the same permissions as the IAM user. // // For more information about using GetSessionToken to create temporary credentials, // go to Temporary Credentials for Users in Untrusted Environments (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#api_getsessiontoken) @@ -1355,7 +1375,8 @@ func (c *STS) GetSessionTokenRequest(input *GetSessionTokenInput) (req *request. // STS is not activated in the requested region for the account that is being // asked to generate credentials. The account administrator must use the IAM // console to activate STS in that region. For more information, see Activating -// and Deactivating AWS STS in an AWS Region (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) +// and Deactivating Amazon Web Services STS in an Amazon Web Services Region +// (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) // in the IAM User Guide. // // See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/GetSessionToken @@ -1401,7 +1422,7 @@ type AssumeRoleInput struct { // to the federation endpoint for a console sign-in token takes a SessionDuration // parameter that specifies the maximum length of the console session. For more // information, see Creating a URL that Enables Federated Users to Access the - // AWS Management Console (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_enable-console-custom-url.html) + // Management Console (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_enable-console-custom-url.html) // in the IAM User Guide. DurationSeconds *int64 `min:"900" type:"integer"` @@ -1413,8 +1434,8 @@ type AssumeRoleInput struct { // of the trusting account might send an external ID to the administrator of // the trusted account. That way, only someone with the ID can assume the role, // rather than everyone in the account. For more information about the external - // ID, see How to Use an External ID When Granting Access to Your AWS Resources - // to a Third Party (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html) + // ID, see How to Use an External ID When Granting Access to Your Amazon Web + // Services Resources to a Third Party (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html) // in the IAM User Guide. // // The regex used to validate this parameter is a string of characters consisting @@ -1427,10 +1448,11 @@ type AssumeRoleInput struct { // This parameter is optional. Passing policies to this operation returns new // temporary credentials. The resulting session's permissions are the intersection // of the role's identity-based policy and the session policies. You can use - // the role's temporary credentials in subsequent AWS API calls to access resources - // in the account that owns the role. You cannot use session policies to grant - // more permissions than those allowed by the identity-based policy of the role - // that is being assumed. For more information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) + // the role's temporary credentials in subsequent Amazon Web Services API calls + // to access resources in the account that owns the role. You cannot use session + // policies to grant more permissions than those allowed by the identity-based + // policy of the role that is being assumed. For more information, see Session + // Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) // in the IAM User Guide. // // The plaintext that you use for both inline and managed session policies can't @@ -1439,11 +1461,11 @@ type AssumeRoleInput struct { // \u00FF). It can also include the tab (\u0009), linefeed (\u000A), and carriage // return (\u000D) characters. // - // An AWS conversion compresses the passed session policies and session tags - // into a packed binary format that has a separate limit. Your request can fail - // for this limit even if your plaintext meets the other requirements. The PackedPolicySize - // response element indicates by percentage how close the policies and tags - // for your request are to the upper size limit. + // An Amazon Web Services conversion compresses the passed session policies + // and session tags into a packed binary format that has a separate limit. Your + // request can fail for this limit even if your plaintext meets the other requirements. + // The PackedPolicySize response element indicates by percentage how close the + // policies and tags for your request are to the upper size limit. Policy *string `min:"1" type:"string"` // The Amazon Resource Names (ARNs) of the IAM managed policies that you want @@ -1453,22 +1475,22 @@ type AssumeRoleInput struct { // This parameter is optional. You can provide up to 10 managed policy ARNs. // However, the plaintext that you use for both inline and managed session policies // can't exceed 2,048 characters. For more information about ARNs, see Amazon - // Resource Names (ARNs) and AWS Service Namespaces (https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) - // in the AWS General Reference. + // Resource Names (ARNs) and Amazon Web Services Service Namespaces (https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) + // in the Amazon Web Services General Reference. // - // An AWS conversion compresses the passed session policies and session tags - // into a packed binary format that has a separate limit. Your request can fail - // for this limit even if your plaintext meets the other requirements. The PackedPolicySize - // response element indicates by percentage how close the policies and tags - // for your request are to the upper size limit. + // An Amazon Web Services conversion compresses the passed session policies + // and session tags into a packed binary format that has a separate limit. Your + // request can fail for this limit even if your plaintext meets the other requirements. + // The PackedPolicySize response element indicates by percentage how close the + // policies and tags for your request are to the upper size limit. // // Passing policies to this operation returns new temporary credentials. The // resulting session's permissions are the intersection of the role's identity-based // policy and the session policies. You can use the role's temporary credentials - // in subsequent AWS API calls to access resources in the account that owns - // the role. You cannot use session policies to grant more permissions than - // those allowed by the identity-based policy of the role that is being assumed. - // For more information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) + // in subsequent Amazon Web Services API calls to access resources in the account + // that owns the role. You cannot use session policies to grant more permissions + // than those allowed by the identity-based policy of the role that is being + // assumed. For more information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) // in the IAM User Guide. PolicyArns []*PolicyDescriptorType `type:"list"` @@ -1485,7 +1507,7 @@ type AssumeRoleInput struct { // account that owns the role. The role session name is also used in the ARN // of the assumed role principal. This means that subsequent cross-account API // requests that use the temporary security credentials will expose the role - // session name to the external account in their AWS CloudTrail logs. + // session name to the external account in their CloudTrail logs. // // The regex used to validate this parameter is a string of characters consisting // of upper- and lower-case alphanumeric characters with no spaces. You can @@ -1510,23 +1532,23 @@ type AssumeRoleInput struct { // // You can require users to specify a source identity when they assume a role. // You do this by using the sts:SourceIdentity condition key in a role trust - // policy. You can use source identity information in AWS CloudTrail logs to - // determine who took actions with a role. You can use the aws:SourceIdentity - // condition key to further control access to AWS resources based on the value - // of source identity. For more information about using source identity, see - // Monitor and control actions taken with assumed roles (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_control-access_monitor.html) + // policy. You can use source identity information in CloudTrail logs to determine + // who took actions with a role. You can use the aws:SourceIdentity condition + // key to further control access to Amazon Web Services resources based on the + // value of source identity. For more information about using source identity, + // see Monitor and control actions taken with assumed roles (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_control-access_monitor.html) // in the IAM User Guide. // // The regex used to validate this parameter is a string of characters consisting // of upper- and lower-case alphanumeric characters with no spaces. You can // also include underscores or any of the following characters: =,.@-. You cannot - // use a value that begins with the text aws:. This prefix is reserved for AWS - // internal use. + // use a value that begins with the text aws:. This prefix is reserved for Amazon + // Web Services internal use. SourceIdentity *string `min:"2" type:"string"` // A list of session tags that you want to pass. Each session tag consists of // a key name and an associated value. For more information about session tags, - // see Tagging AWS STS Sessions (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) + // see Tagging STS Sessions (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) // in the IAM User Guide. // // This parameter is optional. You can pass up to 50 session tags. The plaintext @@ -1535,11 +1557,11 @@ type AssumeRoleInput struct { // Limits (https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-limits.html#reference_iam-limits-entity-length) // in the IAM User Guide. // - // An AWS conversion compresses the passed session policies and session tags - // into a packed binary format that has a separate limit. Your request can fail - // for this limit even if your plaintext meets the other requirements. The PackedPolicySize - // response element indicates by percentage how close the policies and tags - // for your request are to the upper size limit. + // An Amazon Web Services conversion compresses the passed session policies + // and session tags into a packed binary format that has a separate limit. Your + // request can fail for this limit even if your plaintext meets the other requirements. + // The PackedPolicySize response element indicates by percentage how close the + // policies and tags for your request are to the upper size limit. // // You can pass a session tag with the same key as a tag that is already attached // to the role. When you do, session tags override a role tag with the same @@ -1554,7 +1576,7 @@ type AssumeRoleInput struct { // Additionally, if you used temporary credentials to perform this operation, // the new session inherits any transitive session tags from the calling session. // If you pass a session tag with the same key as an inherited tag, the operation - // fails. To view the inherited tags for a session, see the AWS CloudTrail logs. + // fails. To view the inherited tags for a session, see the CloudTrail logs. // For more information, see Viewing Session Tags in CloudTrail (https://docs.aws.amazon.com/IAM/latest/UserGuide/session-tags.html#id_session-tags_ctlogs) // in the IAM User Guide. Tags []*Tag `type:"list"` @@ -1720,7 +1742,8 @@ func (s *AssumeRoleInput) SetTransitiveTagKeys(v []*string) *AssumeRoleInput { } // Contains the response to a successful AssumeRole request, including temporary -// AWS credentials that can be used to make AWS requests. +// Amazon Web Services credentials that can be used to make Amazon Web Services +// requests. type AssumeRoleOutput struct { _ struct{} `type:"structure"` @@ -1749,11 +1772,11 @@ type AssumeRoleOutput struct { // // You can require users to specify a source identity when they assume a role. // You do this by using the sts:SourceIdentity condition key in a role trust - // policy. You can use source identity information in AWS CloudTrail logs to - // determine who took actions with a role. You can use the aws:SourceIdentity - // condition key to further control access to AWS resources based on the value - // of source identity. For more information about using source identity, see - // Monitor and control actions taken with assumed roles (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_control-access_monitor.html) + // policy. You can use source identity information in CloudTrail logs to determine + // who took actions with a role. You can use the aws:SourceIdentity condition + // key to further control access to Amazon Web Services resources based on the + // value of source identity. For more information about using source identity, + // see Monitor and control actions taken with assumed roles (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_control-access_monitor.html) // in the IAM User Guide. // // The regex used to validate this parameter is a string of characters consisting @@ -1819,7 +1842,7 @@ type AssumeRoleWithSAMLInput struct { // to the federation endpoint for a console sign-in token takes a SessionDuration // parameter that specifies the maximum length of the console session. For more // information, see Creating a URL that Enables Federated Users to Access the - // AWS Management Console (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_enable-console-custom-url.html) + // Management Console (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_enable-console-custom-url.html) // in the IAM User Guide. DurationSeconds *int64 `min:"900" type:"integer"` @@ -1828,10 +1851,11 @@ type AssumeRoleWithSAMLInput struct { // This parameter is optional. Passing policies to this operation returns new // temporary credentials. The resulting session's permissions are the intersection // of the role's identity-based policy and the session policies. You can use - // the role's temporary credentials in subsequent AWS API calls to access resources - // in the account that owns the role. You cannot use session policies to grant - // more permissions than those allowed by the identity-based policy of the role - // that is being assumed. For more information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) + // the role's temporary credentials in subsequent Amazon Web Services API calls + // to access resources in the account that owns the role. You cannot use session + // policies to grant more permissions than those allowed by the identity-based + // policy of the role that is being assumed. For more information, see Session + // Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) // in the IAM User Guide. // // The plaintext that you use for both inline and managed session policies can't @@ -1840,11 +1864,11 @@ type AssumeRoleWithSAMLInput struct { // \u00FF). It can also include the tab (\u0009), linefeed (\u000A), and carriage // return (\u000D) characters. // - // An AWS conversion compresses the passed session policies and session tags - // into a packed binary format that has a separate limit. Your request can fail - // for this limit even if your plaintext meets the other requirements. The PackedPolicySize - // response element indicates by percentage how close the policies and tags - // for your request are to the upper size limit. + // An Amazon Web Services conversion compresses the passed session policies + // and session tags into a packed binary format that has a separate limit. Your + // request can fail for this limit even if your plaintext meets the other requirements. + // The PackedPolicySize response element indicates by percentage how close the + // policies and tags for your request are to the upper size limit. Policy *string `min:"1" type:"string"` // The Amazon Resource Names (ARNs) of the IAM managed policies that you want @@ -1854,22 +1878,22 @@ type AssumeRoleWithSAMLInput struct { // This parameter is optional. You can provide up to 10 managed policy ARNs. // However, the plaintext that you use for both inline and managed session policies // can't exceed 2,048 characters. For more information about ARNs, see Amazon - // Resource Names (ARNs) and AWS Service Namespaces (https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) - // in the AWS General Reference. + // Resource Names (ARNs) and Amazon Web Services Service Namespaces (https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) + // in the Amazon Web Services General Reference. // - // An AWS conversion compresses the passed session policies and session tags - // into a packed binary format that has a separate limit. Your request can fail - // for this limit even if your plaintext meets the other requirements. The PackedPolicySize - // response element indicates by percentage how close the policies and tags - // for your request are to the upper size limit. + // An Amazon Web Services conversion compresses the passed session policies + // and session tags into a packed binary format that has a separate limit. Your + // request can fail for this limit even if your plaintext meets the other requirements. + // The PackedPolicySize response element indicates by percentage how close the + // policies and tags for your request are to the upper size limit. // // Passing policies to this operation returns new temporary credentials. The // resulting session's permissions are the intersection of the role's identity-based // policy and the session policies. You can use the role's temporary credentials - // in subsequent AWS API calls to access resources in the account that owns - // the role. You cannot use session policies to grant more permissions than - // those allowed by the identity-based policy of the role that is being assumed. - // For more information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) + // in subsequent Amazon Web Services API calls to access resources in the account + // that owns the role. You cannot use session policies to grant more permissions + // than those allowed by the identity-based policy of the role that is being + // assumed. For more information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) // in the IAM User Guide. PolicyArns []*PolicyDescriptorType `type:"list"` @@ -1984,7 +2008,8 @@ func (s *AssumeRoleWithSAMLInput) SetSAMLAssertion(v string) *AssumeRoleWithSAML } // Contains the response to a successful AssumeRoleWithSAML request, including -// temporary AWS credentials that can be used to make AWS requests. +// temporary Amazon Web Services credentials that can be used to make Amazon +// Web Services requests. type AssumeRoleWithSAMLOutput struct { _ struct{} `type:"structure"` @@ -2010,7 +2035,7 @@ type AssumeRoleWithSAMLOutput struct { // // * The Issuer response value. // - // * The AWS account ID. + // * The Amazon Web Services account ID. // // * The friendly name (the last part of the ARN) of the SAML provider in // IAM. @@ -2148,7 +2173,7 @@ type AssumeRoleWithWebIdentityInput struct { // to the federation endpoint for a console sign-in token takes a SessionDuration // parameter that specifies the maximum length of the console session. For more // information, see Creating a URL that Enables Federated Users to Access the - // AWS Management Console (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_enable-console-custom-url.html) + // Management Console (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_enable-console-custom-url.html) // in the IAM User Guide. DurationSeconds *int64 `min:"900" type:"integer"` @@ -2157,10 +2182,11 @@ type AssumeRoleWithWebIdentityInput struct { // This parameter is optional. Passing policies to this operation returns new // temporary credentials. The resulting session's permissions are the intersection // of the role's identity-based policy and the session policies. You can use - // the role's temporary credentials in subsequent AWS API calls to access resources - // in the account that owns the role. You cannot use session policies to grant - // more permissions than those allowed by the identity-based policy of the role - // that is being assumed. For more information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) + // the role's temporary credentials in subsequent Amazon Web Services API calls + // to access resources in the account that owns the role. You cannot use session + // policies to grant more permissions than those allowed by the identity-based + // policy of the role that is being assumed. For more information, see Session + // Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) // in the IAM User Guide. // // The plaintext that you use for both inline and managed session policies can't @@ -2169,11 +2195,11 @@ type AssumeRoleWithWebIdentityInput struct { // \u00FF). It can also include the tab (\u0009), linefeed (\u000A), and carriage // return (\u000D) characters. // - // An AWS conversion compresses the passed session policies and session tags - // into a packed binary format that has a separate limit. Your request can fail - // for this limit even if your plaintext meets the other requirements. The PackedPolicySize - // response element indicates by percentage how close the policies and tags - // for your request are to the upper size limit. + // An Amazon Web Services conversion compresses the passed session policies + // and session tags into a packed binary format that has a separate limit. Your + // request can fail for this limit even if your plaintext meets the other requirements. + // The PackedPolicySize response element indicates by percentage how close the + // policies and tags for your request are to the upper size limit. Policy *string `min:"1" type:"string"` // The Amazon Resource Names (ARNs) of the IAM managed policies that you want @@ -2183,22 +2209,22 @@ type AssumeRoleWithWebIdentityInput struct { // This parameter is optional. You can provide up to 10 managed policy ARNs. // However, the plaintext that you use for both inline and managed session policies // can't exceed 2,048 characters. For more information about ARNs, see Amazon - // Resource Names (ARNs) and AWS Service Namespaces (https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) - // in the AWS General Reference. + // Resource Names (ARNs) and Amazon Web Services Service Namespaces (https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) + // in the Amazon Web Services General Reference. // - // An AWS conversion compresses the passed session policies and session tags - // into a packed binary format that has a separate limit. Your request can fail - // for this limit even if your plaintext meets the other requirements. The PackedPolicySize - // response element indicates by percentage how close the policies and tags - // for your request are to the upper size limit. + // An Amazon Web Services conversion compresses the passed session policies + // and session tags into a packed binary format that has a separate limit. Your + // request can fail for this limit even if your plaintext meets the other requirements. + // The PackedPolicySize response element indicates by percentage how close the + // policies and tags for your request are to the upper size limit. // // Passing policies to this operation returns new temporary credentials. The // resulting session's permissions are the intersection of the role's identity-based // policy and the session policies. You can use the role's temporary credentials - // in subsequent AWS API calls to access resources in the account that owns - // the role. You cannot use session policies to grant more permissions than - // those allowed by the identity-based policy of the role that is being assumed. - // For more information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) + // in subsequent Amazon Web Services API calls to access resources in the account + // that owns the role. You cannot use session policies to grant more permissions + // than those allowed by the identity-based policy of the role that is being + // assumed. For more information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) // in the IAM User Guide. PolicyArns []*PolicyDescriptorType `type:"list"` @@ -2338,7 +2364,8 @@ func (s *AssumeRoleWithWebIdentityInput) SetWebIdentityToken(v string) *AssumeRo } // Contains the response to a successful AssumeRoleWithWebIdentity request, -// including temporary AWS credentials that can be used to make AWS requests. +// including temporary Amazon Web Services credentials that can be used to make +// Amazon Web Services requests. type AssumeRoleWithWebIdentityOutput struct { _ struct{} `type:"structure"` @@ -2471,8 +2498,8 @@ type AssumedRoleUser struct { Arn *string `min:"20" type:"string" required:"true"` // A unique identifier that contains the role ID and the role session name of - // the role that is being assumed. The role ID is generated by AWS when the - // role is created. + // the role that is being assumed. The role ID is generated by Amazon Web Services + // when the role is created. // // AssumedRoleId is a required field AssumedRoleId *string `min:"2" type:"string" required:"true"` @@ -2500,7 +2527,7 @@ func (s *AssumedRoleUser) SetAssumedRoleId(v string) *AssumedRoleUser { return s } -// AWS credentials for API authentication. +// Amazon Web Services credentials for API authentication. type Credentials struct { _ struct{} `type:"structure"` @@ -2601,8 +2628,8 @@ func (s *DecodeAuthorizationMessageInput) SetEncodedMessage(v string) *DecodeAut } // A document that contains additional information about the authorization status -// of a request from an encoded message that is returned in response to an AWS -// request. +// of a request from an encoded message that is returned in response to an Amazon +// Web Services request. type DecodeAuthorizationMessageOutput struct { _ struct{} `type:"structure"` @@ -2714,7 +2741,7 @@ func (s *GetAccessKeyInfoInput) SetAccessKeyId(v string) *GetAccessKeyInfoInput type GetAccessKeyInfoOutput struct { _ struct{} `type:"structure"` - // The number used to identify the AWS account. + // The number used to identify the Amazon Web Services account. Account *string `type:"string"` } @@ -2753,11 +2780,11 @@ func (s GetCallerIdentityInput) GoString() string { type GetCallerIdentityOutput struct { _ struct{} `type:"structure"` - // The AWS account ID number of the account that owns or contains the calling - // entity. + // The Amazon Web Services account ID number of the account that owns or contains + // the calling entity. Account *string `type:"string"` - // The AWS ARN associated with the calling entity. + // The Amazon Web Services ARN associated with the calling entity. Arn *string `min:"20" type:"string"` // The unique identifier of the calling entity. The exact value depends on the @@ -2801,9 +2828,10 @@ type GetFederationTokenInput struct { // The duration, in seconds, that the session should last. Acceptable durations // for federation sessions range from 900 seconds (15 minutes) to 129,600 seconds // (36 hours), with 43,200 seconds (12 hours) as the default. Sessions obtained - // using AWS account root user credentials are restricted to a maximum of 3,600 - // seconds (one hour). If the specified duration is longer than one hour, the - // session obtained by using root user credentials defaults to one hour. + // using Amazon Web Services account root user credentials are restricted to + // a maximum of 3,600 seconds (one hour). If the specified duration is longer + // than one hour, the session obtained by using root user credentials defaults + // to one hour. DurationSeconds *int64 `min:"900" type:"integer"` // The name of the federated user. The name is used as an identifier for the @@ -2848,11 +2876,11 @@ type GetFederationTokenInput struct { // \u00FF). It can also include the tab (\u0009), linefeed (\u000A), and carriage // return (\u000D) characters. // - // An AWS conversion compresses the passed session policies and session tags - // into a packed binary format that has a separate limit. Your request can fail - // for this limit even if your plaintext meets the other requirements. The PackedPolicySize - // response element indicates by percentage how close the policies and tags - // for your request are to the upper size limit. + // An Amazon Web Services conversion compresses the passed session policies + // and session tags into a packed binary format that has a separate limit. Your + // request can fail for this limit even if your plaintext meets the other requirements. + // The PackedPolicySize response element indicates by percentage how close the + // policies and tags for your request are to the upper size limit. Policy *string `min:"1" type:"string"` // The Amazon Resource Names (ARNs) of the IAM managed policies that you want @@ -2865,8 +2893,8 @@ type GetFederationTokenInput struct { // use as managed session policies. The plaintext that you use for both inline // and managed session policies can't exceed 2,048 characters. You can provide // up to 10 managed policy ARNs. For more information about ARNs, see Amazon - // Resource Names (ARNs) and AWS Service Namespaces (https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) - // in the AWS General Reference. + // Resource Names (ARNs) and Amazon Web Services Service Namespaces (https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) + // in the Amazon Web Services General Reference. // // This parameter is optional. However, if you do not pass any session policies, // then the resulting federated user session has no permissions. @@ -2885,11 +2913,11 @@ type GetFederationTokenInput struct { // by the policy. These permissions are granted in addition to the permissions // that are granted by the session policies. // - // An AWS conversion compresses the passed session policies and session tags - // into a packed binary format that has a separate limit. Your request can fail - // for this limit even if your plaintext meets the other requirements. The PackedPolicySize - // response element indicates by percentage how close the policies and tags - // for your request are to the upper size limit. + // An Amazon Web Services conversion compresses the passed session policies + // and session tags into a packed binary format that has a separate limit. Your + // request can fail for this limit even if your plaintext meets the other requirements. + // The PackedPolicySize response element indicates by percentage how close the + // policies and tags for your request are to the upper size limit. PolicyArns []*PolicyDescriptorType `type:"list"` // A list of session tags. Each session tag consists of a key name and an associated @@ -2903,11 +2931,11 @@ type GetFederationTokenInput struct { // Limits (https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-limits.html#reference_iam-limits-entity-length) // in the IAM User Guide. // - // An AWS conversion compresses the passed session policies and session tags - // into a packed binary format that has a separate limit. Your request can fail - // for this limit even if your plaintext meets the other requirements. The PackedPolicySize - // response element indicates by percentage how close the policies and tags - // for your request are to the upper size limit. + // An Amazon Web Services conversion compresses the passed session policies + // and session tags into a packed binary format that has a separate limit. Your + // request can fail for this limit even if your plaintext meets the other requirements. + // The PackedPolicySize response element indicates by percentage how close the + // policies and tags for your request are to the upper size limit. // // You can pass a session tag with the same key as a tag that is already attached // to the user you are federating. When you do, session tags override a user @@ -3004,7 +3032,8 @@ func (s *GetFederationTokenInput) SetTags(v []*Tag) *GetFederationTokenInput { } // Contains the response to a successful GetFederationToken request, including -// temporary AWS credentials that can be used to make AWS requests. +// temporary Amazon Web Services credentials that can be used to make Amazon +// Web Services requests. type GetFederationTokenOutput struct { _ struct{} `type:"structure"` @@ -3062,9 +3091,9 @@ type GetSessionTokenInput struct { // The duration, in seconds, that the credentials should remain valid. Acceptable // durations for IAM user sessions range from 900 seconds (15 minutes) to 129,600 // seconds (36 hours), with 43,200 seconds (12 hours) as the default. Sessions - // for AWS account owners are restricted to a maximum of 3,600 seconds (one - // hour). If the duration is longer than one hour, the session for AWS account - // owners defaults to one hour. + // for Amazon Web Services account owners are restricted to a maximum of 3,600 + // seconds (one hour). If the duration is longer than one hour, the session + // for Amazon Web Services account owners defaults to one hour. DurationSeconds *int64 `min:"900" type:"integer"` // The identification number of the MFA device that is associated with the IAM @@ -3072,7 +3101,7 @@ type GetSessionTokenInput struct { // user has a policy that requires MFA authentication. The value is either the // serial number for a hardware device (such as GAHT12345678) or an Amazon Resource // Name (ARN) for a virtual device (such as arn:aws:iam::123456789012:mfa/user). - // You can find the device for an IAM user by going to the AWS Management Console + // You can find the device for an IAM user by going to the Management Console // and viewing the user's security credentials. // // The regex used to validate this parameter is a string of characters consisting @@ -3139,7 +3168,8 @@ func (s *GetSessionTokenInput) SetTokenCode(v string) *GetSessionTokenInput { } // Contains the response to a successful GetSessionToken request, including -// temporary AWS credentials that can be used to make AWS requests. +// temporary Amazon Web Services credentials that can be used to make Amazon +// Web Services requests. type GetSessionTokenOutput struct { _ struct{} `type:"structure"` @@ -3174,8 +3204,8 @@ type PolicyDescriptorType struct { // The Amazon Resource Name (ARN) of the IAM managed policy to use as a session // policy for the role. For more information about ARNs, see Amazon Resource - // Names (ARNs) and AWS Service Namespaces (https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) - // in the AWS General Reference. + // Names (ARNs) and Amazon Web Services Service Namespaces (https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) + // in the Amazon Web Services General Reference. Arn *string `locationName:"arn" min:"20" type:"string"` } @@ -3210,9 +3240,9 @@ func (s *PolicyDescriptorType) SetArn(v string) *PolicyDescriptorType { // You can pass custom key-value pair attributes when you assume a role or federate // a user. These are called session tags. You can then use the session tags -// to control access to resources. For more information, see Tagging AWS STS -// Sessions (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) -// in the IAM User Guide. +// to control access to resources. For more information, see Tagging STS Sessions +// (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) in +// the IAM User Guide. type Tag struct { _ struct{} `type:"structure"` diff --git a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/service/sts/doc.go b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/service/sts/doc.go index cb1debbaa45..2d98d92353a 100644 --- a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/service/sts/doc.go +++ b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/service/sts/doc.go @@ -3,11 +3,11 @@ // Package sts provides the client and types for making API // requests to AWS Security Token Service. // -// AWS Security Token Service (STS) enables you to request temporary, limited-privilege -// credentials for AWS Identity and Access Management (IAM) users or for users -// that you authenticate (federated users). This guide provides descriptions -// of the STS API. For more information about using this service, see Temporary -// Security Credentials (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp.html). +// Security Token Service (STS) enables you to request temporary, limited-privilege +// credentials for Identity and Access Management (IAM) users or for users that +// you authenticate (federated users). This guide provides descriptions of the +// STS API. For more information about using this service, see Temporary Security +// Credentials (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp.html). // // See https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15 for more information on this service. // diff --git a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/service/sts/errors.go b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/service/sts/errors.go index a233f542ef2..7897d70c87a 100644 --- a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/service/sts/errors.go +++ b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/service/sts/errors.go @@ -42,8 +42,9 @@ const ( // ErrCodeInvalidIdentityTokenException for service response error code // "InvalidIdentityToken". // - // The web identity token that was passed could not be validated by AWS. Get - // a new identity token from the identity provider and then retry the request. + // The web identity token that was passed could not be validated by Amazon Web + // Services. Get a new identity token from the identity provider and then retry + // the request. ErrCodeInvalidIdentityTokenException = "InvalidIdentityToken" // ErrCodeMalformedPolicyDocumentException for service response error code @@ -57,11 +58,11 @@ const ( // "PackedPolicyTooLarge". // // The request was rejected because the total packed size of the session policies - // and session tags combined was too large. An AWS conversion compresses the - // session policy document, session policy ARNs, and session tags into a packed - // binary format that has a separate limit. The error message indicates by percentage - // how close the policies and tags are to the upper size limit. For more information, - // see Passing Session Tags in STS (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) + // and session tags combined was too large. An Amazon Web Services conversion + // compresses the session policy document, session policy ARNs, and session + // tags into a packed binary format that has a separate limit. The error message + // indicates by percentage how close the policies and tags are to the upper + // size limit. For more information, see Passing Session Tags in STS (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) // in the IAM User Guide. // // You could receive this error even though you meet other defined session policy @@ -76,7 +77,8 @@ const ( // STS is not activated in the requested region for the account that is being // asked to generate credentials. The account administrator must use the IAM // console to activate STS in that region. For more information, see Activating - // and Deactivating AWS STS in an AWS Region (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) + // and Deactivating Amazon Web Services STS in an Amazon Web Services Region + // (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) // in the IAM User Guide. ErrCodeRegionDisabledException = "RegionDisabledException" ) diff --git a/awsproviderlint/vendor/golang.org/x/net/http/httpguts/httplex.go b/awsproviderlint/vendor/golang.org/x/net/http/httpguts/httplex.go index e7de24ee64e..c79aa73f28b 100644 --- a/awsproviderlint/vendor/golang.org/x/net/http/httpguts/httplex.go +++ b/awsproviderlint/vendor/golang.org/x/net/http/httpguts/httplex.go @@ -137,11 +137,13 @@ func trimOWS(x string) string { // contains token amongst its comma-separated tokens, ASCII // case-insensitively. func headerValueContainsToken(v string, token string) bool { - v = trimOWS(v) - if comma := strings.IndexByte(v, ','); comma != -1 { - return tokenEqual(trimOWS(v[:comma]), token) || headerValueContainsToken(v[comma+1:], token) + for comma := strings.IndexByte(v, ','); comma != -1; comma = strings.IndexByte(v, ',') { + if tokenEqual(trimOWS(v[:comma]), token) { + return true + } + v = v[comma+1:] } - return tokenEqual(v, token) + return tokenEqual(trimOWS(v), token) } // lowerASCII returns the ASCII lowercase version of b. diff --git a/awsproviderlint/vendor/golang.org/x/net/http2/Dockerfile b/awsproviderlint/vendor/golang.org/x/net/http2/Dockerfile index 53fc5257974..8512245952b 100644 --- a/awsproviderlint/vendor/golang.org/x/net/http2/Dockerfile +++ b/awsproviderlint/vendor/golang.org/x/net/http2/Dockerfile @@ -38,7 +38,7 @@ RUN make RUN make install WORKDIR /root -RUN wget http://curl.haxx.se/download/curl-7.45.0.tar.gz +RUN wget https://curl.se/download/curl-7.45.0.tar.gz RUN tar -zxvf curl-7.45.0.tar.gz WORKDIR /root/curl-7.45.0 RUN ./configure --with-ssl --with-nghttp2=/usr/local diff --git a/awsproviderlint/vendor/golang.org/x/net/http2/ascii.go b/awsproviderlint/vendor/golang.org/x/net/http2/ascii.go new file mode 100644 index 00000000000..17caa205869 --- /dev/null +++ b/awsproviderlint/vendor/golang.org/x/net/http2/ascii.go @@ -0,0 +1,53 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package http2 + +import "strings" + +// The HTTP protocols are defined in terms of ASCII, not Unicode. This file +// contains helper functions which may use Unicode-aware functions which would +// otherwise be unsafe and could introduce vulnerabilities if used improperly. + +// asciiEqualFold is strings.EqualFold, ASCII only. It reports whether s and t +// are equal, ASCII-case-insensitively. +func asciiEqualFold(s, t string) bool { + if len(s) != len(t) { + return false + } + for i := 0; i < len(s); i++ { + if lower(s[i]) != lower(t[i]) { + return false + } + } + return true +} + +// lower returns the ASCII lowercase version of b. +func lower(b byte) byte { + if 'A' <= b && b <= 'Z' { + return b + ('a' - 'A') + } + return b +} + +// isASCIIPrint returns whether s is ASCII and printable according to +// https://tools.ietf.org/html/rfc20#section-4.2. +func isASCIIPrint(s string) bool { + for i := 0; i < len(s); i++ { + if s[i] < ' ' || s[i] > '~' { + return false + } + } + return true +} + +// asciiToLower returns the lowercase version of s if s is ASCII and printable, +// and whether or not it was. +func asciiToLower(s string) (lower string, ok bool) { + if !isASCIIPrint(s) { + return "", false + } + return strings.ToLower(s), true +} diff --git a/awsproviderlint/vendor/golang.org/x/net/http2/client_conn_pool.go b/awsproviderlint/vendor/golang.org/x/net/http2/client_conn_pool.go index 3a67636fe25..652bc11a029 100644 --- a/awsproviderlint/vendor/golang.org/x/net/http2/client_conn_pool.go +++ b/awsproviderlint/vendor/golang.org/x/net/http2/client_conn_pool.go @@ -7,7 +7,9 @@ package http2 import ( + "context" "crypto/tls" + "errors" "net/http" "sync" ) @@ -78,61 +80,69 @@ func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMis // It gets its own connection. traceGetConn(req, addr) const singleUse = true - cc, err := p.t.dialClientConn(addr, singleUse) + cc, err := p.t.dialClientConn(req.Context(), addr, singleUse) if err != nil { return nil, err } return cc, nil } - p.mu.Lock() - for _, cc := range p.conns[addr] { - if st := cc.idleState(); st.canTakeNewRequest { - if p.shouldTraceGetConn(st) { - traceGetConn(req, addr) + for { + p.mu.Lock() + for _, cc := range p.conns[addr] { + if st := cc.idleState(); st.canTakeNewRequest { + if p.shouldTraceGetConn(st) { + traceGetConn(req, addr) + } + p.mu.Unlock() + return cc, nil } + } + if !dialOnMiss { p.mu.Unlock() - return cc, nil + return nil, ErrNoCachedConn } - } - if !dialOnMiss { + traceGetConn(req, addr) + call := p.getStartDialLocked(req.Context(), addr) p.mu.Unlock() - return nil, ErrNoCachedConn + <-call.done + if shouldRetryDial(call, req) { + continue + } + return call.res, call.err } - traceGetConn(req, addr) - call := p.getStartDialLocked(addr) - p.mu.Unlock() - <-call.done - return call.res, call.err } // dialCall is an in-flight Transport dial call to a host. type dialCall struct { - _ incomparable - p *clientConnPool + _ incomparable + p *clientConnPool + // the context associated with the request + // that created this dialCall + ctx context.Context done chan struct{} // closed when done res *ClientConn // valid after done is closed err error // valid after done is closed } // requires p.mu is held. -func (p *clientConnPool) getStartDialLocked(addr string) *dialCall { +func (p *clientConnPool) getStartDialLocked(ctx context.Context, addr string) *dialCall { if call, ok := p.dialing[addr]; ok { // A dial is already in-flight. Don't start another. return call } - call := &dialCall{p: p, done: make(chan struct{})} + call := &dialCall{p: p, done: make(chan struct{}), ctx: ctx} if p.dialing == nil { p.dialing = make(map[string]*dialCall) } p.dialing[addr] = call - go call.dial(addr) + go call.dial(call.ctx, addr) return call } // run in its own goroutine. -func (c *dialCall) dial(addr string) { +func (c *dialCall) dial(ctx context.Context, addr string) { const singleUse = false // shared conn - c.res, c.err = c.p.t.dialClientConn(addr, singleUse) + c.res, c.err = c.p.t.dialClientConn(ctx, addr, singleUse) close(c.done) c.p.mu.Lock() @@ -276,3 +286,28 @@ type noDialClientConnPool struct{ *clientConnPool } func (p noDialClientConnPool) GetClientConn(req *http.Request, addr string) (*ClientConn, error) { return p.getClientConn(req, addr, noDialOnMiss) } + +// shouldRetryDial reports whether the current request should +// retry dialing after the call finished unsuccessfully, for example +// if the dial was canceled because of a context cancellation or +// deadline expiry. +func shouldRetryDial(call *dialCall, req *http.Request) bool { + if call.err == nil { + // No error, no need to retry + return false + } + if call.ctx == req.Context() { + // If the call has the same context as the request, the dial + // should not be retried, since any cancellation will have come + // from this request. + return false + } + if !errors.Is(call.err, context.Canceled) && !errors.Is(call.err, context.DeadlineExceeded) { + // If the call error is not because of a context cancellation or a deadline expiry, + // the dial should not be retried. + return false + } + // Only retry if the error is a context cancellation error or deadline expiry + // and the context associated with the call was canceled or expired. + return call.ctx.Err() != nil +} diff --git a/awsproviderlint/vendor/golang.org/x/net/http2/go115.go b/awsproviderlint/vendor/golang.org/x/net/http2/go115.go new file mode 100644 index 00000000000..908af1ab93c --- /dev/null +++ b/awsproviderlint/vendor/golang.org/x/net/http2/go115.go @@ -0,0 +1,27 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.15 +// +build go1.15 + +package http2 + +import ( + "context" + "crypto/tls" +) + +// dialTLSWithContext uses tls.Dialer, added in Go 1.15, to open a TLS +// connection. +func (t *Transport) dialTLSWithContext(ctx context.Context, network, addr string, cfg *tls.Config) (*tls.Conn, error) { + dialer := &tls.Dialer{ + Config: cfg, + } + cn, err := dialer.DialContext(ctx, network, addr) + if err != nil { + return nil, err + } + tlsCn := cn.(*tls.Conn) // DialContext comment promises this will always succeed + return tlsCn, nil +} diff --git a/awsproviderlint/vendor/golang.org/x/net/http2/headermap.go b/awsproviderlint/vendor/golang.org/x/net/http2/headermap.go index c3ff3fa1c78..9e12941da4c 100644 --- a/awsproviderlint/vendor/golang.org/x/net/http2/headermap.go +++ b/awsproviderlint/vendor/golang.org/x/net/http2/headermap.go @@ -6,7 +6,6 @@ package http2 import ( "net/http" - "strings" "sync" ) @@ -79,10 +78,10 @@ func buildCommonHeaderMaps() { } } -func lowerHeader(v string) string { +func lowerHeader(v string) (lower string, ascii bool) { buildCommonHeaderMapsOnce() if s, ok := commonLowerHeader[v]; ok { - return s + return s, true } - return strings.ToLower(v) + return asciiToLower(v) } diff --git a/awsproviderlint/vendor/golang.org/x/net/http2/not_go115.go b/awsproviderlint/vendor/golang.org/x/net/http2/not_go115.go new file mode 100644 index 00000000000..e6c04cf7ac7 --- /dev/null +++ b/awsproviderlint/vendor/golang.org/x/net/http2/not_go115.go @@ -0,0 +1,31 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !go1.15 +// +build !go1.15 + +package http2 + +import ( + "context" + "crypto/tls" +) + +// dialTLSWithContext opens a TLS connection. +func (t *Transport) dialTLSWithContext(ctx context.Context, network, addr string, cfg *tls.Config) (*tls.Conn, error) { + cn, err := tls.Dial(network, addr, cfg) + if err != nil { + return nil, err + } + if err := cn.Handshake(); err != nil { + return nil, err + } + if cfg.InsecureSkipVerify { + return cn, nil + } + if err := cn.VerifyHostname(cfg.ServerName); err != nil { + return nil, err + } + return cn, nil +} diff --git a/awsproviderlint/vendor/golang.org/x/net/http2/server.go b/awsproviderlint/vendor/golang.org/x/net/http2/server.go index e125bbd2a26..0ccbe9b4c2b 100644 --- a/awsproviderlint/vendor/golang.org/x/net/http2/server.go +++ b/awsproviderlint/vendor/golang.org/x/net/http2/server.go @@ -231,13 +231,12 @@ func ConfigureServer(s *http.Server, conf *Server) error { if s.TLSConfig == nil { s.TLSConfig = new(tls.Config) - } else if s.TLSConfig.CipherSuites != nil { - // If they already provided a CipherSuite list, return - // an error if it has a bad order or is missing - // ECDHE_RSA_WITH_AES_128_GCM_SHA256 or ECDHE_ECDSA_WITH_AES_128_GCM_SHA256. + } else if s.TLSConfig.CipherSuites != nil && s.TLSConfig.MinVersion < tls.VersionTLS13 { + // If they already provided a TLS 1.0–1.2 CipherSuite list, return an + // error if it is missing ECDHE_RSA_WITH_AES_128_GCM_SHA256 or + // ECDHE_ECDSA_WITH_AES_128_GCM_SHA256. haveRequired := false - sawBad := false - for i, cs := range s.TLSConfig.CipherSuites { + for _, cs := range s.TLSConfig.CipherSuites { switch cs { case tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, // Alternative MTI cipher to not discourage ECDSA-only servers. @@ -245,14 +244,9 @@ func ConfigureServer(s *http.Server, conf *Server) error { tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: haveRequired = true } - if isBadCipher(cs) { - sawBad = true - } else if sawBad { - return fmt.Errorf("http2: TLSConfig.CipherSuites index %d contains an HTTP/2-approved cipher suite (%#04x), but it comes after unapproved cipher suites. With this configuration, clients that don't support previous, approved cipher suites may be given an unapproved one and reject the connection.", i, cs) - } } if !haveRequired { - return fmt.Errorf("http2: TLSConfig.CipherSuites is missing an HTTP/2-required AES_128_GCM_SHA256 cipher (need at least one of TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 or TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256).") + return fmt.Errorf("http2: TLSConfig.CipherSuites is missing an HTTP/2-required AES_128_GCM_SHA256 cipher (need at least one of TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 or TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)") } } @@ -265,16 +259,12 @@ func ConfigureServer(s *http.Server, conf *Server) error { s.TLSConfig.PreferServerCipherSuites = true - haveNPN := false - for _, p := range s.TLSConfig.NextProtos { - if p == NextProtoTLS { - haveNPN = true - break - } - } - if !haveNPN { + if !strSliceContains(s.TLSConfig.NextProtos, NextProtoTLS) { s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, NextProtoTLS) } + if !strSliceContains(s.TLSConfig.NextProtos, "http/1.1") { + s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, "http/1.1") + } if s.TLSNextProto == nil { s.TLSNextProto = map[string]func(*http.Server, *tls.Conn, http.Handler){} @@ -2789,8 +2779,12 @@ func (w *responseWriter) Push(target string, opts *http.PushOptions) error { // but PUSH_PROMISE requests cannot have a body. // http://tools.ietf.org/html/rfc7540#section-8.2 // Also disallow Host, since the promised URL must be absolute. - switch strings.ToLower(k) { - case "content-length", "content-encoding", "trailer", "te", "expect", "host": + if asciiEqualFold(k, "content-length") || + asciiEqualFold(k, "content-encoding") || + asciiEqualFold(k, "trailer") || + asciiEqualFold(k, "te") || + asciiEqualFold(k, "expect") || + asciiEqualFold(k, "host") { return fmt.Errorf("promised request headers cannot include %q", k) } } diff --git a/awsproviderlint/vendor/golang.org/x/net/http2/transport.go b/awsproviderlint/vendor/golang.org/x/net/http2/transport.go index 7688d72c396..b97adff7d0f 100644 --- a/awsproviderlint/vendor/golang.org/x/net/http2/transport.go +++ b/awsproviderlint/vendor/golang.org/x/net/http2/transport.go @@ -264,9 +264,8 @@ type ClientConn struct { peerMaxHeaderListSize uint64 initialWindowSize uint32 - hbuf bytes.Buffer // HPACK encoder writes into this - henc *hpack.Encoder - freeBuf [][]byte + hbuf bytes.Buffer // HPACK encoder writes into this + henc *hpack.Encoder wmu sync.Mutex // held while writing; acquire AFTER mu if holding both werr error // first write error that has occurred @@ -564,12 +563,12 @@ func canRetryError(err error) bool { return false } -func (t *Transport) dialClientConn(addr string, singleUse bool) (*ClientConn, error) { +func (t *Transport) dialClientConn(ctx context.Context, addr string, singleUse bool) (*ClientConn, error) { host, _, err := net.SplitHostPort(addr) if err != nil { return nil, err } - tconn, err := t.dialTLS()("tcp", addr, t.newTLSConfig(host)) + tconn, err := t.dialTLS(ctx)("tcp", addr, t.newTLSConfig(host)) if err != nil { return nil, err } @@ -590,34 +589,24 @@ func (t *Transport) newTLSConfig(host string) *tls.Config { return cfg } -func (t *Transport) dialTLS() func(string, string, *tls.Config) (net.Conn, error) { +func (t *Transport) dialTLS(ctx context.Context) func(string, string, *tls.Config) (net.Conn, error) { if t.DialTLS != nil { return t.DialTLS } - return t.dialTLSDefault -} - -func (t *Transport) dialTLSDefault(network, addr string, cfg *tls.Config) (net.Conn, error) { - cn, err := tls.Dial(network, addr, cfg) - if err != nil { - return nil, err - } - if err := cn.Handshake(); err != nil { - return nil, err - } - if !cfg.InsecureSkipVerify { - if err := cn.VerifyHostname(cfg.ServerName); err != nil { + return func(network, addr string, cfg *tls.Config) (net.Conn, error) { + tlsCn, err := t.dialTLSWithContext(ctx, network, addr, cfg) + if err != nil { return nil, err } + state := tlsCn.ConnectionState() + if p := state.NegotiatedProtocol; p != NextProtoTLS { + return nil, fmt.Errorf("http2: unexpected ALPN protocol %q; want %q", p, NextProtoTLS) + } + if !state.NegotiatedProtocolIsMutual { + return nil, errors.New("http2: could not negotiate protocol mutually") + } + return tlsCn, nil } - state := cn.ConnectionState() - if p := state.NegotiatedProtocol; p != NextProtoTLS { - return nil, fmt.Errorf("http2: unexpected ALPN protocol %q; want %q", p, NextProtoTLS) - } - if !state.NegotiatedProtocolIsMutual { - return nil, errors.New("http2: could not negotiate protocol mutually") - } - return cn, nil } // disableKeepAlives reports whether connections should be closed as @@ -923,46 +912,6 @@ func (cc *ClientConn) closeForLostPing() error { return cc.closeForError(err) } -const maxAllocFrameSize = 512 << 10 - -// frameBuffer returns a scratch buffer suitable for writing DATA frames. -// They're capped at the min of the peer's max frame size or 512KB -// (kinda arbitrarily), but definitely capped so we don't allocate 4GB -// bufers. -func (cc *ClientConn) frameScratchBuffer() []byte { - cc.mu.Lock() - size := cc.maxFrameSize - if size > maxAllocFrameSize { - size = maxAllocFrameSize - } - for i, buf := range cc.freeBuf { - if len(buf) >= int(size) { - cc.freeBuf[i] = nil - cc.mu.Unlock() - return buf[:size] - } - } - cc.mu.Unlock() - return make([]byte, size) -} - -func (cc *ClientConn) putFrameScratchBuffer(buf []byte) { - cc.mu.Lock() - defer cc.mu.Unlock() - const maxBufs = 4 // arbitrary; 4 concurrent requests per conn? investigate. - if len(cc.freeBuf) < maxBufs { - cc.freeBuf = append(cc.freeBuf, buf) - return - } - for i, old := range cc.freeBuf { - if old == nil { - cc.freeBuf[i] = buf - return - } - } - // forget about it. -} - // errRequestCanceled is a copy of net/http's errRequestCanceled because it's not // exported. At least they'll be DeepEqual for h1-vs-h2 comparisons tests. var errRequestCanceled = errors.New("net/http: request canceled") @@ -1005,7 +954,7 @@ func checkConnHeaders(req *http.Request) error { if vv := req.Header["Transfer-Encoding"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && vv[0] != "chunked") { return fmt.Errorf("http2: invalid Transfer-Encoding request header: %q", vv) } - if vv := req.Header["Connection"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && !strings.EqualFold(vv[0], "close") && !strings.EqualFold(vv[0], "keep-alive")) { + if vv := req.Header["Connection"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && !asciiEqualFold(vv[0], "close") && !asciiEqualFold(vv[0], "keep-alive")) { return fmt.Errorf("http2: invalid Connection request header: %q", vv) } return nil @@ -1305,11 +1254,35 @@ var ( errReqBodyTooLong = errors.New("http2: request body larger than specified content length") ) +// frameScratchBufferLen returns the length of a buffer to use for +// outgoing request bodies to read/write to/from. +// +// It returns max(1, min(peer's advertised max frame size, +// Request.ContentLength+1, 512KB)). +func (cs *clientStream) frameScratchBufferLen(maxFrameSize int) int { + const max = 512 << 10 + n := int64(maxFrameSize) + if n > max { + n = max + } + if cl := actualContentLength(cs.req); cl != -1 && cl+1 < n { + // Add an extra byte past the declared content-length to + // give the caller's Request.Body io.Reader a chance to + // give us more bytes than they declared, so we can catch it + // early. + n = cl + 1 + } + if n < 1 { + return 1 + } + return int(n) // doesn't truncate; max is 512K +} + +var bufPool sync.Pool // of *[]byte + func (cs *clientStream) writeRequestBody(body io.Reader, bodyCloser io.Closer) (err error) { cc := cs.cc sentEnd := false // whether we sent the final DATA frame w/ END_STREAM - buf := cc.frameScratchBuffer() - defer cc.putFrameScratchBuffer(buf) defer func() { traceWroteRequest(cs.trace, err) @@ -1328,9 +1301,24 @@ func (cs *clientStream) writeRequestBody(body io.Reader, bodyCloser io.Closer) ( remainLen := actualContentLength(req) hasContentLen := remainLen != -1 + cc.mu.Lock() + maxFrameSize := int(cc.maxFrameSize) + cc.mu.Unlock() + + // Scratch buffer for reading into & writing from. + scratchLen := cs.frameScratchBufferLen(maxFrameSize) + var buf []byte + if bp, ok := bufPool.Get().(*[]byte); ok && len(*bp) >= scratchLen { + defer bufPool.Put(bp) + buf = *bp + } else { + buf = make([]byte, scratchLen) + defer bufPool.Put(&buf) + } + var sawEOF bool for !sawEOF { - n, err := body.Read(buf[:len(buf)-1]) + n, err := body.Read(buf[:len(buf)]) if hasContentLen { remainLen -= int64(n) if remainLen == 0 && err == nil { @@ -1341,8 +1329,9 @@ func (cs *clientStream) writeRequestBody(body io.Reader, bodyCloser io.Closer) ( // to send the END_STREAM bit early, double-check that we're actually // at EOF. Subsequent reads should return (0, EOF) at this point. // If either value is different, we return an error in one of two ways below. + var scratch [1]byte var n1 int - n1, err = body.Read(buf[n:]) + n1, err = body.Read(scratch[:]) remainLen -= int64(n1) } if remainLen < 0 { @@ -1412,10 +1401,6 @@ func (cs *clientStream) writeRequestBody(body io.Reader, bodyCloser io.Closer) ( } } - cc.mu.Lock() - maxFrameSize := int(cc.maxFrameSize) - cc.mu.Unlock() - cc.wmu.Lock() defer cc.wmu.Unlock() @@ -1531,19 +1516,21 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail var didUA bool for k, vv := range req.Header { - if strings.EqualFold(k, "host") || strings.EqualFold(k, "content-length") { + if asciiEqualFold(k, "host") || asciiEqualFold(k, "content-length") { // Host is :authority, already sent. // Content-Length is automatic, set below. continue - } else if strings.EqualFold(k, "connection") || strings.EqualFold(k, "proxy-connection") || - strings.EqualFold(k, "transfer-encoding") || strings.EqualFold(k, "upgrade") || - strings.EqualFold(k, "keep-alive") { + } else if asciiEqualFold(k, "connection") || + asciiEqualFold(k, "proxy-connection") || + asciiEqualFold(k, "transfer-encoding") || + asciiEqualFold(k, "upgrade") || + asciiEqualFold(k, "keep-alive") { // Per 8.1.2.2 Connection-Specific Header // Fields, don't send connection-specific // fields. We have already checked if any // are error-worthy so just ignore the rest. continue - } else if strings.EqualFold(k, "user-agent") { + } else if asciiEqualFold(k, "user-agent") { // Match Go's http1 behavior: at most one // User-Agent. If set to nil or empty string, // then omit it. Otherwise if not mentioned, @@ -1556,7 +1543,7 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail if vv[0] == "" { continue } - } else if strings.EqualFold(k, "cookie") { + } else if asciiEqualFold(k, "cookie") { // Per 8.1.2.5 To allow for better compression efficiency, the // Cookie header field MAY be split into separate header fields, // each with one or more cookie-pairs. @@ -1615,7 +1602,12 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail // Header list size is ok. Write the headers. enumerateHeaders(func(name, value string) { - name = strings.ToLower(name) + name, ascii := asciiToLower(name) + if !ascii { + // Skip writing invalid headers. Per RFC 7540, Section 8.1.2, header + // field names have to be ASCII characters (just as in HTTP/1.x). + return + } cc.writeHeader(name, value) if traceHeaders { traceWroteHeaderField(trace, name, value) @@ -1663,9 +1655,14 @@ func (cc *ClientConn) encodeTrailers(req *http.Request) ([]byte, error) { } for k, vv := range req.Trailer { + lowKey, ascii := asciiToLower(k) + if !ascii { + // Skip writing invalid headers. Per RFC 7540, Section 8.1.2, header + // field names have to be ASCII characters (just as in HTTP/1.x). + continue + } // Transfer-Encoding, etc.. have already been filtered at the // start of RoundTrip - lowKey := strings.ToLower(k) for _, v := range vv { cc.writeHeader(lowKey, v) } diff --git a/awsproviderlint/vendor/golang.org/x/net/http2/write.go b/awsproviderlint/vendor/golang.org/x/net/http2/write.go index 3849bc2632e..33f61398a12 100644 --- a/awsproviderlint/vendor/golang.org/x/net/http2/write.go +++ b/awsproviderlint/vendor/golang.org/x/net/http2/write.go @@ -341,7 +341,12 @@ func encodeHeaders(enc *hpack.Encoder, h http.Header, keys []string) { } for _, k := range keys { vv := h[k] - k = lowerHeader(k) + k, ascii := lowerHeader(k) + if !ascii { + // Skip writing invalid headers. Per RFC 7540, Section 8.1.2, header + // field names have to be ASCII characters (just as in HTTP/1.x). + continue + } if !validWireHeaderFieldName(k) { // Skip it as backup paranoia. Per // golang.org/issue/14048, these should diff --git a/awsproviderlint/vendor/golang.org/x/net/idna/idna10.0.0.go b/awsproviderlint/vendor/golang.org/x/net/idna/idna10.0.0.go index 7e69ee1b22e..5208ba6cb88 100644 --- a/awsproviderlint/vendor/golang.org/x/net/idna/idna10.0.0.go +++ b/awsproviderlint/vendor/golang.org/x/net/idna/idna10.0.0.go @@ -67,15 +67,14 @@ func Transitional(transitional bool) Option { // VerifyDNSLength sets whether a Profile should fail if any of the IDN parts // are longer than allowed by the RFC. +// +// This option corresponds to the VerifyDnsLength flag in UTS #46. func VerifyDNSLength(verify bool) Option { return func(o *options) { o.verifyDNSLength = verify } } // RemoveLeadingDots removes leading label separators. Leading runes that map to // dots, such as U+3002 IDEOGRAPHIC FULL STOP, are removed as well. -// -// This is the behavior suggested by the UTS #46 and is adopted by some -// browsers. func RemoveLeadingDots(remove bool) Option { return func(o *options) { o.removeLeadingDots = remove } } @@ -83,6 +82,8 @@ func RemoveLeadingDots(remove bool) Option { // ValidateLabels sets whether to check the mandatory label validation criteria // as defined in Section 5.4 of RFC 5891. This includes testing for correct use // of hyphens ('-'), normalization, validity of runes, and the context rules. +// In particular, ValidateLabels also sets the CheckHyphens and CheckJoiners flags +// in UTS #46. func ValidateLabels(enable bool) Option { return func(o *options) { // Don't override existing mappings, but set one that at least checks @@ -91,25 +92,48 @@ func ValidateLabels(enable bool) Option { o.mapping = normalize } o.trie = trie - o.validateLabels = enable - o.fromPuny = validateFromPunycode + o.checkJoiners = enable + o.checkHyphens = enable + if enable { + o.fromPuny = validateFromPunycode + } else { + o.fromPuny = nil + } + } +} + +// CheckHyphens sets whether to check for correct use of hyphens ('-') in +// labels. Most web browsers do not have this option set, since labels such as +// "r3---sn-apo3qvuoxuxbt-j5pe" are in common use. +// +// This option corresponds to the CheckHyphens flag in UTS #46. +func CheckHyphens(enable bool) Option { + return func(o *options) { o.checkHyphens = enable } +} + +// CheckJoiners sets whether to check the ContextJ rules as defined in Appendix +// A of RFC 5892, concerning the use of joiner runes. +// +// This option corresponds to the CheckJoiners flag in UTS #46. +func CheckJoiners(enable bool) Option { + return func(o *options) { + o.trie = trie + o.checkJoiners = enable } } // StrictDomainName limits the set of permissible ASCII characters to those // allowed in domain names as defined in RFC 1034 (A-Z, a-z, 0-9 and the -// hyphen). This is set by default for MapForLookup and ValidateForRegistration. +// hyphen). This is set by default for MapForLookup and ValidateForRegistration, +// but is only useful if ValidateLabels is set. // // This option is useful, for instance, for browsers that allow characters // outside this range, for example a '_' (U+005F LOW LINE). See -// http://www.rfc-editor.org/std/std3.txt for more details This option -// corresponds to the UseSTD3ASCIIRules option in UTS #46. +// http://www.rfc-editor.org/std/std3.txt for more details. +// +// This option corresponds to the UseSTD3ASCIIRules flag in UTS #46. func StrictDomainName(use bool) Option { - return func(o *options) { - o.trie = trie - o.useSTD3Rules = use - o.fromPuny = validateFromPunycode - } + return func(o *options) { o.useSTD3Rules = use } } // NOTE: the following options pull in tables. The tables should not be linked @@ -117,6 +141,8 @@ func StrictDomainName(use bool) Option { // BidiRule enables the Bidi rule as defined in RFC 5893. Any application // that relies on proper validation of labels should include this rule. +// +// This option corresponds to the CheckBidi flag in UTS #46. func BidiRule() Option { return func(o *options) { o.bidirule = bidirule.ValidString } } @@ -152,7 +178,8 @@ func MapForLookup() Option { type options struct { transitional bool useSTD3Rules bool - validateLabels bool + checkHyphens bool + checkJoiners bool verifyDNSLength bool removeLeadingDots bool @@ -225,8 +252,11 @@ func (p *Profile) String() string { if p.useSTD3Rules { s += ":UseSTD3Rules" } - if p.validateLabels { - s += ":ValidateLabels" + if p.checkHyphens { + s += ":CheckHyphens" + } + if p.checkJoiners { + s += ":CheckJoiners" } if p.verifyDNSLength { s += ":VerifyDNSLength" @@ -254,26 +284,29 @@ var ( punycode = &Profile{} lookup = &Profile{options{ - transitional: true, - useSTD3Rules: true, - validateLabels: true, - trie: trie, - fromPuny: validateFromPunycode, - mapping: validateAndMap, - bidirule: bidirule.ValidString, + transitional: true, + useSTD3Rules: true, + checkHyphens: true, + checkJoiners: true, + trie: trie, + fromPuny: validateFromPunycode, + mapping: validateAndMap, + bidirule: bidirule.ValidString, }} display = &Profile{options{ - useSTD3Rules: true, - validateLabels: true, - trie: trie, - fromPuny: validateFromPunycode, - mapping: validateAndMap, - bidirule: bidirule.ValidString, + useSTD3Rules: true, + checkHyphens: true, + checkJoiners: true, + trie: trie, + fromPuny: validateFromPunycode, + mapping: validateAndMap, + bidirule: bidirule.ValidString, }} registration = &Profile{options{ useSTD3Rules: true, - validateLabels: true, verifyDNSLength: true, + checkHyphens: true, + checkJoiners: true, trie: trie, fromPuny: validateFromPunycode, mapping: validateRegistration, @@ -340,7 +373,7 @@ func (p *Profile) process(s string, toASCII bool) (string, error) { } isBidi = isBidi || bidirule.DirectionString(u) != bidi.LeftToRight labels.set(u) - if err == nil && p.validateLabels { + if err == nil && p.fromPuny != nil { err = p.fromPuny(p, u) } if err == nil { @@ -681,16 +714,18 @@ func (p *Profile) validateLabel(s string) (err error) { } return nil } - if !p.validateLabels { - return nil - } - trie := p.trie // p.validateLabels is only set if trie is set. - if len(s) > 4 && s[2] == '-' && s[3] == '-' { - return &labelError{s, "V2"} + if p.checkHyphens { + if len(s) > 4 && s[2] == '-' && s[3] == '-' { + return &labelError{s, "V2"} + } + if s[0] == '-' || s[len(s)-1] == '-' { + return &labelError{s, "V3"} + } } - if s[0] == '-' || s[len(s)-1] == '-' { - return &labelError{s, "V3"} + if !p.checkJoiners { + return nil } + trie := p.trie // p.checkJoiners is only set if trie is set. // TODO: merge the use of this in the trie. v, sz := trie.lookupString(s) x := info(v) diff --git a/awsproviderlint/vendor/golang.org/x/net/idna/idna9.0.0.go b/awsproviderlint/vendor/golang.org/x/net/idna/idna9.0.0.go index 7c7456374c1..55f718f1274 100644 --- a/awsproviderlint/vendor/golang.org/x/net/idna/idna9.0.0.go +++ b/awsproviderlint/vendor/golang.org/x/net/idna/idna9.0.0.go @@ -66,15 +66,14 @@ func Transitional(transitional bool) Option { // VerifyDNSLength sets whether a Profile should fail if any of the IDN parts // are longer than allowed by the RFC. +// +// This option corresponds to the VerifyDnsLength flag in UTS #46. func VerifyDNSLength(verify bool) Option { return func(o *options) { o.verifyDNSLength = verify } } // RemoveLeadingDots removes leading label separators. Leading runes that map to // dots, such as U+3002 IDEOGRAPHIC FULL STOP, are removed as well. -// -// This is the behavior suggested by the UTS #46 and is adopted by some -// browsers. func RemoveLeadingDots(remove bool) Option { return func(o *options) { o.removeLeadingDots = remove } } @@ -82,6 +81,8 @@ func RemoveLeadingDots(remove bool) Option { // ValidateLabels sets whether to check the mandatory label validation criteria // as defined in Section 5.4 of RFC 5891. This includes testing for correct use // of hyphens ('-'), normalization, validity of runes, and the context rules. +// In particular, ValidateLabels also sets the CheckHyphens and CheckJoiners flags +// in UTS #46. func ValidateLabels(enable bool) Option { return func(o *options) { // Don't override existing mappings, but set one that at least checks @@ -90,25 +91,48 @@ func ValidateLabels(enable bool) Option { o.mapping = normalize } o.trie = trie - o.validateLabels = enable - o.fromPuny = validateFromPunycode + o.checkJoiners = enable + o.checkHyphens = enable + if enable { + o.fromPuny = validateFromPunycode + } else { + o.fromPuny = nil + } + } +} + +// CheckHyphens sets whether to check for correct use of hyphens ('-') in +// labels. Most web browsers do not have this option set, since labels such as +// "r3---sn-apo3qvuoxuxbt-j5pe" are in common use. +// +// This option corresponds to the CheckHyphens flag in UTS #46. +func CheckHyphens(enable bool) Option { + return func(o *options) { o.checkHyphens = enable } +} + +// CheckJoiners sets whether to check the ContextJ rules as defined in Appendix +// A of RFC 5892, concerning the use of joiner runes. +// +// This option corresponds to the CheckJoiners flag in UTS #46. +func CheckJoiners(enable bool) Option { + return func(o *options) { + o.trie = trie + o.checkJoiners = enable } } // StrictDomainName limits the set of permissable ASCII characters to those // allowed in domain names as defined in RFC 1034 (A-Z, a-z, 0-9 and the -// hyphen). This is set by default for MapForLookup and ValidateForRegistration. +// hyphen). This is set by default for MapForLookup and ValidateForRegistration, +// but is only useful if ValidateLabels is set. // // This option is useful, for instance, for browsers that allow characters // outside this range, for example a '_' (U+005F LOW LINE). See -// http://www.rfc-editor.org/std/std3.txt for more details This option -// corresponds to the UseSTD3ASCIIRules option in UTS #46. +// http://www.rfc-editor.org/std/std3.txt for more details. +// +// This option corresponds to the UseSTD3ASCIIRules flag in UTS #46. func StrictDomainName(use bool) Option { - return func(o *options) { - o.trie = trie - o.useSTD3Rules = use - o.fromPuny = validateFromPunycode - } + return func(o *options) { o.useSTD3Rules = use } } // NOTE: the following options pull in tables. The tables should not be linked @@ -116,6 +140,8 @@ func StrictDomainName(use bool) Option { // BidiRule enables the Bidi rule as defined in RFC 5893. Any application // that relies on proper validation of labels should include this rule. +// +// This option corresponds to the CheckBidi flag in UTS #46. func BidiRule() Option { return func(o *options) { o.bidirule = bidirule.ValidString } } @@ -152,7 +178,8 @@ func MapForLookup() Option { type options struct { transitional bool useSTD3Rules bool - validateLabels bool + checkHyphens bool + checkJoiners bool verifyDNSLength bool removeLeadingDots bool @@ -225,8 +252,11 @@ func (p *Profile) String() string { if p.useSTD3Rules { s += ":UseSTD3Rules" } - if p.validateLabels { - s += ":ValidateLabels" + if p.checkHyphens { + s += ":CheckHyphens" + } + if p.checkJoiners { + s += ":CheckJoiners" } if p.verifyDNSLength { s += ":VerifyDNSLength" @@ -255,9 +285,10 @@ var ( punycode = &Profile{} lookup = &Profile{options{ transitional: true, - useSTD3Rules: true, - validateLabels: true, removeLeadingDots: true, + useSTD3Rules: true, + checkHyphens: true, + checkJoiners: true, trie: trie, fromPuny: validateFromPunycode, mapping: validateAndMap, @@ -265,8 +296,9 @@ var ( }} display = &Profile{options{ useSTD3Rules: true, - validateLabels: true, removeLeadingDots: true, + checkHyphens: true, + checkJoiners: true, trie: trie, fromPuny: validateFromPunycode, mapping: validateAndMap, @@ -274,8 +306,9 @@ var ( }} registration = &Profile{options{ useSTD3Rules: true, - validateLabels: true, verifyDNSLength: true, + checkHyphens: true, + checkJoiners: true, trie: trie, fromPuny: validateFromPunycode, mapping: validateRegistration, @@ -339,7 +372,7 @@ func (p *Profile) process(s string, toASCII bool) (string, error) { continue } labels.set(u) - if err == nil && p.validateLabels { + if err == nil && p.fromPuny != nil { err = p.fromPuny(p, u) } if err == nil { @@ -629,16 +662,18 @@ func (p *Profile) validateLabel(s string) error { if p.bidirule != nil && !p.bidirule(s) { return &labelError{s, "B"} } - if !p.validateLabels { - return nil - } - trie := p.trie // p.validateLabels is only set if trie is set. - if len(s) > 4 && s[2] == '-' && s[3] == '-' { - return &labelError{s, "V2"} + if p.checkHyphens { + if len(s) > 4 && s[2] == '-' && s[3] == '-' { + return &labelError{s, "V2"} + } + if s[0] == '-' || s[len(s)-1] == '-' { + return &labelError{s, "V3"} + } } - if s[0] == '-' || s[len(s)-1] == '-' { - return &labelError{s, "V3"} + if !p.checkJoiners { + return nil } + trie := p.trie // p.checkJoiners is only set if trie is set. // TODO: merge the use of this in the trie. v, sz := trie.lookupString(s) x := info(v) diff --git a/awsproviderlint/vendor/golang.org/x/text/secure/bidirule/bidirule10.0.0.go b/awsproviderlint/vendor/golang.org/x/text/secure/bidirule/bidirule10.0.0.go index e4c62289f90..8a7392c4a16 100644 --- a/awsproviderlint/vendor/golang.org/x/text/secure/bidirule/bidirule10.0.0.go +++ b/awsproviderlint/vendor/golang.org/x/text/secure/bidirule/bidirule10.0.0.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.10 // +build go1.10 package bidirule diff --git a/awsproviderlint/vendor/golang.org/x/text/secure/bidirule/bidirule9.0.0.go b/awsproviderlint/vendor/golang.org/x/text/secure/bidirule/bidirule9.0.0.go index 02b9e1e9d4c..bb0a920018c 100644 --- a/awsproviderlint/vendor/golang.org/x/text/secure/bidirule/bidirule9.0.0.go +++ b/awsproviderlint/vendor/golang.org/x/text/secure/bidirule/bidirule9.0.0.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !go1.10 // +build !go1.10 package bidirule diff --git a/awsproviderlint/vendor/golang.org/x/text/unicode/bidi/tables10.0.0.go b/awsproviderlint/vendor/golang.org/x/text/unicode/bidi/tables10.0.0.go index d8c94e1bd1a..42fa8d72cec 100644 --- a/awsproviderlint/vendor/golang.org/x/text/unicode/bidi/tables10.0.0.go +++ b/awsproviderlint/vendor/golang.org/x/text/unicode/bidi/tables10.0.0.go @@ -1,5 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. +//go:build go1.10 && !go1.13 // +build go1.10,!go1.13 package bidi diff --git a/awsproviderlint/vendor/golang.org/x/text/unicode/bidi/tables11.0.0.go b/awsproviderlint/vendor/golang.org/x/text/unicode/bidi/tables11.0.0.go index 16b11db5388..56a0e1ea216 100644 --- a/awsproviderlint/vendor/golang.org/x/text/unicode/bidi/tables11.0.0.go +++ b/awsproviderlint/vendor/golang.org/x/text/unicode/bidi/tables11.0.0.go @@ -1,5 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. +//go:build go1.13 && !go1.14 // +build go1.13,!go1.14 package bidi diff --git a/awsproviderlint/vendor/golang.org/x/text/unicode/bidi/tables12.0.0.go b/awsproviderlint/vendor/golang.org/x/text/unicode/bidi/tables12.0.0.go index 647f2d4279e..baacf32b43c 100644 --- a/awsproviderlint/vendor/golang.org/x/text/unicode/bidi/tables12.0.0.go +++ b/awsproviderlint/vendor/golang.org/x/text/unicode/bidi/tables12.0.0.go @@ -1,5 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. +//go:build go1.14 && !go1.16 // +build go1.14,!go1.16 package bidi diff --git a/awsproviderlint/vendor/golang.org/x/text/unicode/bidi/tables13.0.0.go b/awsproviderlint/vendor/golang.org/x/text/unicode/bidi/tables13.0.0.go index c937d0976fe..f248effae17 100644 --- a/awsproviderlint/vendor/golang.org/x/text/unicode/bidi/tables13.0.0.go +++ b/awsproviderlint/vendor/golang.org/x/text/unicode/bidi/tables13.0.0.go @@ -1,5 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. +//go:build go1.16 // +build go1.16 package bidi diff --git a/awsproviderlint/vendor/golang.org/x/text/unicode/bidi/tables9.0.0.go b/awsproviderlint/vendor/golang.org/x/text/unicode/bidi/tables9.0.0.go index 0ca0193ebe2..f517fdb202a 100644 --- a/awsproviderlint/vendor/golang.org/x/text/unicode/bidi/tables9.0.0.go +++ b/awsproviderlint/vendor/golang.org/x/text/unicode/bidi/tables9.0.0.go @@ -1,5 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. +//go:build !go1.10 // +build !go1.10 package bidi diff --git a/awsproviderlint/vendor/golang.org/x/text/unicode/norm/tables10.0.0.go b/awsproviderlint/vendor/golang.org/x/text/unicode/norm/tables10.0.0.go index 26fbd55a124..f5a0788277f 100644 --- a/awsproviderlint/vendor/golang.org/x/text/unicode/norm/tables10.0.0.go +++ b/awsproviderlint/vendor/golang.org/x/text/unicode/norm/tables10.0.0.go @@ -1,5 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. +//go:build go1.10 && !go1.13 // +build go1.10,!go1.13 package norm diff --git a/awsproviderlint/vendor/golang.org/x/text/unicode/norm/tables11.0.0.go b/awsproviderlint/vendor/golang.org/x/text/unicode/norm/tables11.0.0.go index 2c58f09baa4..cb7239c4377 100644 --- a/awsproviderlint/vendor/golang.org/x/text/unicode/norm/tables11.0.0.go +++ b/awsproviderlint/vendor/golang.org/x/text/unicode/norm/tables11.0.0.go @@ -1,5 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. +//go:build go1.13 && !go1.14 // +build go1.13,!go1.14 package norm diff --git a/awsproviderlint/vendor/golang.org/x/text/unicode/norm/tables12.0.0.go b/awsproviderlint/vendor/golang.org/x/text/unicode/norm/tables12.0.0.go index 7e1ae096e5c..11b27330017 100644 --- a/awsproviderlint/vendor/golang.org/x/text/unicode/norm/tables12.0.0.go +++ b/awsproviderlint/vendor/golang.org/x/text/unicode/norm/tables12.0.0.go @@ -1,5 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. +//go:build go1.14 && !go1.16 // +build go1.14,!go1.16 package norm diff --git a/awsproviderlint/vendor/golang.org/x/text/unicode/norm/tables13.0.0.go b/awsproviderlint/vendor/golang.org/x/text/unicode/norm/tables13.0.0.go index 9ea1b421407..96a130d30e9 100644 --- a/awsproviderlint/vendor/golang.org/x/text/unicode/norm/tables13.0.0.go +++ b/awsproviderlint/vendor/golang.org/x/text/unicode/norm/tables13.0.0.go @@ -1,5 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. +//go:build go1.16 // +build go1.16 package norm diff --git a/awsproviderlint/vendor/golang.org/x/text/unicode/norm/tables9.0.0.go b/awsproviderlint/vendor/golang.org/x/text/unicode/norm/tables9.0.0.go index 94290692913..0175eae50aa 100644 --- a/awsproviderlint/vendor/golang.org/x/text/unicode/norm/tables9.0.0.go +++ b/awsproviderlint/vendor/golang.org/x/text/unicode/norm/tables9.0.0.go @@ -1,5 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. +//go:build !go1.10 // +build !go1.10 package norm diff --git a/awsproviderlint/vendor/modules.txt b/awsproviderlint/vendor/modules.txt index 5af0bac157a..87f7b43fbb1 100644 --- a/awsproviderlint/vendor/modules.txt +++ b/awsproviderlint/vendor/modules.txt @@ -14,7 +14,7 @@ github.com/agext/levenshtein github.com/apparentlymart/go-textseg/v12/textseg # github.com/apparentlymart/go-textseg/v13 v13.0.0 github.com/apparentlymart/go-textseg/v13/textseg -# github.com/aws/aws-sdk-go v1.39.0 +# github.com/aws/aws-sdk-go v1.39.2 ## explicit github.com/aws/aws-sdk-go/aws github.com/aws/aws-sdk-go/aws/arn @@ -390,7 +390,7 @@ golang.org/x/lint/golint # golang.org/x/mod v0.3.0 golang.org/x/mod/module golang.org/x/mod/semver -# golang.org/x/net v0.0.0-20210326060303-6b1517762897 +# golang.org/x/net v0.0.0-20210614182718-04defd469f4e golang.org/x/net/context golang.org/x/net/context/ctxhttp golang.org/x/net/http/httpguts @@ -408,7 +408,7 @@ golang.org/x/oauth2/jwt # golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79 golang.org/x/sys/internal/unsafeheader golang.org/x/sys/unix -# golang.org/x/text v0.3.5 +# golang.org/x/text v0.3.6 golang.org/x/text/secure/bidirule golang.org/x/text/transform golang.org/x/text/unicode/bidi From 67c0e44e36a7bfc1ae88e970aa9964bc63dc5b00 Mon Sep 17 00:00:00 2001 From: Karri Balk Date: Sat, 5 Jun 2021 20:58:35 +0000 Subject: [PATCH 186/398] Ignore RAM OperationNotPermittedException disassocation err --- aws/resource_aws_ram_resource_share_accepter.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aws/resource_aws_ram_resource_share_accepter.go b/aws/resource_aws_ram_resource_share_accepter.go index cb157f6bcc1..dd8594ece24 100644 --- a/aws/resource_aws_ram_resource_share_accepter.go +++ b/aws/resource_aws_ram_resource_share_accepter.go @@ -207,8 +207,9 @@ func resourceAwsRamResourceShareAccepterDelete(d *schema.ResourceData, meta inte _, err := conn.DisassociateResourceShare(input) - if err != nil { + if err != nil && !tfawserr.ErrCodeEquals(err, ram.ErrCodeOperationNotPermittedException) { return fmt.Errorf("Error leaving RAM resource share: %s", err) + } _, err = waiter.ResourceShareOwnedBySelfDisassociated(conn, d.Id(), d.Timeout(schema.TimeoutDelete)) From 84a231c9cde65b2336aa87deccf2dfc98452a1f0 Mon Sep 17 00:00:00 2001 From: Karri Balk Date: Tue, 8 Jun 2021 03:08:59 +0000 Subject: [PATCH 187/398] Add test with shared route53 resource --- ...ce_aws_ram_resource_share_accepter_test.go | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/aws/resource_aws_ram_resource_share_accepter_test.go b/aws/resource_aws_ram_resource_share_accepter_test.go index ee8cc487937..bfa2b3664ae 100644 --- a/aws/resource_aws_ram_resource_share_accepter_test.go +++ b/aws/resource_aws_ram_resource_share_accepter_test.go @@ -78,6 +78,45 @@ func TestAccAwsRamResourceShareAccepter_disappears(t *testing.T) { }) } +func TestAccAwsRamResourceShareAccepter_resource_association(t *testing.T) { + var providers []*schema.Provider + resourceName := "aws_ram_resource_share_accepter.test" + principalAssociationResourceName := "aws_ram_principal_association.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccAlternateAccountPreCheck(t) + }, + ErrorCheck: testAccErrorCheck(t, ram.EndpointsID), + ProviderFactories: testAccProviderFactoriesAlternate(&providers), + CheckDestroy: testAccCheckAwsRamResourceShareAccepterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsRamResourceShareAccepterResourceAssociation(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsRamResourceShareAccepterExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "share_arn", principalAssociationResourceName, "resource_share_arn"), + testAccMatchResourceAttrRegionalARNAccountID(resourceName, "invitation_arn", "ram", `\d{12}`, regexp.MustCompile(fmt.Sprintf("resource-share-invitation/%s$", uuidRegexPattern))), + resource.TestMatchResourceAttr(resourceName, "share_id", regexp.MustCompile(fmt.Sprintf(`^rs-%s$`, uuidRegexPattern))), + resource.TestCheckResourceAttr(resourceName, "status", ram.ResourceShareStatusActive), + testAccCheckResourceAttrAccountID(resourceName, "receiver_account_id"), + resource.TestMatchResourceAttr(resourceName, "sender_account_id", regexp.MustCompile(`\d{12}`)), + resource.TestCheckResourceAttr(resourceName, "share_name", rName), + resource.TestCheckResourceAttr(resourceName, "resources.%", "0"), + ), + }, + { + Config: testAccAwsRamResourceShareAccepterResourceAssociation(rName), + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testAccCheckAwsRamResourceShareAccepterDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).ramconn @@ -158,3 +197,27 @@ resource "aws_ram_principal_association" "test" { data "aws_caller_identity" "receiver" {} `, rName) } + +func testAccAwsRamResourceShareAccepterResourceAssociation(rName string) string { + return composeConfig(testAccAwsRamResourceShareAccepterBasic(rName), fmt.Sprintf(` +resource "aws_ram_resource_association" "test" { + provider = "awsalternate" + resource_arn = aws_route53_resolver_query_log_config.test.arn + resource_share_arn = aws_ram_resource_share.test.arn +} + +resource "aws_route53_resolver_query_log_config" "test" { + provider = "awsalternate" + + name = %[1]q + destination_arn = aws_s3_bucket.test.arn +} + +resource "aws_s3_bucket" "test" { + bucket = %[1]q + provider = "awsalternate" + + force_destroy = true +} +`, rName)) +} From e0ad098b087c0c0d74eff259a4cef6d01d427707 Mon Sep 17 00:00:00 2001 From: Karri Balk Date: Tue, 8 Jun 2021 19:37:46 +0000 Subject: [PATCH 188/398] Add change log --- .changelog/19718.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/19718.txt diff --git a/.changelog/19718.txt b/.changelog/19718.txt new file mode 100644 index 00000000000..b2cf0a1874b --- /dev/null +++ b/.changelog/19718.txt @@ -0,0 +1,3 @@ +```release-note:bug +resource/aws_ram_resource_share_acceptor: Destroy aws_ram_resource_share_accepter from member account when share contains some resource types +``` From db357b0c598242416dcfffd55c78dc4f84355719 Mon Sep 17 00:00:00 2001 From: Karri Balk Date: Tue, 15 Jun 2021 23:13:19 +0000 Subject: [PATCH 189/398] Replace unit test with one that doesn't fail --- ...ce_aws_ram_resource_share_accepter_test.go | 73 ++++++++++++++++--- 1 file changed, 64 insertions(+), 9 deletions(-) diff --git a/aws/resource_aws_ram_resource_share_accepter_test.go b/aws/resource_aws_ram_resource_share_accepter_test.go index bfa2b3664ae..89b1cfa0430 100644 --- a/aws/resource_aws_ram_resource_share_accepter_test.go +++ b/aws/resource_aws_ram_resource_share_accepter_test.go @@ -202,22 +202,77 @@ func testAccAwsRamResourceShareAccepterResourceAssociation(rName string) string return composeConfig(testAccAwsRamResourceShareAccepterBasic(rName), fmt.Sprintf(` resource "aws_ram_resource_association" "test" { provider = "awsalternate" - resource_arn = aws_route53_resolver_query_log_config.test.arn + resource_arn = aws_codebuild_project.test.arn resource_share_arn = aws_ram_resource_share.test.arn } -resource "aws_route53_resolver_query_log_config" "test" { - provider = "awsalternate" +resource "aws_codebuild_project" "test" { + provider = "awsalternate" - name = %[1]q - destination_arn = aws_s3_bucket.test.arn + name = %[1]q + service_role = aws_iam_role.test.arn + + artifacts { + type = "NO_ARTIFACTS" + } + + environment { + compute_type = "BUILD_GENERAL1_SMALL" + image = "2" + type = "LINUX_CONTAINER" + } + + source { + type = "GITHUB" + location = "https://github.com/hashicorp/packer.git" + } } -resource "aws_s3_bucket" "test" { - bucket = %[1]q - provider = "awsalternate" +resource "aws_iam_role" "test" { + provider = "awsalternate" + + name = %[1]q + + assume_role_policy = <<-EOF + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "codebuild.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] + } + EOF +} + +resource "aws_iam_role_policy" "test" { + provider = "awsalternate" - force_destroy = true + name = %[1]q + role = aws_iam_role.test.name + + policy = <<-POLICY + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Resource": [ + "*" + ], + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ] + } + ] + } + POLICY } `, rName)) } From 70ef0217ddc2bcb06e5b3ef2332cc1569f5014b3 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 8 Jul 2021 09:14:51 -0400 Subject: [PATCH 190/398] Update changelog language --- .changelog/19718.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changelog/19718.txt b/.changelog/19718.txt index b2cf0a1874b..02c0d651b12 100644 --- a/.changelog/19718.txt +++ b/.changelog/19718.txt @@ -1,3 +1,3 @@ ```release-note:bug -resource/aws_ram_resource_share_acceptor: Destroy aws_ram_resource_share_accepter from member account when share contains some resource types +resource/aws_ram_resource_share_acceptor: Allow destroy even where AWS API provides no way to disassociate ``` From f1e5959cd6ac00c1deeb427f3e898d1eb016641d Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 8 Jul 2021 09:15:47 -0400 Subject: [PATCH 191/398] r/ram_resource_share_accepter: Delete succeed with one type of error --- aws/resource_aws_ram_resource_share_accepter.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/aws/resource_aws_ram_resource_share_accepter.go b/aws/resource_aws_ram_resource_share_accepter.go index dd8594ece24..75297d8f693 100644 --- a/aws/resource_aws_ram_resource_share_accepter.go +++ b/aws/resource_aws_ram_resource_share_accepter.go @@ -207,12 +207,16 @@ func resourceAwsRamResourceShareAccepterDelete(d *schema.ResourceData, meta inte _, err := conn.DisassociateResourceShare(input) + if tfawserr.ErrCodeEquals(err, ram.ErrCodeOperationNotPermittedException) { + log.Printf("[WARN] Resource share could not be disassociated, but continuing: %s", err) + } + if err != nil && !tfawserr.ErrCodeEquals(err, ram.ErrCodeOperationNotPermittedException) { return fmt.Errorf("Error leaving RAM resource share: %s", err) - } _, err = waiter.ResourceShareOwnedBySelfDisassociated(conn, d.Id(), d.Timeout(schema.TimeoutDelete)) + if err != nil { return fmt.Errorf("Error waiting for RAM resource share (%s) state: %s", d.Id(), err) } From 3bd16ce334bdf8341555064505aae86f27fc89d6 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 8 Jul 2021 09:16:27 -0400 Subject: [PATCH 192/398] tests/r/ram_resource_share_accepter: Clean up tests --- ...ce_aws_ram_resource_share_accepter_test.go | 68 ++++++++----------- 1 file changed, 30 insertions(+), 38 deletions(-) diff --git a/aws/resource_aws_ram_resource_share_accepter_test.go b/aws/resource_aws_ram_resource_share_accepter_test.go index 89b1cfa0430..34695af140e 100644 --- a/aws/resource_aws_ram_resource_share_accepter_test.go +++ b/aws/resource_aws_ram_resource_share_accepter_test.go @@ -78,7 +78,7 @@ func TestAccAwsRamResourceShareAccepter_disappears(t *testing.T) { }) } -func TestAccAwsRamResourceShareAccepter_resource_association(t *testing.T) { +func TestAccAwsRamResourceShareAccepter_resourceAssociation(t *testing.T) { var providers []*schema.Provider resourceName := "aws_ram_resource_share_accepter.test" principalAssociationResourceName := "aws_ram_principal_association.test" @@ -207,7 +207,7 @@ resource "aws_ram_resource_association" "test" { } resource "aws_codebuild_project" "test" { - provider = "awsalternate" + provider = "awsalternate" name = %[1]q service_role = aws_iam_role.test.arn @@ -228,51 +228,43 @@ resource "aws_codebuild_project" "test" { } } +data "aws_partition" "current" {} + resource "aws_iam_role" "test" { provider = "awsalternate" - name = %[1]q - - assume_role_policy = <<-EOF - { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": { - "Service": "codebuild.amazonaws.com" - }, - "Action": "sts:AssumeRole" - } - ] - } - EOF + name = %[1]q + + assume_role_policy = jsonencode({ + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "codebuild.${data.aws_partition.current.dns_suffix}" + } + }] + Version = "2012-10-17" + }) } resource "aws_iam_role_policy" "test" { provider = "awsalternate" - name = %[1]q - role = aws_iam_role.test.name - - policy = <<-POLICY - { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Resource": [ - "*" - ], - "Action": [ - "logs:CreateLogGroup", - "logs:CreateLogStream", - "logs:PutLogEvents" - ] - } + name = %[1]q + role = aws_iam_role.test.name + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Effect = "Allow" + Resource = ["*"] + Action = [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" ] - } - POLICY + }] + }) } `, rName)) } From 6265cf54561953d8bff7898be6fdefc62d16ba8d Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 8 Jul 2021 09:17:58 -0400 Subject: [PATCH 193/398] tests/r/ram_resource_share_accepter: Clean up tests --- aws/resource_aws_ram_resource_share_accepter_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aws/resource_aws_ram_resource_share_accepter_test.go b/aws/resource_aws_ram_resource_share_accepter_test.go index 34695af140e..666be5a8c2c 100644 --- a/aws/resource_aws_ram_resource_share_accepter_test.go +++ b/aws/resource_aws_ram_resource_share_accepter_test.go @@ -201,7 +201,8 @@ data "aws_caller_identity" "receiver" {} func testAccAwsRamResourceShareAccepterResourceAssociation(rName string) string { return composeConfig(testAccAwsRamResourceShareAccepterBasic(rName), fmt.Sprintf(` resource "aws_ram_resource_association" "test" { - provider = "awsalternate" + provider = "awsalternate" + resource_arn = aws_codebuild_project.test.arn resource_share_arn = aws_ram_resource_share.test.arn } From 5f10edcaae4459bb76084eeda4b1a552c650eb0b Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 8 Jul 2021 09:20:04 -0400 Subject: [PATCH 194/398] tests/r/ram_resource_share_accepter: Appease linter overlord --- aws/resource_aws_ram_resource_share_accepter_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/aws/resource_aws_ram_resource_share_accepter_test.go b/aws/resource_aws_ram_resource_share_accepter_test.go index 666be5a8c2c..3b30df4fdba 100644 --- a/aws/resource_aws_ram_resource_share_accepter_test.go +++ b/aws/resource_aws_ram_resource_share_accepter_test.go @@ -255,14 +255,14 @@ resource "aws_iam_role_policy" "test" { role = aws_iam_role.test.name policy = jsonencode({ - Version = "2012-10-17" + Version = "2012-10-17" Statement = [{ Effect = "Allow" Resource = ["*"] Action = [ - "logs:CreateLogGroup", - "logs:CreateLogStream", - "logs:PutLogEvents" + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" ] }] }) From cc094523ef5de16c899080a04cb6d9100412dc71 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 8 Jul 2021 10:50:20 -0400 Subject: [PATCH 195/398] r/aws_dx_gateway_association_proposal: Ensure that any pseudo-proposal ID is not blank. --- .../service/directconnect/finder/finder.go | 20 +- aws/resource_aws_dx_gateway_association.go | 17 +- ...rce_aws_dx_gateway_association_proposal.go | 310 +++++------------- ...ws_dx_gateway_association_proposal_test.go | 15 +- ...gateway_association_proposal.html.markdown | 1 + 5 files changed, 124 insertions(+), 239 deletions(-) diff --git a/aws/internal/service/directconnect/finder/finder.go b/aws/internal/service/directconnect/finder/finder.go index 9bcf768f73e..9a97f05d34d 100644 --- a/aws/internal/service/directconnect/finder/finder.go +++ b/aws/internal/service/directconnect/finder/finder.go @@ -40,16 +40,23 @@ func GatewayAssociation(conn *directconnect.DirectConnect, input *directconnect. // TODO Check for multiple results. // TODO https://github.com/hashicorp/terraform-provider-aws/pull/17613. - gatewayAssociation := output.DirectConnectGatewayAssociations[0] + association := output.DirectConnectGatewayAssociations[0] - if state := aws.StringValue(gatewayAssociation.AssociationState); state == directconnect.GatewayAssociationStateDisassociated { + if state := aws.StringValue(association.AssociationState); state == directconnect.GatewayAssociationStateDisassociated { return nil, &resource.NotFoundError{ Message: state, LastRequest: input, } } - return gatewayAssociation, nil + if association.AssociatedGateway == nil { + return nil, &resource.NotFoundError{ + Message: "Empty AssociatedGateway", + LastRequest: input, + } + } + + return association, nil } func GatewayAssociationProposalByID(conn *directconnect.DirectConnect, id string) (*directconnect.GatewayAssociationProposal, error) { @@ -82,5 +89,12 @@ func GatewayAssociationProposalByID(conn *directconnect.DirectConnect, id string } } + if proposal.AssociatedGateway == nil { + return nil, &resource.NotFoundError{ + Message: "Empty AssociatedGateway", + LastRequest: input, + } + } + return proposal, nil } diff --git a/aws/resource_aws_dx_gateway_association.go b/aws/resource_aws_dx_gateway_association.go index a574640275e..f9f6ea4d1b5 100644 --- a/aws/resource_aws_dx_gateway_association.go +++ b/aws/resource_aws_dx_gateway_association.go @@ -8,6 +8,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/directconnect" + "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" ) @@ -233,21 +234,23 @@ func resourceAwsDxGatewayAssociationUpdate(d *schema.ResourceData, meta interfac func resourceAwsDxGatewayAssociationDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).dxconn - associationId := d.Get("dx_gateway_association_id").(string) + associationID := d.Get("dx_gateway_association_id").(string) - log.Printf("[DEBUG] Deleting Direct Connect gateway association: %s", d.Id()) + log.Printf("[DEBUG] Deleting Direct Connect Gateway Association: %s", d.Id()) _, err := conn.DeleteDirectConnectGatewayAssociation(&directconnect.DeleteDirectConnectGatewayAssociationInput{ - AssociationId: aws.String(associationId), + AssociationId: aws.String(associationID), }) - if isAWSErr(err, directconnect.ErrCodeClientException, "No association exists") { + + if tfawserr.ErrMessageContains(err, directconnect.ErrCodeClientException, "does not exist") { return nil } + if err != nil { - return fmt.Errorf("error deleting Direct Connect gateway association: %s", err) + return fmt.Errorf("error deleting Direct Connect Gateway Association (%s): %w", d.Id(), err) } - if err := waitForDirectConnectGatewayAssociationDeletion(conn, associationId, d.Timeout(schema.TimeoutDelete)); err != nil { - return fmt.Errorf("error waiting for Direct Connect gateway association (%s) to be deleted: %s", d.Id(), err) + if err := waitForDirectConnectGatewayAssociationDeletion(conn, associationID, d.Timeout(schema.TimeoutDelete)); err != nil { + return fmt.Errorf("error waiting for Direct Connect Gateway Association (%s) to delete: %w", d.Id(), err) } return nil diff --git a/aws/resource_aws_dx_gateway_association_proposal.go b/aws/resource_aws_dx_gateway_association_proposal.go index 6218cde43ea..b5fc3d40997 100644 --- a/aws/resource_aws_dx_gateway_association_proposal.go +++ b/aws/resource_aws_dx_gateway_association_proposal.go @@ -8,8 +8,8 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/directconnect/finder" "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" @@ -29,30 +29,23 @@ func resourceAwsDxGatewayAssociationProposal() *schema.Resource { // Accepting the proposal with overridden prefixes changes the returned RequestedAllowedPrefixesToDirectConnectGateway value (allowed_prefixes attribute). // We only want to force a new resource if this value changes and the current proposal state is "requested". customdiff.ForceNewIf("allowed_prefixes", func(_ context.Context, d *schema.ResourceDiff, meta interface{}) bool { + conn := meta.(*AWSClient).dxconn + + log.Printf("[DEBUG] CustomizeDiff for Direct Connect Gateway Association Proposal (%s) allowed_prefixes", d.Id()) - log.Printf("[DEBUG] Checking diff for Direct Connect Gateway Association Proposal (%s) allowed_prefixes", d.Id()) + output, err := finder.GatewayAssociationProposalByID(conn, d.Id()) - if len(strings.Join(strings.Fields(d.Id()), "")) < 1 { - log.Printf("[WARN] Direct Connect Gateway Association Proposal Id not available (%s)", d.Id()) - log.Printf("[DEBUG] Direct Connect Gateway Association Proposal UpdatedKeys (%s)", strings.Join(d.UpdatedKeys(), "/")) - // assume proposal is end-of-life, rely on Read func to test + if tfresource.NotFound(err) { + // Proposal may be end-of-life and removed by AWS. return false } - conn := meta.(*AWSClient).dxconn - - proposal, err := describeDirectConnectGatewayAssociationProposal(conn, d.Id()) if err != nil { log.Printf("[ERROR] Error reading Direct Connect Gateway Association Proposal (%s): %s", d.Id(), err) return false } - if proposal == nil { - // proposal maybe end-of-life and removed by AWS, existence checked in Read func - return false - } - - return aws.StringValue(proposal.ProposalState) == directconnect.GatewayAssociationProposalStateRequested + return aws.StringValue(output.ProposalState) == directconnect.GatewayAssociationProposalStateRequested }), ), @@ -63,24 +56,29 @@ func resourceAwsDxGatewayAssociationProposal() *schema.Resource { Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, + "associated_gateway_id": { Type: schema.TypeString, Required: true, ForceNew: true, }, + "associated_gateway_owner_account_id": { Type: schema.TypeString, Computed: true, }, + "associated_gateway_type": { Type: schema.TypeString, Computed: true, }, + "dx_gateway_id": { Type: schema.TypeString, Required: true, ForceNew: true, }, + "dx_gateway_owner_account_id": { Type: schema.TypeString, Required: true, @@ -94,19 +92,23 @@ func resourceAwsDxGatewayAssociationProposal() *schema.Resource { func resourceAwsDxGatewayAssociationProposalCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).dxconn - allowedPrefixes := expandDirectConnectGatewayAssociationProposalAllowedPrefixes(d.Get("allowed_prefixes").(*schema.Set).List()) + directConnectGatewayID := d.Get("dx_gateway_id").(string) + associatedGatewayID := d.Get("associated_gateway_id").(string) input := &directconnect.CreateDirectConnectGatewayAssociationProposalInput{ - AddAllowedPrefixesToDirectConnectGateway: allowedPrefixes, - DirectConnectGatewayId: aws.String(d.Get("dx_gateway_id").(string)), - DirectConnectGatewayOwnerAccount: aws.String(d.Get("dx_gateway_owner_account_id").(string)), - GatewayId: aws.String(d.Get("associated_gateway_id").(string)), + DirectConnectGatewayId: aws.String(directConnectGatewayID), + DirectConnectGatewayOwnerAccount: aws.String(d.Get("dx_gateway_owner_account_id").(string)), + GatewayId: aws.String(associatedGatewayID), + } + + if v, ok := d.GetOk("allowed_prefixes"); ok && v.(*schema.Set).Len() > 0 { + input.AddAllowedPrefixesToDirectConnectGateway = expandDirectConnectRouteFilterPrefixes(v.(*schema.Set).List()) } log.Printf("[DEBUG] Creating Direct Connect Gateway Association Proposal: %s", input) output, err := conn.CreateDirectConnectGatewayAssociationProposal(input) if err != nil { - return fmt.Errorf("error creating Direct Connect Gateway Association Proposal: %s", err) + return fmt.Errorf("error creating Direct Connect Gateway Association Proposal (%s/%s): %w", directConnectGatewayID, associatedGatewayID, err) } d.SetId(aws.StringValue(output.DirectConnectGatewayAssociationProposal.ProposalId)) @@ -115,274 +117,142 @@ func resourceAwsDxGatewayAssociationProposalCreate(d *schema.ResourceData, meta } func resourceAwsDxGatewayAssociationProposalRead(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Read Direct Connect Gateway Association Proposal: %s", d.Id()) - - var proposal *directconnect.GatewayAssociationProposal - conn := meta.(*AWSClient).dxconn - trimmedId := strings.Join(strings.Fields(d.Id()), "") - if len(trimmedId) > 0 { - var err error - proposal, err = describeDirectConnectGatewayAssociationProposal(conn, d.Id()) + // First attempt to find by proposal ID. + output, err := finder.GatewayAssociationProposalByID(conn, d.Id()) - if err != nil { - return fmt.Errorf("error reading Direct Connect Gateway Association Proposal (%s): %s", d.Id(), err) - } - } else { - log.Printf("[WARN] Direct Connect Gateway Association Proposal Id not available (%s)", d.Id()) - } - - if proposal == nil || len(trimmedId) < 1 { - log.Printf("[WARN] Direct Connect Gateway Association Proposal (%s) not found, checking for associated gateway", d.Id()) + if tfresource.NotFound(err) { + // Attempt to find an existing association. + directConnectGatewayID := d.Get("dx_gateway_id").(string) + associatedGatewayID := d.Get("associated_gateway_id").(string) - var dxGatewayId string - if rawDGId, ok := d.GetOk("dx_gateway_id"); ok { - dxGatewayId = rawDGId.(string) - } else if rawDGId == nil { - d.SetId("") - return fmt.Errorf("error reading dx_gateway_id (%s) from Proposal state", d.Id()) - } + output, err := finder.GatewayAssociationByDirectConnectGatewayIDAndAssociatedGatewayID(conn, directConnectGatewayID, associatedGatewayID) - var associatedGatewayId string - if rawAGId, ok := d.GetOk("associated_gateway_id"); ok { - associatedGatewayId = rawAGId.(string) - } else if rawAGId == nil { + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] Direct Connect Gateway Association Proposal (%s) not found, removing from state", d.Id()) d.SetId("") - return fmt.Errorf("error reading associated_gateway_id (%s) from Proposal state", d.Id()) + return nil } - log.Printf("[DEBUG] looking for Direct Connect Gateway Association using dx_gateway_id (%s) and associated_gateway_id (%s) to validate Proposal state data", dxGatewayId, associatedGatewayId) - assocRaw, state, err := getDxGatewayAssociation(conn, dxGatewayId, associatedGatewayId)() - if err != nil { - d.SetId("") - return fmt.Errorf("error reading Direct Connect gateway association (%s) from Proposal state: %s", d.Id(), err) - } - - if state == gatewayAssociationStateDeleted { - log.Printf("[WARN] Direct Connect gateway association (%s/%s/%s) not found, removing from state", d.Id(), dxGatewayId, associatedGatewayId) - d.SetId("") - return nil + return fmt.Errorf("error reading Direct Connect Gateway Association (%s/%s): %w", directConnectGatewayID, associatedGatewayID, err) } - // once accepted, AWS will delete the proposal after after some time (days?) - // in this case we don't need to create a new proposal, use metadata from the association + // Once accepted, AWS will delete the proposal after after some time (days?). + // In this case we don't need to create a new proposal, use metadata from the association // to artificially populate the missing proposal in state as if it was still there. - log.Printf("[INFO] Direct Connect Gateway Association Proposal (%s) has reached end-of-life and has been removed by AWS.", d.Id()) - assoc := assocRaw.(*directconnect.GatewayAssociation) + log.Printf("[INFO] Direct Connect Gateway Association Proposal (%s) has reached end-of-life and has been removed by AWS", d.Id()) - err = d.Set("allowed_prefixes", flattenDxRouteFilterPrefixes(assoc.AllowedPrefixesToDirectConnectGateway)) - if err != nil { - return fmt.Errorf("error setting allowed_prefixes: %s", err) + if err := d.Set("allowed_prefixes", flattenDirectConnectRouteFilterPrefixes(output.AllowedPrefixesToDirectConnectGateway)); err != nil { + return fmt.Errorf("error setting allowed_prefixes: %w", err) } - d.Set("associated_gateway_id", assoc.AssociatedGateway.Id) - d.Set("dx_gateway_id", assoc.DirectConnectGatewayId) - d.Set("dx_gateway_owner_account_id", assoc.DirectConnectGatewayOwnerAccount) + d.Set("associated_gateway_id", output.AssociatedGateway.Id) + d.Set("associated_gateway_owner_account_id", output.AssociatedGateway.OwnerAccount) + d.Set("associated_gateway_type", output.AssociatedGateway.Type) + d.Set("dx_gateway_id", output.DirectConnectGatewayId) + d.Set("dx_gateway_owner_account_id", output.DirectConnectGatewayOwnerAccount) + } else if err != nil { + return fmt.Errorf("error reading Direct Connect Gateway Association Proposal (%s): %w", d.Id(), err) } else { - log.Printf("[DEBUG] Direct Connect Gateway Association Proposal (%s) found, continuing as normal: %s", d.Id(), proposal.String()) - - if aws.StringValue(proposal.ProposalState) == directconnect.GatewayAssociationProposalStateDeleted { - log.Printf("[WARN] Direct Connect Gateway Association Proposal (%s) deleted, removing from state", d.Id()) - d.SetId("") - return nil + if err := d.Set("allowed_prefixes", flattenDirectConnectRouteFilterPrefixes(output.RequestedAllowedPrefixesToDirectConnectGateway)); err != nil { + return fmt.Errorf("error setting allowed_prefixes: %w", err) } - if proposal.AssociatedGateway == nil { - return fmt.Errorf("error reading Direct Connect Gateway Association Proposal (%s): missing associated gateway information", d.Id()) - } - - if err := d.Set("allowed_prefixes", flattenDirectConnectGatewayAssociationProposalAllowedPrefixes(proposal.RequestedAllowedPrefixesToDirectConnectGateway)); err != nil { - return fmt.Errorf("error setting allowed_prefixes: %s", err) - } - - d.Set("associated_gateway_id", proposal.AssociatedGateway.Id) - d.Set("associated_gateway_owner_account_id", proposal.AssociatedGateway.OwnerAccount) - d.Set("associated_gateway_type", proposal.AssociatedGateway.Type) - d.Set("dx_gateway_id", proposal.DirectConnectGatewayId) - d.Set("dx_gateway_owner_account_id", proposal.DirectConnectGatewayOwnerAccount) - + d.Set("associated_gateway_id", output.AssociatedGateway.Id) + d.Set("associated_gateway_owner_account_id", output.AssociatedGateway.OwnerAccount) + d.Set("associated_gateway_type", output.AssociatedGateway.Type) + d.Set("dx_gateway_id", output.DirectConnectGatewayId) + d.Set("dx_gateway_owner_account_id", output.DirectConnectGatewayOwnerAccount) } + return nil } func resourceAwsDxGatewayAssociationProposalDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).dxconn - input := &directconnect.DeleteDirectConnectGatewayAssociationProposalInput{ - ProposalId: aws.String(d.Id()), - } - log.Printf("[DEBUG] Deleting Direct Connect Gateway Association Proposal: %s", d.Id()) + _, err := conn.DeleteDirectConnectGatewayAssociationProposal(&directconnect.DeleteDirectConnectGatewayAssociationProposalInput{ + ProposalId: aws.String(d.Id()), + }) - _, err := conn.DeleteDirectConnectGatewayAssociationProposal(input) + if tfawserr.ErrMessageContains(err, directconnect.ErrCodeClientException, "is not found") { + return nil + } if err != nil { - return fmt.Errorf("error deleting Direct Connect Gateway Association Proposal (%s): %s", d.Id(), err) + return fmt.Errorf("error deleting Direct Connect Gateway Association Proposal (%s): %w", d.Id(), err) } return nil } -func describeDirectConnectGatewayAssociationProposal(conn *directconnect.DirectConnect, proposalID string) (*directconnect.GatewayAssociationProposal, error) { - input := &directconnect.DescribeDirectConnectGatewayAssociationProposalsInput{ - ProposalId: aws.String(proposalID), +func expandDirectConnectRouteFilterPrefixes(tfList []interface{}) []*directconnect.RouteFilterPrefix { + if len(tfList) == 0 { + return nil } - for { - output, err := conn.DescribeDirectConnectGatewayAssociationProposals(input) + var apiObjects []*directconnect.RouteFilterPrefix - if err != nil { - return nil, err - } + for _, tfStringRaw := range tfList { + tfString, ok := tfStringRaw.(string) - if output == nil { + if !ok { continue } - for _, proposal := range output.DirectConnectGatewayAssociationProposals { - if aws.StringValue(proposal.ProposalId) == proposalID { - return proposal, nil - } + apiObject := &directconnect.RouteFilterPrefix{ + Cidr: aws.String(tfString), } - if aws.StringValue(output.NextToken) == "" { - break - } - - input.NextToken = output.NextToken + apiObjects = append(apiObjects, apiObject) } - return nil, nil + return apiObjects } -func expandDirectConnectGatewayAssociationProposalAllowedPrefixes(allowedPrefixes []interface{}) []*directconnect.RouteFilterPrefix { - if len(allowedPrefixes) == 0 { +func flattenDirectConnectRouteFilterPrefixes(apiObjects []*directconnect.RouteFilterPrefix) []interface{} { + if len(apiObjects) == 0 { return nil } - var routeFilterPrefixes []*directconnect.RouteFilterPrefix + var tfList []interface{} - for _, allowedPrefixRaw := range allowedPrefixes { - if allowedPrefixRaw == nil { + for _, apiObject := range apiObjects { + if apiObject == nil { continue } - routeFilterPrefix := &directconnect.RouteFilterPrefix{ - Cidr: aws.String(allowedPrefixRaw.(string)), - } - - routeFilterPrefixes = append(routeFilterPrefixes, routeFilterPrefix) + tfList = append(tfList, aws.StringValue(apiObject.Cidr)) } - return routeFilterPrefixes -} - -func flattenDirectConnectGatewayAssociationProposalAllowedPrefixes(routeFilterPrefixes []*directconnect.RouteFilterPrefix) []interface{} { - if len(routeFilterPrefixes) == 0 { - return []interface{}{} - } - - var allowedPrefixes []interface{} - - for _, routeFilterPrefix := range routeFilterPrefixes { - if routeFilterPrefix == nil { - continue - } - - allowedPrefix := aws.StringValue(routeFilterPrefix.Cidr) - - allowedPrefixes = append(allowedPrefixes, allowedPrefix) - } - - return allowedPrefixes + return tfList } func resourceAwsDxGatewayAssociationProposalImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - parts := strings.Split(strings.ToLower(d.Id()), "/") - - var proposalID, directConnectGatewayID, associatedGatewayID string - switch n := len(parts); n { + switch parts := strings.Split(strings.ToLower(d.Id()), "/"); len(parts) { case 1: - return []*schema.ResourceData{d}, nil + break case 3: - proposalID = parts[0] - directConnectGatewayID = parts[1] - associatedGatewayID = parts[2] + proposalID := parts[0] + directConnectGatewayID := parts[1] + associatedGatewayID := parts[2] - if directConnectGatewayID == "" || associatedGatewayID == "" { - return nil, fmt.Errorf("Incorrect resource ID format: %q. DXGATEWAYID and TARGETGATEWAYID must not be empty strings", d.Id()) + if proposalID == "" || directConnectGatewayID == "" || associatedGatewayID == "" { + return nil, fmt.Errorf("Incorrect resource ID format: %q. PROPOSALID, DXGATEWAYID and TARGETGATEWAYID must not be empty strings", d.Id()) } - break + // Use pseudo-proposal ID and actual DirectConnectGatewayId and AssociatedGatewayId. + d.SetId(proposalID) + d.Set("associated_gateway_id", associatedGatewayID) + d.Set("dx_gateway_id", directConnectGatewayID) default: return nil, fmt.Errorf("Incorrect resource ID format: %q. Expected PROPOSALID or PROPOSALID/DXGATEWAYID/TARGETGATEWAYID", d.Id()) } - conn := meta.(*AWSClient).dxconn - - if proposalID != "" { - _, err := finder.GatewayAssociationProposalByID(conn, proposalID) - - if tfresource.NotFound(err) { - // Proposal not found. - } else if err != nil { - return nil, err - } else { - // Proposal still exists. - d.SetId(proposalID) - - return []*schema.ResourceData{d}, nil - } - } - - _, err := finder.GatewayAssociationByDirectConnectGatewayIDAndAssociatedGatewayID(conn, directConnectGatewayID, associatedGatewayID) - - if err != nil { - return nil, err - } - - d.SetId(proposalID) - d.Set("associated_gateway_id", associatedGatewayID) - d.Set("dx_gateway_id", directConnectGatewayID) - return []*schema.ResourceData{d}, nil } - -func getDxGatewayAssociation(conn *directconnect.DirectConnect, dxGatewayId, associatedGatewayId string) resource.StateRefreshFunc { - return func() (interface{}, string, error) { - - resp, err := conn.DescribeDirectConnectGatewayAssociations(&directconnect.DescribeDirectConnectGatewayAssociationsInput{ - AssociatedGatewayId: &associatedGatewayId, - DirectConnectGatewayId: &dxGatewayId, - }) - - if err != nil { - return nil, "", err - } - - n := len(resp.DirectConnectGatewayAssociations) - switch n { - case 0: - return "", gatewayAssociationStateDeleted, nil - - case 1: - assoc := resp.DirectConnectGatewayAssociations[0] - - if stateChangeError := aws.StringValue(assoc.StateChangeError); stateChangeError != "" { - id := dxGatewayAssociationId( - aws.StringValue(resp.DirectConnectGatewayAssociations[0].DirectConnectGatewayId), - aws.StringValue(resp.DirectConnectGatewayAssociations[0].AssociatedGateway.Id)) - log.Printf("[INFO] Direct Connect gateway association (%s) state change error: %s", id, stateChangeError) - } - - return assoc, aws.StringValue(assoc.AssociationState), nil - - default: - return nil, "", fmt.Errorf("Found %d Direct Connect gateway associations for %s, expected 1", n, associatedGatewayId) - } - } -} diff --git a/aws/resource_aws_dx_gateway_association_proposal_test.go b/aws/resource_aws_dx_gateway_association_proposal_test.go index 2f30084a2c0..9e17079a0b1 100644 --- a/aws/resource_aws_dx_gateway_association_proposal_test.go +++ b/aws/resource_aws_dx_gateway_association_proposal_test.go @@ -373,10 +373,10 @@ func testAccCheckAwsDxGatewayAssociationProposalDisappears(proposal *directconne } } -func testAccCheckAwsDxGatewayAssociationProposalRecreated(i, j *directconnect.GatewayAssociationProposal) resource.TestCheckFunc { +func testAccCheckAwsDxGatewayAssociationProposalRecreated(old, new *directconnect.GatewayAssociationProposal) resource.TestCheckFunc { return func(s *terraform.State) error { - if aws.StringValue(i.ProposalId) == aws.StringValue(j.ProposalId) { - return fmt.Errorf("Direct Connect Gateway Association Proposal not recreated") + if old, new := aws.StringValue(old.ProposalId), aws.StringValue(new.ProposalId); old == new { + return fmt.Errorf("Direct Connect Gateway Association Proposal (%s) not recreated", old) } return nil @@ -392,19 +392,16 @@ func testAccCheckAwsDxGatewayAssociationProposalAccepted(resourceName string) re conn := testAccProvider.Meta().(*AWSClient).dxconn - proposal, err := describeDirectConnectGatewayAssociationProposal(conn, rs.Primary.ID) + output, err := finder.GatewayAssociationProposalByID(conn, rs.Primary.ID) if err != nil { return err } - if proposal == nil { - return fmt.Errorf("Direct Connect Gateway Association Proposal (%s) not found", rs.Primary.ID) - } - - if aws.StringValue(proposal.ProposalState) != "accepted" { + if aws.StringValue(output.ProposalState) != directconnect.GatewayAssociationProposalStateAccepted { return fmt.Errorf("Direct Connect Gateway Association Proposal (%s) not accepted", rs.Primary.ID) } + return nil } } diff --git a/website/docs/r/dx_gateway_association_proposal.html.markdown b/website/docs/r/dx_gateway_association_proposal.html.markdown index 5701772d2b4..74ce04dc4a8 100644 --- a/website/docs/r/dx_gateway_association_proposal.html.markdown +++ b/website/docs/r/dx_gateway_association_proposal.html.markdown @@ -55,3 +55,4 @@ $ terraform import aws_dx_gateway_association_proposal.example ac90e981-b718-436 The latter case is useful when a previous proposal has been accepted and deleted by AWS. The `aws_dx_gateway_association_proposal` resource will then represent a pseudo-proposal for the same Direct Connect Gateway and associated gateway. +If no previous proposal is available, use a tool like [`uuidgen`](http://manpages.ubuntu.com/manpages/bionic/man1/uuidgen.1.html) to generate a new random pseudo-proposal ID. From 50910413e150f74fcb2f78c1a1b9bef3616f710f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 8 Jul 2021 11:14:15 -0400 Subject: [PATCH 196/398] Tweak acceptance test configurations. --- ...ws_dx_gateway_association_proposal_test.go | 126 +++++++----------- ...esource_aws_dx_gateway_association_test.go | 22 +-- 2 files changed, 53 insertions(+), 95 deletions(-) diff --git a/aws/resource_aws_dx_gateway_association_proposal_test.go b/aws/resource_aws_dx_gateway_association_proposal_test.go index 9e17079a0b1..de71096ffd7 100644 --- a/aws/resource_aws_dx_gateway_association_proposal_test.go +++ b/aws/resource_aws_dx_gateway_association_proposal_test.go @@ -82,7 +82,7 @@ func testSweepDirectConnectGatewayAssociationProposals(region string) error { } func TestAccAwsDxGatewayAssociationProposal_basicVpnGateway(t *testing.T) { - var proposal1 directconnect.GatewayAssociationProposal + var proposal directconnect.GatewayAssociationProposal var providers []*schema.Provider rBgpAsn := acctest.RandIntRange(64512, 65534) rName := acctest.RandomWithPrefix("tf-acc-test") @@ -91,10 +91,7 @@ func TestAccAwsDxGatewayAssociationProposal_basicVpnGateway(t *testing.T) { resourceNameVgw := "aws_vpn_gateway.test" resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { - testAccPreCheck(t) - testAccAlternateAccountPreCheck(t) - }, + PreCheck: func() { testAccPreCheck(t); testAccAlternateAccountPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, directconnect.EndpointsID), ProviderFactories: testAccProviderFactoriesAlternate(&providers), CheckDestroy: testAccCheckAwsDxGatewayAssociationProposalDestroy, @@ -102,12 +99,12 @@ func TestAccAwsDxGatewayAssociationProposal_basicVpnGateway(t *testing.T) { { Config: testAccDxGatewayAssociationProposalConfig_basicVpnGateway(rName, rBgpAsn), Check: resource.ComposeTestCheckFunc( - testAccCheckAwsDxGatewayAssociationProposalExists(resourceName, &proposal1), - resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", resourceNameDxGw, "id"), + testAccCheckAwsDxGatewayAssociationProposalExists(resourceName, &proposal), + resource.TestCheckResourceAttr(resourceName, "allowed_prefixes.#", "1"), resource.TestCheckResourceAttrPair(resourceName, "associated_gateway_id", resourceNameVgw, "id"), testAccCheckResourceAttrAccountID(resourceName, "associated_gateway_owner_account_id"), resource.TestCheckResourceAttr(resourceName, "associated_gateway_type", "virtualPrivateGateway"), - resource.TestCheckResourceAttr(resourceName, "allowed_prefixes.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", resourceNameDxGw, "id"), ), }, { @@ -121,7 +118,7 @@ func TestAccAwsDxGatewayAssociationProposal_basicVpnGateway(t *testing.T) { } func TestAccAwsDxGatewayAssociationProposal_basicTransitGateway(t *testing.T) { - var proposal1 directconnect.GatewayAssociationProposal + var proposal directconnect.GatewayAssociationProposal var providers []*schema.Provider rBgpAsn := acctest.RandIntRange(64512, 65534) rName := acctest.RandomWithPrefix("tf-acc-test") @@ -130,10 +127,7 @@ func TestAccAwsDxGatewayAssociationProposal_basicTransitGateway(t *testing.T) { resourceNameTgw := "aws_ec2_transit_gateway.test" resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { - testAccPreCheck(t) - testAccAlternateAccountPreCheck(t) - }, + PreCheck: func() { testAccPreCheck(t); testAccAlternateAccountPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, directconnect.EndpointsID), ProviderFactories: testAccProviderFactoriesAlternate(&providers), CheckDestroy: testAccCheckAwsDxGatewayAssociationProposalDestroy, @@ -141,14 +135,14 @@ func TestAccAwsDxGatewayAssociationProposal_basicTransitGateway(t *testing.T) { { Config: testAccDxGatewayAssociationProposalConfig_basicTransitGateway(rName, rBgpAsn), Check: resource.ComposeTestCheckFunc( - testAccCheckAwsDxGatewayAssociationProposalExists(resourceName, &proposal1), - resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", resourceNameDxGw, "id"), - resource.TestCheckResourceAttrPair(resourceName, "associated_gateway_id", resourceNameTgw, "id"), - testAccCheckResourceAttrAccountID(resourceName, "associated_gateway_owner_account_id"), - resource.TestCheckResourceAttr(resourceName, "associated_gateway_type", "transitGateway"), + testAccCheckAwsDxGatewayAssociationProposalExists(resourceName, &proposal), resource.TestCheckResourceAttr(resourceName, "allowed_prefixes.#", "2"), resource.TestCheckTypeSetElemAttr(resourceName, "allowed_prefixes.*", "10.255.255.0/30"), resource.TestCheckTypeSetElemAttr(resourceName, "allowed_prefixes.*", "10.255.255.8/30"), + resource.TestCheckResourceAttrPair(resourceName, "associated_gateway_id", resourceNameTgw, "id"), + testAccCheckResourceAttrAccountID(resourceName, "associated_gateway_owner_account_id"), + resource.TestCheckResourceAttr(resourceName, "associated_gateway_type", "transitGateway"), + resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", resourceNameDxGw, "id"), ), }, { @@ -162,17 +156,14 @@ func TestAccAwsDxGatewayAssociationProposal_basicTransitGateway(t *testing.T) { } func TestAccAwsDxGatewayAssociationProposal_disappears(t *testing.T) { - var proposal1 directconnect.GatewayAssociationProposal + var proposal directconnect.GatewayAssociationProposal var providers []*schema.Provider rBgpAsn := acctest.RandIntRange(64512, 65534) rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_dx_gateway_association_proposal.test" resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { - testAccPreCheck(t) - testAccAlternateAccountPreCheck(t) - }, + PreCheck: func() { testAccPreCheck(t); testAccAlternateAccountPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, directconnect.EndpointsID), ProviderFactories: testAccProviderFactoriesAlternate(&providers), CheckDestroy: testAccCheckAwsDxGatewayAssociationProposalDestroy, @@ -180,8 +171,8 @@ func TestAccAwsDxGatewayAssociationProposal_disappears(t *testing.T) { { Config: testAccDxGatewayAssociationProposalConfig_basicVpnGateway(rName, rBgpAsn), Check: resource.ComposeTestCheckFunc( - testAccCheckAwsDxGatewayAssociationProposalExists(resourceName, &proposal1), - testAccCheckAwsDxGatewayAssociationProposalDisappears(&proposal1), + testAccCheckAwsDxGatewayAssociationProposalExists(resourceName, &proposal), + testAccCheckResourceDisappears(testAccProvider, resourceAwsDxGatewayAssociationProposal(), resourceName), ), ExpectNonEmptyPlan: true, }, @@ -190,17 +181,14 @@ func TestAccAwsDxGatewayAssociationProposal_disappears(t *testing.T) { } func TestAccAwsDxGatewayAssociationProposal_endOfLifeVpn(t *testing.T) { - var proposal1 directconnect.GatewayAssociationProposal + var proposal directconnect.GatewayAssociationProposal var providers []*schema.Provider rBgpAsn := acctest.RandIntRange(64512, 65534) rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_dx_gateway_association_proposal.test" resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { - testAccPreCheck(t) - testAccAlternateAccountPreCheck(t) - }, + PreCheck: func() { testAccPreCheck(t); testAccAlternateAccountPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, directconnect.EndpointsID), ProviderFactories: testAccProviderFactoriesAlternate(&providers), CheckDestroy: testAccCheckAwsDxGatewayAssociationProposalDestroy, @@ -208,39 +196,36 @@ func TestAccAwsDxGatewayAssociationProposal_endOfLifeVpn(t *testing.T) { { Config: testAccDxGatewayAssociationProposalConfig_endOfLifeVpn(rName, rBgpAsn), Check: resource.ComposeTestCheckFunc( - testAccCheckAwsDxGatewayAssociationProposalExists(resourceName, &proposal1), + testAccCheckAwsDxGatewayAssociationProposalExists(resourceName, &proposal), testAccCheckAwsDxGatewayAssociationProposalAccepted(resourceName), - testAccCheckAwsDxGatewayAssociationProposalDisappears(&proposal1), + testAccCheckResourceDisappears(testAccProvider, resourceAwsDxGatewayAssociationProposal(), resourceName), ), }, { ResourceName: resourceName, ImportStateIdFunc: func(s *terraform.State) (string, error) { return strings.Join([]string{ - aws.StringValue(proposal1.ProposalId), - aws.StringValue(proposal1.DirectConnectGatewayId), - aws.StringValue(proposal1.AssociatedGateway.Id), + aws.StringValue(proposal.ProposalId), + aws.StringValue(proposal.DirectConnectGatewayId), + aws.StringValue(proposal.AssociatedGateway.Id), }, "/"), nil }, ImportState: true, - ImportStateVerify: false, // proposal attributes not applicable when it does not exist + ImportStateVerify: true, }, }, }) } func TestAccAwsDxGatewayAssociationProposal_endOfLifeTgw(t *testing.T) { - var proposal1 directconnect.GatewayAssociationProposal + var proposal directconnect.GatewayAssociationProposal var providers []*schema.Provider rBgpAsn := acctest.RandIntRange(64512, 65534) rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_dx_gateway_association_proposal.test" resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { - testAccPreCheck(t) - testAccAlternateAccountPreCheck(t) - }, + PreCheck: func() { testAccPreCheck(t); testAccAlternateAccountPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, directconnect.EndpointsID), ProviderFactories: testAccProviderFactoriesAlternate(&providers), CheckDestroy: testAccCheckAwsDxGatewayAssociationProposalDestroy, @@ -248,22 +233,22 @@ func TestAccAwsDxGatewayAssociationProposal_endOfLifeTgw(t *testing.T) { { Config: testAccDxGatewayAssociationProposalConfig_endOfLifeTgw(rName, rBgpAsn), Check: resource.ComposeTestCheckFunc( - testAccCheckAwsDxGatewayAssociationProposalExists(resourceName, &proposal1), + testAccCheckAwsDxGatewayAssociationProposalExists(resourceName, &proposal), testAccCheckAwsDxGatewayAssociationProposalAccepted(resourceName), - testAccCheckAwsDxGatewayAssociationProposalDisappears(&proposal1), + testAccCheckResourceDisappears(testAccProvider, resourceAwsDxGatewayAssociationProposal(), resourceName), ), }, { ResourceName: resourceName, ImportStateIdFunc: func(s *terraform.State) (string, error) { return strings.Join([]string{ - aws.StringValue(proposal1.ProposalId), - aws.StringValue(proposal1.DirectConnectGatewayId), - aws.StringValue(proposal1.AssociatedGateway.Id), + aws.StringValue(proposal.ProposalId), + aws.StringValue(proposal.DirectConnectGatewayId), + aws.StringValue(proposal.AssociatedGateway.Id), }, "/"), nil }, ImportState: true, - ImportStateVerify: false, // proposal attributes not applicable when it does not exist + ImportStateVerify: true, }, }, }) @@ -277,10 +262,7 @@ func TestAccAwsDxGatewayAssociationProposal_AllowedPrefixes(t *testing.T) { resourceName := "aws_dx_gateway_association_proposal.test" resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { - testAccPreCheck(t) - testAccAlternateAccountPreCheck(t) - }, + PreCheck: func() { testAccPreCheck(t); testAccAlternateAccountPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, directconnect.EndpointsID), ProviderFactories: testAccProviderFactoriesAlternate(&providers), CheckDestroy: testAccCheckAwsDxGatewayAssociationProposalDestroy, @@ -359,20 +341,6 @@ func testAccCheckAwsDxGatewayAssociationProposalExists(resourceName string, gate } } -func testAccCheckAwsDxGatewayAssociationProposalDisappears(proposal *directconnect.GatewayAssociationProposal) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).dxconn - - input := &directconnect.DeleteDirectConnectGatewayAssociationProposalInput{ - ProposalId: proposal.ProposalId, - } - - _, err := conn.DeleteDirectConnectGatewayAssociationProposal(input) - - return err - } -} - func testAccCheckAwsDxGatewayAssociationProposalRecreated(old, new *directconnect.GatewayAssociationProposal) resource.TestCheckFunc { return func(s *terraform.State) error { if old, new := aws.StringValue(old.ProposalId), aws.StringValue(new.ProposalId); old == new { @@ -407,7 +375,7 @@ func testAccCheckAwsDxGatewayAssociationProposalAccepted(resourceName string) re } func testAccDxGatewayAssociationProposalConfigBase_vpnGateway(rName string, rBgpAsn int) string { - return testAccAlternateAccountProviderConfig() + fmt.Sprintf(` + return composeConfig(testAccAlternateAccountProviderConfig(), fmt.Sprintf(` resource "aws_dx_gateway" "test" { provider = "awsalternate" @@ -430,21 +398,21 @@ resource "aws_vpn_gateway" "test" { Name = %[1]q } } -`, rName, rBgpAsn) +`, rName, rBgpAsn)) } func testAccDxGatewayAssociationProposalConfig_basicVpnGateway(rName string, rBgpAsn int) string { - return testAccDxGatewayAssociationProposalConfigBase_vpnGateway(rName, rBgpAsn) + ` + return composeConfig(testAccDxGatewayAssociationProposalConfigBase_vpnGateway(rName, rBgpAsn), ` resource "aws_dx_gateway_association_proposal" "test" { dx_gateway_id = aws_dx_gateway.test.id dx_gateway_owner_account_id = aws_dx_gateway.test.owner_account_id associated_gateway_id = aws_vpn_gateway.test.id } -` +`) } func testAccDxGatewayAssociationProposalConfig_endOfLifeVpn(rName string, rBgpAsn int) string { - return testAccDxGatewayAssociationProposalConfig_basicVpnGateway(rName, rBgpAsn) + ` + return composeConfig(testAccDxGatewayAssociationProposalConfig_basicVpnGateway(rName, rBgpAsn), ` data "aws_caller_identity" "current" {} resource "aws_dx_gateway_association" "test" { @@ -454,11 +422,11 @@ proposal_id = aws_dx_gateway_association_proposal.test.i dx_gateway_id = aws_dx_gateway.test.id associated_gateway_owner_account_id = data.aws_caller_identity.current.account_id } -` +`) } func testAccDxGatewayAssociationProposalConfig_endOfLifeTgw(rName string, rBgpAsn int) string { - return testAccDxGatewayAssociationProposalConfig_basicTransitGateway(rName, rBgpAsn) + ` + return composeConfig(testAccDxGatewayAssociationProposalConfig_basicTransitGateway(rName, rBgpAsn), ` data "aws_caller_identity" "current" {} resource "aws_dx_gateway_association" "test" { @@ -468,11 +436,11 @@ proposal_id = aws_dx_gateway_association_proposal.test.i dx_gateway_id = aws_dx_gateway.test.id associated_gateway_owner_account_id = data.aws_caller_identity.current.account_id } -` +`) } func testAccDxGatewayAssociationProposalConfig_basicTransitGateway(rName string, rBgpAsn int) string { - return testAccAlternateAccountProviderConfig() + fmt.Sprintf(` + return composeConfig(testAccAlternateAccountProviderConfig(), fmt.Sprintf(` resource "aws_dx_gateway" "test" { provider = "awsalternate" @@ -496,27 +464,27 @@ resource "aws_dx_gateway_association_proposal" "test" { "10.255.255.8/30", ] } -`, rName, rBgpAsn) +`, rName, rBgpAsn)) } func testAccDxGatewayAssociationProposalConfigAllowedPrefixes1(rName string, rBgpAsn int) string { - return testAccDxGatewayAssociationProposalConfigBase_vpnGateway(rName, rBgpAsn) + ` + return composeConfig(testAccDxGatewayAssociationProposalConfigBase_vpnGateway(rName, rBgpAsn), ` resource "aws_dx_gateway_association_proposal" "test" { allowed_prefixes = ["10.0.0.0/16"] dx_gateway_id = aws_dx_gateway.test.id dx_gateway_owner_account_id = aws_dx_gateway.test.owner_account_id associated_gateway_id = aws_vpn_gateway.test.id } -` +`) } func testAccDxGatewayAssociationProposalConfigAllowedPrefixes2(rName string, rBgpAsn int) string { - return testAccDxGatewayAssociationProposalConfigBase_vpnGateway(rName, rBgpAsn) + ` + return composeConfig(testAccDxGatewayAssociationProposalConfigBase_vpnGateway(rName, rBgpAsn), ` resource "aws_dx_gateway_association_proposal" "test" { allowed_prefixes = ["10.0.0.0/24", "10.0.1.0/24"] dx_gateway_id = aws_dx_gateway.test.id dx_gateway_owner_account_id = aws_dx_gateway.test.owner_account_id associated_gateway_id = aws_vpn_gateway.test.id } -` +`) } diff --git a/aws/resource_aws_dx_gateway_association_test.go b/aws/resource_aws_dx_gateway_association_test.go index d85ed68d5ba..ff422981634 100644 --- a/aws/resource_aws_dx_gateway_association_test.go +++ b/aws/resource_aws_dx_gateway_association_test.go @@ -490,7 +490,7 @@ func TestAccAwsDxGatewayAssociation_recreateProposal(t *testing.T) { Config: testAccDxGatewayAssociationConfig_basicVpnGatewayCrossAccount(rName, rBgpAsn), Check: resource.ComposeTestCheckFunc( testAccCheckAwsDxGatewayAssociationExists(resourceName, &ga1, &gap1), - testAccCheckAwsDxGatewayAssociationProposalDisappears(&gap1), + testAccCheckResourceDisappears(testAccProvider, resourceAwsDxGatewayAssociationProposal(), resourceName), ), ExpectNonEmptyPlan: true, }, @@ -498,8 +498,8 @@ func TestAccAwsDxGatewayAssociation_recreateProposal(t *testing.T) { Config: testAccDxGatewayAssociationConfig_basicVpnGatewayCrossAccount(rName, rBgpAsn), Check: resource.ComposeTestCheckFunc( testAccCheckAwsDxGatewayAssociationExists(resourceName, &ga2, &gap2), - testAccCheckAwsDxGatewayAssociationSameAssociation(&ga1, &ga2), - testAccCheckAwsDxGatewayAssociationDifferentProposal(&gap1, &gap2), + testAccCheckAwsDxGatewayAssociationNotRecreated(&ga1, &ga2), + testAccCheckAwsDxGatewayAssociationProposalRecreated(&gap1, &gap2), ), }, }, @@ -575,20 +575,10 @@ func testAccCheckAwsDxGatewayAssociationExists(name string, ga *directconnect.Ga } } -func testAccCheckAwsDxGatewayAssociationSameAssociation(ga1, ga2 *directconnect.GatewayAssociation) resource.TestCheckFunc { +func testAccCheckAwsDxGatewayAssociationNotRecreated(old, new *directconnect.GatewayAssociation) resource.TestCheckFunc { return func(s *terraform.State) error { - if aws.StringValue(ga1.AssociationId) != aws.StringValue(ga2.AssociationId) { - return fmt.Errorf("Association IDs differ") - } - - return nil - } -} - -func testAccCheckAwsDxGatewayAssociationDifferentProposal(gap1, gap2 *directconnect.GatewayAssociationProposal) resource.TestCheckFunc { - return func(s *terraform.State) error { - if aws.StringValue(gap1.ProposalId) == aws.StringValue(gap2.ProposalId) { - return fmt.Errorf("Proposals IDs are equal") + if old, new := aws.StringValue(old.AssociationId), aws.StringValue(new.AssociationId); old == new { + return fmt.Errorf("Direct Connect Gateway Association (%s) recreated (%s)", old, new) } return nil From f06467088341e408a658903f6e000cac02e597c0 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 8 Jul 2021 11:30:08 -0400 Subject: [PATCH 197/398] Correct resource name in CHANGELOG --- .changelog/19718.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changelog/19718.txt b/.changelog/19718.txt index 02c0d651b12..10a50d191fa 100644 --- a/.changelog/19718.txt +++ b/.changelog/19718.txt @@ -1,3 +1,3 @@ ```release-note:bug -resource/aws_ram_resource_share_acceptor: Allow destroy even where AWS API provides no way to disassociate +resource/aws_ram_resource_share_accepter: Allow destroy even where AWS API provides no way to disassociate ``` From 81f8ff4b5e72bf385b5cff693562749481805b54 Mon Sep 17 00:00:00 2001 From: changelogbot Date: Thu, 8 Jul 2021 15:34:31 +0000 Subject: [PATCH 198/398] Update CHANGELOG.md for #20105 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4eade413bc5..8117c545ea2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ ENHANCEMENTS: BUG FIXES: * resource/aws_eks_cluster: Don't associate an `encryption_config` if there's already one ([#19986](https://github.com/hashicorp/terraform-provider-aws/issues/19986)) +* resource/aws_ram_resource_share_accepter: Allow destroy even where AWS API provides no way to disassociate ([#19718](https://github.com/hashicorp/terraform-provider-aws/issues/19718)) ## 3.48.0 (July 02, 2021) From 847d868699f34843817e1baa3e7cf40f0f404628 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 8 Jul 2021 11:39:18 -0400 Subject: [PATCH 199/398] Fix 'errcheck' linter errors. --- aws/resource_aws_dx_gateway_association_proposal_test.go | 2 +- aws/resource_aws_dx_gateway_association_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_dx_gateway_association_proposal_test.go b/aws/resource_aws_dx_gateway_association_proposal_test.go index de71096ffd7..c77eed169a1 100644 --- a/aws/resource_aws_dx_gateway_association_proposal_test.go +++ b/aws/resource_aws_dx_gateway_association_proposal_test.go @@ -35,7 +35,7 @@ func testSweepDirectConnectGatewayAssociationProposals(region string) error { var sweeperErrs *multierror.Error sweepResources := make([]*testSweepResource, 0) - lister.DescribeDirectConnectGatewayAssociationProposalsPages(conn, input, func(page *directconnect.DescribeDirectConnectGatewayAssociationProposalsOutput, lastPage bool) bool { + err = lister.DescribeDirectConnectGatewayAssociationProposalsPages(conn, input, func(page *directconnect.DescribeDirectConnectGatewayAssociationProposalsOutput, lastPage bool) bool { if page == nil { return !lastPage } diff --git a/aws/resource_aws_dx_gateway_association_test.go b/aws/resource_aws_dx_gateway_association_test.go index ff422981634..c57fc0efaac 100644 --- a/aws/resource_aws_dx_gateway_association_test.go +++ b/aws/resource_aws_dx_gateway_association_test.go @@ -40,7 +40,7 @@ func testSweepDirectConnectGatewayAssociations(region string) error { var sweeperErrs *multierror.Error sweepResources := make([]*testSweepResource, 0) - lister.DescribeDirectConnectGatewaysPages(conn, input, func(page *directconnect.DescribeDirectConnectGatewaysOutput, lastPage bool) bool { + err = lister.DescribeDirectConnectGatewaysPages(conn, input, func(page *directconnect.DescribeDirectConnectGatewaysOutput, lastPage bool) bool { if page == nil { return !lastPage } From 260874d8a01cd33b05163e0fef578041b7a71a90 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 8 Jul 2021 11:42:30 -0400 Subject: [PATCH 200/398] Remove reference to hashibot --- docs/MAINTAINING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/MAINTAINING.md b/docs/MAINTAINING.md index bb36731efe7..8561a4e3ab8 100644 --- a/docs/MAINTAINING.md +++ b/docs/MAINTAINING.md @@ -506,4 +506,4 @@ Environment variables (beyond standard AWS Go SDK ones) used by acceptance testi - Web interface: With the `DEPLOYMENT_TARGET_VERSION` matching the expected release milestone and `DEPLOYMENT_NEXT_VERSION` matching the next release milestone - Wait for the TeamCity release job to complete either by watching the build logs or Slack notifications - Close the release milestone -- Create a new GitHub release with the release title exactly matching the tag and milestone (e.g. `v2.22.0`) and copy the entries from the CHANGELOG to the release notes. This will trigger [HashiBot](https://github.com/apps/hashibot) release comments. +- Create a new GitHub release with the release title exactly matching the tag and milestone (e.g. `v2.22.0`) and copy the entries from the CHANGELOG to the release notes. From 1a884d2a7934a7ab746e26403b6a061a5872a91c Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 8 Jul 2021 11:59:46 -0400 Subject: [PATCH 201/398] r/aws_dx_gateway_association: Use internal waiter package. --- aws/resource_aws_dx_gateway_association.go | 61 ++++------------------ 1 file changed, 11 insertions(+), 50 deletions(-) diff --git a/aws/resource_aws_dx_gateway_association.go b/aws/resource_aws_dx_gateway_association.go index f9f6ea4d1b5..19ba70b50bf 100644 --- a/aws/resource_aws_dx_gateway_association.go +++ b/aws/resource_aws_dx_gateway_association.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/directconnect/waiter" ) const ( @@ -23,6 +24,7 @@ func resourceAwsDxGatewayAssociation() *schema.Resource { Read: resourceAwsDxGatewayAssociationRead, Update: resourceAwsDxGatewayAssociationUpdate, Delete: resourceAwsDxGatewayAssociationDelete, + Importer: &schema.ResourceImporter{ State: resourceAwsDxGatewayAssociationImport, }, @@ -50,6 +52,7 @@ func resourceAwsDxGatewayAssociation() *schema.Resource { Computed: true, ForceNew: true, ConflictsWith: []string{"associated_gateway_owner_account_id", "proposal_id"}, + AtLeastOneOf: []string{"associated_gateway_id", "associated_gateway_owner_account_id", "proposal_id"}, }, "associated_gateway_owner_account_id": { @@ -59,6 +62,8 @@ func resourceAwsDxGatewayAssociation() *schema.Resource { ForceNew: true, ValidateFunc: validateAwsAccountId, ConflictsWith: []string{"associated_gateway_id"}, + RequiredWith: []string{"proposal_id"}, + AtLeastOneOf: []string{"associated_gateway_id", "associated_gateway_owner_account_id", "proposal_id"}, }, "associated_gateway_type": { @@ -86,6 +91,7 @@ func resourceAwsDxGatewayAssociation() *schema.Resource { Type: schema.TypeString, Optional: true, ConflictsWith: []string{"associated_gateway_id", "vpn_gateway_id"}, + AtLeastOneOf: []string{"associated_gateway_id", "associated_gateway_owner_account_id", "proposal_id"}, }, "vpn_gateway_id": { @@ -161,8 +167,8 @@ func resourceAwsDxGatewayAssociationCreate(d *schema.ResourceData, meta interfac } d.Set("dx_gateway_association_id", associationId) - if err := waitForDirectConnectGatewayAssociationAvailabilityOnCreate(conn, associationId, d.Timeout(schema.TimeoutCreate)); err != nil { - return fmt.Errorf("error waiting for Direct Connect gateway association (%s) to become available: %s", d.Id(), err) + if _, err := waiter.GatewayAssociationCreated(conn, associationId, d.Timeout(schema.TimeoutCreate)); err != nil { + return fmt.Errorf("error waiting for Direct Connect Gateway Association (%s) to create: %w", d.Id(), err) } return resourceAwsDxGatewayAssociationRead(d, meta) @@ -223,8 +229,8 @@ func resourceAwsDxGatewayAssociationUpdate(d *schema.ResourceData, meta interfac return fmt.Errorf("error updating Direct Connect gateway association (%s): %s", d.Id(), err) } - if err := waitForDirectConnectGatewayAssociationAvailabilityOnUpdate(conn, associationId, d.Timeout(schema.TimeoutUpdate)); err != nil { - return fmt.Errorf("error waiting for Direct Connect gateway association (%s) to become available: %s", d.Id(), err) + if _, err := waiter.GatewayAssociationUpdated(conn, associationId, d.Timeout(schema.TimeoutUpdate)); err != nil { + return fmt.Errorf("error waiting for Direct Connect Gateway Association (%s) to update: %w", d.Id(), err) } } @@ -249,7 +255,7 @@ func resourceAwsDxGatewayAssociationDelete(d *schema.ResourceData, meta interfac return fmt.Errorf("error deleting Direct Connect Gateway Association (%s): %w", d.Id(), err) } - if err := waitForDirectConnectGatewayAssociationDeletion(conn, associationID, d.Timeout(schema.TimeoutDelete)); err != nil { + if _, err := waiter.GatewayAssociationDeleted(conn, associationID, d.Timeout(schema.TimeoutDelete)); err != nil { return fmt.Errorf("error waiting for Direct Connect Gateway Association (%s) to delete: %w", d.Id(), err) } @@ -323,48 +329,3 @@ func dxGatewayAssociationStateRefresh(conn *directconnect.DirectConnect, associa func dxGatewayAssociationId(dxgwId, gwId string) string { return fmt.Sprintf("ga-%s%s", dxgwId, gwId) } - -func waitForDirectConnectGatewayAssociationAvailabilityOnCreate(conn *directconnect.DirectConnect, associationId string, timeout time.Duration) error { - stateConf := &resource.StateChangeConf{ - Pending: []string{directconnect.GatewayAssociationStateAssociating}, - Target: []string{directconnect.GatewayAssociationStateAssociated}, - Refresh: dxGatewayAssociationStateRefresh(conn, associationId), - Timeout: timeout, - Delay: 10 * time.Second, - MinTimeout: 5 * time.Second, - } - - _, err := stateConf.WaitForState() - - return err -} - -func waitForDirectConnectGatewayAssociationAvailabilityOnUpdate(conn *directconnect.DirectConnect, associationId string, timeout time.Duration) error { - stateConf := &resource.StateChangeConf{ - Pending: []string{directconnect.GatewayAssociationStateUpdating}, - Target: []string{directconnect.GatewayAssociationStateAssociated}, - Refresh: dxGatewayAssociationStateRefresh(conn, associationId), - Timeout: timeout, - Delay: 10 * time.Second, - MinTimeout: 5 * time.Second, - } - - _, err := stateConf.WaitForState() - - return err -} - -func waitForDirectConnectGatewayAssociationDeletion(conn *directconnect.DirectConnect, associationId string, timeout time.Duration) error { - stateConf := &resource.StateChangeConf{ - Pending: []string{directconnect.GatewayAssociationStateDisassociating}, - Target: []string{directconnect.GatewayAssociationStateDisassociated, gatewayAssociationStateDeleted}, - Refresh: dxGatewayAssociationStateRefresh(conn, associationId), - Timeout: timeout, - Delay: 10 * time.Second, - MinTimeout: 5 * time.Second, - } - - _, err := stateConf.WaitForState() - - return err -} From 50c70f0a169ed7356d40fcd8b9b814e16631b88f Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 8 Jul 2021 14:24:58 -0400 Subject: [PATCH 202/398] i/lakeformation: Add enum file --- aws/internal/service/lakeformation/enum.go | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 aws/internal/service/lakeformation/enum.go diff --git a/aws/internal/service/lakeformation/enum.go b/aws/internal/service/lakeformation/enum.go new file mode 100644 index 00000000000..d6be8ccf03f --- /dev/null +++ b/aws/internal/service/lakeformation/enum.go @@ -0,0 +1,8 @@ +package lakeformation + +const ( + TableNameAllTables = "ALL_TABLES" + TableTypeTable = "Table" + TableTypeTableWithColumns = "TableWithColumns" + IAMAllowedPrincipals = "IAM_ALLOWED_PRINCIPALS" +) From fa5163176fa8be3e9ecc386fc0eb12525dddfbe4 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 8 Jul 2021 14:28:30 -0400 Subject: [PATCH 203/398] docs/r/lakeformation_permissions: Add docs on IAMAllowedPrincipals --- .../r/lakeformation_permissions.html.markdown | 85 +++++++++++++++++-- 1 file changed, 80 insertions(+), 5 deletions(-) diff --git a/website/docs/r/lakeformation_permissions.html.markdown b/website/docs/r/lakeformation_permissions.html.markdown index 9fb3166fe72..d6b116689bf 100644 --- a/website/docs/r/lakeformation_permissions.html.markdown +++ b/website/docs/r/lakeformation_permissions.html.markdown @@ -10,8 +10,83 @@ description: |- Grants permissions to the principal to access metadata in the Data Catalog and data organized in underlying data storage such as Amazon S3. Permissions are granted to a principal, in a Data Catalog, relative to a Lake Formation resource, which includes the Data Catalog, databases, and tables. For more information, see [Security and Access Control to Metadata and Data in Lake Formation](https://docs.aws.amazon.com/lake-formation/latest/dg/security-data-access.html). +!> **WARNING:** Lake Formation permissions are not in effect by default within AWS. Using this resource will not secure your data and will result in errors if you do not change the security settings for existing resources and the default security settings for new resources. See [Default Behavior and `IAMAllowedPrincipals`](#default-behavior-and-iamallowedprincipals) for additional details. + ~> **NOTE:** In general, the `principal` should _NOT_ be a Lake Formation administrator or the entity (e.g., IAM role) that is running Terraform. Administrators have implicit permissions. These should be managed by granting or not granting administrator rights using `aws_lakeformation_data_lake_settings`, _not_ with this resource. +## Default Behavior and `IAMAllowedPrincipals` + +**_Lake Formation permissions are not in effect by default within AWS._** `IAMAllowedPrincipals` (i.e., `IAM_ALLOWED_PRINCIPALS`) conflicts with individual Lake Formation permissions (i.e., non-`IAMAllowedPrincipals` permissions), will cause unexpected behavior, and may result in errors. + +When using Lake Formation, you need to choose between these two mutually exclusive options: + +1. Use this resource (`aws_lakeformation_permissions`), change the default security settings using [`aws_lakeformation_data_lake_settings`](/docs/providers/aws/r/lakeformation_data_lake_settings.html), and remove existing `IAMAllowedPrincipals` permissions +2. Use `IAMAllowedPrincipals` and not use this resource + +This example shows removing the `IAMAllowedPrincipals` default security settings and making the caller a Lake Formation admin. Since `create_database_default_permissions` and `create_table_default_permissions` are not set in the [`aws_lakeformation_data_lake_settings`](/docs/providers/aws/r/lakeformation_data_lake_settings.html) resource, they are cleared. + +```terraform +data "aws_caller_identity" "current" {} + +data "aws_iam_session_context" "current" { + arn = data.aws_caller_identity.current.arn +} + +resource "aws_lakeformation_data_lake_settings" "test" { + admins = [data.aws_iam_session_context.current.issuer_arn] +} +``` + +To remove existing `IAMAllowedPrincipals` permissions, use the [AWS Lake Formation Console](https://console.aws.amazon.com/lakeformation/) or [AWS CLI](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lakeformation/batch-revoke-permissions.html). + +`IAMAllowedPrincipals` is a hook to maintain backwards compatibility with AWS Glue. `IAMAllowedPrincipals` is a pseudo-entity group that acts like a Lake Formation principal. The group includes any IAM users and roles that are allowed access to your Data Catalog resources by your IAM policies. + +This is Lake Formation's default behavior: + +* Lake Formation grants `Super` permission to `IAMAllowedPrincipals` on all existing AWS Glue Data Catalog resources. +* Lake Formation enables "Use only IAM access control" for new Data Catalog resources. + +For more details, see [Changing the Default Security Settings for Your Data Lake](https://docs.aws.amazon.com/lake-formation/latest/dg/change-settings.html). + +### Example Problem Using `IAMAllowedPrincipals` + +AWS does not support combining `IAMAllowedPrincipals` permissions and non-`IAMAllowedPrincipals` permissions. Doing so results in unexpected permissions. For example, this configuration grants a user `SELECT` on a column in a table. + +```terraform +resource "aws_glue_catalog_database" "example" { + name = "sadabate" +} + +resource "aws_glue_catalog_table" "example" { + name = "abelt" + database_name = aws_glue_catalog_database.test.name + + storage_descriptor { + columns { + name = "event" + type = "string" + } + } +} + +resource "aws_lakeformation_permissions" "example" { + permissions = ["SELECT"] + principal = "arn:aws:iam:us-east-1:123456789012:user/SanHolo" + + table_with_columns { + database_name = aws_glue_catalog_table.example.database_name + name = aws_glue_catalog_table.example.name + column_names = ["event"] + } +} +``` + +The resulting permissions depend on whether the table had `IAMAllowedPrincipals` (IAP) permissions or not. + +| Result With IAP | Result Without IAP | +| ---- | ---- | +| `SELECT` on table with columns with column wildcard (i.e., all columns) | `SELECT` on `"event"` (as expected) | + ## Using Lake Formation Permissions Lake Formation grants implicit permissions to data lake administrators, database creators, and table creators. These implicit permissions cannot be revoked _per se_. If this resource reads implicit permissions, it will attempt to revoke them, which causes an error when the resource is destroyed. @@ -25,12 +100,12 @@ If the `principal` is also a data lake administrator, AWS grants implicit permis ### Grant Permissions For A Lake Formation S3 Resource ```terraform -resource "aws_lakeformation_permissions" "test" { +resource "aws_lakeformation_permissions" "example" { principal = aws_iam_role.workflow_role.arn permissions = ["ALL"] data_location { - arn = aws_lakeformation_resource.test.arn + arn = aws_lakeformation_resource.example.arn } } ``` @@ -38,12 +113,12 @@ resource "aws_lakeformation_permissions" "test" { ### Grant Permissions For A Glue Catalog Database ```terraform -resource "aws_lakeformation_permissions" "test" { +resource "aws_lakeformation_permissions" "example" { role = aws_iam_role.workflow_role.arn permissions = ["CREATE_TABLE", "ALTER", "DROP"] database { - name = aws_glue_catalog_database.test.name + name = aws_glue_catalog_database.example.name catalog_id = "110376042874" } } @@ -54,7 +129,7 @@ resource "aws_lakeformation_permissions" "test" { The following arguments are required: * `permissions` – (Required) List of permissions granted to the principal. Valid values may include `ALL`, `ALTER`, `CREATE_DATABASE`, `CREATE_TABLE`, `DATA_LOCATION_ACCESS`, `DELETE`, `DESCRIBE`, `DROP`, `INSERT`, and `SELECT`. For details on each permission, see [Lake Formation Permissions Reference](https://docs.aws.amazon.com/lake-formation/latest/dg/lf-permissions-reference.html). -* `principal` – (Required) Principal to be granted the permissions on the resource. Supported principals include IAM roles, users, groups, SAML groups and users, QuickSight groups, OUs, and organizations as well as AWS account IDs for cross-account permissions. For more information, see [Lake Formation Permissions Reference](https://docs.aws.amazon.com/lake-formation/latest/dg/lf-permissions-reference.html). +* `principal` – (Required) Principal to be granted the permissions on the resource. Supported principals include `IAM_ALLOWED_PRINCIPALS` (see [Default Behavior and `IAMAllowedPrincipals`](#default-behavior-and-iamallowedprincipals) above), IAM roles, users, groups, SAML groups and users, QuickSight groups, OUs, and organizations as well as AWS account IDs for cross-account permissions. For more information, see [Lake Formation Permissions Reference](https://docs.aws.amazon.com/lake-formation/latest/dg/lf-permissions-reference.html). ~> **NOTE:** We highly recommend that the `principal` _NOT_ be a Lake Formation administrator (granted using `aws_lakeformation_data_lake_settings`). The entity (e.g., IAM role) running Terraform will most likely need to be a Lake Formation administrator. As such, the entity will have implicit permissions and does not need permissions granted through this resource. From 10970c4e99ddf1988432f31b3b2c0a696fbcd2c7 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 8 Jul 2021 14:29:06 -0400 Subject: [PATCH 204/398] tests/lakeformation: Add new tests, rename existing --- aws/resource_aws_lakeformation_test.go | 53 +++++++++++++++----------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/aws/resource_aws_lakeformation_test.go b/aws/resource_aws_lakeformation_test.go index 6f012aefe4e..aef0bd5574b 100644 --- a/aws/resource_aws_lakeformation_test.go +++ b/aws/resource_aws_lakeformation_test.go @@ -8,35 +8,42 @@ func TestAccAWSLakeFormation_serial(t *testing.T) { testCases := map[string]map[string]func(t *testing.T){ "DataLakeSettings": { "basic": testAccAWSLakeFormationDataLakeSettings_basic, + "dataSource": testAccAWSLakeFormationDataLakeSettingsDataSource_basic, "disappears": testAccAWSLakeFormationDataLakeSettings_disappears, "withoutCatalogId": testAccAWSLakeFormationDataLakeSettings_withoutCatalogId, - "dataSource": testAccAWSLakeFormationDataLakeSettingsDataSource_basic, }, - "BasicPermissions": { - "basic": testAccAWSLakeFormationPermissions_basic, - "dataLocation": testAccAWSLakeFormationPermissions_dataLocation, - "database": testAccAWSLakeFormationPermissions_database, - "disappears": testAccAWSLakeFormationPermissions_disappears, + "PermissionsBasic": { + "basic": testAccAWSLakeFormationPermissions_basic, + "database": testAccAWSLakeFormationPermissions_database, + "databaseIAMAllowed": testAccAWSLakeFormationPermissions_databaseIAMAllowed, + "databaseMultiple": testAccAWSLakeFormationPermissions_databaseMultiple, + "dataLocation": testAccAWSLakeFormationPermissions_dataLocation, + "disappears": testAccAWSLakeFormationPermissions_disappears, }, - "TablePermissions": { - "implicitTablePermissions": testAccAWSLakeFormationPermissions_implicitTablePermissions, - "selectPermissions": testAccAWSLakeFormationPermissions_selectPermissions, - "tableName": testAccAWSLakeFormationPermissions_tableName, - "tableWildcard": testAccAWSLakeFormationPermissions_tableWildcard, - "tableWildcardPermissions": testAccAWSLakeFormationPermissions_tableWildcardPermissions, + "PermissionsDataSource": { + "basic": testAccAWSLakeFormationPermissionsDataSource_basic, + "database": testAccAWSLakeFormationPermissionsDataSource_database, + "dataLocation": testAccAWSLakeFormationPermissionsDataSource_dataLocation, + "table": testAccAWSLakeFormationPermissionsDataSource_table, + "tableWithColumns": testAccAWSLakeFormationPermissionsDataSource_tableWithColumns, }, - "TableWithColumnsPermissions": { - "columnWildcardExcludedColumnsPermissions": testAccAWSLakeFormationPermissions_columnWildcardExcludedColumnsPermissions, - "columnWildcardPermissions": testAccAWSLakeFormationPermissions_columnWildcardPermissions, - "implicitTableWithColumnsPermissions": testAccAWSLakeFormationPermissions_implicitTableWithColumnsPermissions, - "tableWithColumns": testAccAWSLakeFormationPermissions_tableWithColumns, + "PermissionsTable": { + "basic": testAccAWSLakeFormationPermissions_tableBasic, + "iamAllowed": testAccAWSLakeFormationPermissions_tableIAMAllowed, + "implicit": testAccAWSLakeFormationPermissions_tableImplicit, + "multipleRoles": testAccAWSLakeFormationPermissions_tableMultipleRoles, + "selectOnly": testAccAWSLakeFormationPermissions_tableSelectOnly, + "selectPlus": testAccAWSLakeFormationPermissions_tableSelectPlus, + "wildcardNoSelect": testAccAWSLakeFormationPermissions_tableWildcardNoSelect, + "wildcardSelectOnly": testAccAWSLakeFormationPermissions_tableWildcardSelectOnly, + "wildcardSelectPlus": testAccAWSLakeFormationPermissions_tableWildcardSelectPlus, }, - "DataSourcePermissions": { - "basicDataSource": testAccAWSLakeFormationPermissionsDataSource_basic, - "dataLocationDataSource": testAccAWSLakeFormationPermissionsDataSource_dataLocation, - "databaseDataSource": testAccAWSLakeFormationPermissionsDataSource_database, - "tableDataSource": testAccAWSLakeFormationPermissionsDataSource_table, - "tableWithColumnsDataSource": testAccAWSLakeFormationPermissionsDataSource_tableWithColumns, + "PermissionsTableWithColumns": { + "basic": testAccAWSLakeFormationPermissions_twcBasic, + "implicit": testAccAWSLakeFormationPermissions_twcImplicit, + "wildcardExcludedColumns": testAccAWSLakeFormationPermissions_twcWildcardExcludedColumns, + "wildcardSelectOnly": testAccAWSLakeFormationPermissions_twcWildcardSelectOnly, + "wildcardSelectPlus": testAccAWSLakeFormationPermissions_twcWildcardSelectPlus, }, } From 81b10a55bcc58a59b13256bd7ef60cd9140c0955 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 8 Jul 2021 14:29:42 -0400 Subject: [PATCH 205/398] tests/r/lakeformation_permissions: Fix bugs --- ...urce_aws_lakeformation_permissions_test.go | 1485 ++++++++++++----- 1 file changed, 1081 insertions(+), 404 deletions(-) diff --git a/aws/resource_aws_lakeformation_permissions_test.go b/aws/resource_aws_lakeformation_permissions_test.go index bfd87e3fd9f..8fcbb28dcc4 100644 --- a/aws/resource_aws_lakeformation_permissions_test.go +++ b/aws/resource_aws_lakeformation_permissions_test.go @@ -53,7 +53,7 @@ func testAccAWSLakeFormationPermissions_disappears(t *testing.T) { CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_tableWithColumns(rName, "\"event\", \"timestamp\""), + Config: testAccAWSLakeFormationPermissionsConfig_twcBasic(rName, "\"event\", \"timestamp\""), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationPermissionsExists(resourceName), testAccCheckResourceDisappears(testAccProvider, resourceAwsLakeFormationPermissions(), resourceName), @@ -64,11 +64,11 @@ func testAccAWSLakeFormationPermissions_disappears(t *testing.T) { }) } -func testAccAWSLakeFormationPermissions_dataLocation(t *testing.T) { +func testAccAWSLakeFormationPermissions_database(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_lakeformation_permissions.test" roleName := "aws_iam_role.test" - bucketName := "aws_s3_bucket.test" + dbName := "aws_glue_catalog_database.test" resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, @@ -77,25 +77,60 @@ func testAccAWSLakeFormationPermissions_dataLocation(t *testing.T) { CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_dataLocation(rName), + Config: testAccAWSLakeFormationPermissionsConfig_database(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationPermissionsExists(resourceName), resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), - resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionDataLocationAccess), resource.TestCheckResourceAttr(resourceName, "catalog_resource", "false"), - resource.TestCheckResourceAttr(resourceName, "data_location.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "data_location.0.arn", bucketName, "arn"), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "database.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "database.0.name", dbName, "name"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "3"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionAlter), + resource.TestCheckResourceAttr(resourceName, "permissions.1", lakeformation.PermissionCreateTable), + resource.TestCheckResourceAttr(resourceName, "permissions.2", lakeformation.PermissionDrop), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.0", lakeformation.PermissionCreateTable), ), }, }, }) } -func testAccAWSLakeFormationPermissions_database(t *testing.T) { +func testAccAWSLakeFormationPermissions_databaseIAMAllowed(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + dbName := "aws_glue_catalog_database.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsConfig_databaseIAMAllowed(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "principal", tflakeformation.IAMAllowedPrincipals), + resource.TestCheckResourceAttr(resourceName, "catalog_resource", "false"), + resource.TestCheckResourceAttr(resourceName, "database.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "database.0.name", dbName, "name"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionAll), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "0"), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissions_databaseMultiple(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_lakeformation_permissions.test" + resourceName2 := "aws_lakeformation_permissions.test2" roleName := "aws_iam_role.test" + roleName2 := "aws_iam_role.test2" dbName := "aws_glue_catalog_database.test" resource.Test(t, resource.TestCase{ @@ -105,7 +140,7 @@ func testAccAWSLakeFormationPermissions_database(t *testing.T) { CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_database(rName), + Config: testAccAWSLakeFormationPermissionsConfig_databaseMultiple(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationPermissionsExists(resourceName), resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), @@ -114,18 +149,56 @@ func testAccAWSLakeFormationPermissions_database(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "database.#", "1"), resource.TestCheckResourceAttrPair(resourceName, "database.0.name", dbName, "name"), resource.TestCheckResourceAttr(resourceName, "permissions.#", "3"), - resource.TestCheckResourceAttr(resourceName, "permissions.0", "ALTER"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionAlter), resource.TestCheckResourceAttr(resourceName, "permissions.1", lakeformation.PermissionCreateTable), resource.TestCheckResourceAttr(resourceName, "permissions.2", lakeformation.PermissionDrop), resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "1"), resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.0", lakeformation.PermissionCreateTable), + testAccCheckAWSLakeFormationPermissionsExists(resourceName2), + resource.TestCheckResourceAttrPair(resourceName2, "principal", roleName2, "arn"), + resource.TestCheckResourceAttr(resourceName2, "catalog_resource", "false"), + resource.TestCheckResourceAttrPair(resourceName2, "principal", roleName2, "arn"), + resource.TestCheckResourceAttr(resourceName2, "database.#", "1"), + resource.TestCheckResourceAttrPair(resourceName2, "database.0.name", dbName, "name"), + resource.TestCheckResourceAttr(resourceName2, "permissions.#", "2"), + resource.TestCheckResourceAttr(resourceName2, "permissions.0", lakeformation.PermissionAlter), + resource.TestCheckResourceAttr(resourceName2, "permissions.1", lakeformation.PermissionDrop), + resource.TestCheckResourceAttr(resourceName2, "permissions_with_grant_option.#", "0"), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissions_dataLocation(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + roleName := "aws_iam_role.test" + bucketName := "aws_s3_bucket.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsConfig_dataLocation(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionDataLocationAccess), + resource.TestCheckResourceAttr(resourceName, "catalog_resource", "false"), + resource.TestCheckResourceAttr(resourceName, "data_location.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "data_location.0.arn", bucketName, "arn"), ), }, }, }) } -func testAccAWSLakeFormationPermissions_tableName(t *testing.T) { +func testAccAWSLakeFormationPermissions_tableBasic(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_lakeformation_permissions.test" roleName := "aws_iam_role.test" @@ -138,7 +211,7 @@ func testAccAWSLakeFormationPermissions_tableName(t *testing.T) { CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_tableName(rName), + Config: testAccAWSLakeFormationPermissionsConfig_tableBasic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationPermissionsExists(resourceName), resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), @@ -155,10 +228,10 @@ func testAccAWSLakeFormationPermissions_tableName(t *testing.T) { }) } -func testAccAWSLakeFormationPermissions_tableWildcard(t *testing.T) { +func testAccAWSLakeFormationPermissions_tableIAMAllowed(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_lakeformation_permissions.test" - databaseResourceName := "aws_glue_catalog_database.test" + dbName := "aws_glue_catalog_table.test" resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, @@ -167,19 +240,24 @@ func testAccAWSLakeFormationPermissions_tableWildcard(t *testing.T) { CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_tableWildcard(rName), + Config: testAccAWSLakeFormationPermissionsConfig_tableIAMAllowed(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "principal", tflakeformation.IAMAllowedPrincipals), + resource.TestCheckResourceAttr(resourceName, "catalog_resource", "false"), resource.TestCheckResourceAttr(resourceName, "table.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "table.0.database_name", databaseResourceName, "name"), - resource.TestCheckResourceAttr(resourceName, "table.0.wildcard", "true"), + resource.TestCheckResourceAttrPair(resourceName, "table.0.database_name", dbName, "database_name"), + resource.TestCheckResourceAttrPair(resourceName, "table.0.name", dbName, "name"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionAll), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "0"), ), }, }, }) } -func testAccAWSLakeFormationPermissions_tableWithColumns(t *testing.T) { +func testAccAWSLakeFormationPermissions_tableImplicit(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_lakeformation_permissions.test" roleName := "aws_iam_role.test" @@ -192,61 +270,80 @@ func testAccAWSLakeFormationPermissions_tableWithColumns(t *testing.T) { CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_tableWithColumns(rName, "\"event\", \"timestamp\""), + Config: testAccAWSLakeFormationPermissionsConfig_tableImplicit(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationPermissionsExists(resourceName), resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), - resource.TestCheckResourceAttr(resourceName, "table_with_columns.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.database_name", tableName, "database_name"), - resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.name", tableName, "name"), - resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.#", "2"), - resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.0", "event"), - resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.1", "timestamp"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), - resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionSelect), - ), - }, - { - Config: testAccAWSLakeFormationPermissionsConfig_tableWithColumns(rName, "\"timestamp\", \"event\""), - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSLakeFormationPermissionsExists(resourceName), - resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), - resource.TestCheckResourceAttr(resourceName, "table_with_columns.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.database_name", tableName, "database_name"), - resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.name", tableName, "name"), - resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.#", "2"), - resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.0", "event"), - resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.1", "timestamp"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), - resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionSelect), + resource.TestCheckResourceAttr(resourceName, "table.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "table.0.database_name", tableName, "database_name"), + resource.TestCheckResourceAttrPair(resourceName, "table.0.name", tableName, "name"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "7"), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "7"), ), }, + }, + }) +} + +func testAccAWSLakeFormationPermissions_tableMultipleRoles(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + resourceName2 := "aws_lakeformation_permissions.test2" + roleName := "aws_iam_role.test" + roleName2 := "aws_iam_role.test2" + tableName := "aws_glue_catalog_table.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_tableWithColumns(rName, "\"timestamp\", \"event\", \"transactionamount\""), + Config: testAccAWSLakeFormationPermissionsConfig_tableMultipleRoles(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationPermissionsExists(resourceName), - resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), - resource.TestCheckResourceAttr(resourceName, "table_with_columns.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.database_name", tableName, "database_name"), - resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.name", tableName, "name"), - resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.#", "3"), - resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.0", "event"), - resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.1", "timestamp"), - resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.2", "transactionamount"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), - resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionSelect), + resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), + resource.TestCheckResourceAttr(resourceName, "table.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "table.0.database_name", tableName, "database_name"), + resource.TestCheckResourceAttrPair(resourceName, "table.0.name", tableName, "name"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "3"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionAlter), + resource.TestCheckResourceAttr(resourceName, "permissions.1", lakeformation.PermissionDelete), + resource.TestCheckResourceAttr(resourceName, "permissions.2", lakeformation.PermissionDescribe), + testAccCheckAWSLakeFormationPermissionsExists(resourceName2), + resource.TestCheckResourceAttrPair(roleName2, "arn", resourceName2, "principal"), + resource.TestCheckResourceAttr(resourceName2, "table.#", "1"), + resource.TestCheckResourceAttrPair(resourceName2, "table.0.database_name", tableName, "database_name"), + resource.TestCheckResourceAttrPair(resourceName2, "table.0.name", tableName, "name"), + resource.TestCheckResourceAttr(resourceName2, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName2, "permissions.0", lakeformation.PermissionSelect), ), }, + }, + }) +} + +func testAccAWSLakeFormationPermissions_tableSelectOnly(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + roleName := "aws_iam_role.test" + tableName := "aws_glue_catalog_table.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_tableWithColumns(rName, "\"event\""), + Config: testAccAWSLakeFormationPermissionsConfig_tableSelectOnly(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationPermissionsExists(resourceName), - resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), - resource.TestCheckResourceAttr(resourceName, "table_with_columns.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.database_name", tableName, "database_name"), - resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.name", tableName, "name"), - resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.#", "1"), - resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.0", "event"), + resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), + resource.TestCheckResourceAttr(resourceName, "table.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "table.0.database_name", tableName, "database_name"), + resource.TestCheckResourceAttrPair(resourceName, "table.0.name", tableName, "name"), resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionSelect), ), @@ -255,11 +352,10 @@ func testAccAWSLakeFormationPermissions_tableWithColumns(t *testing.T) { }) } -func testAccAWSLakeFormationPermissions_implicitTableWithColumnsPermissions(t *testing.T) { +func testAccAWSLakeFormationPermissions_tableSelectPlus(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_lakeformation_permissions.test" roleName := "aws_iam_role.test" - tableName := "aws_glue_catalog_table.test" resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, @@ -268,14 +364,10 @@ func testAccAWSLakeFormationPermissions_implicitTableWithColumnsPermissions(t *t CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_implicitTableWithColumnsPermissions(rName), + Config: testAccAWSLakeFormationPermissionsConfig_tableSelectPlus(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationPermissionsExists(resourceName), resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), - resource.TestCheckResourceAttr(resourceName, "table_with_columns.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.database_name", tableName, "database_name"), - resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.name", tableName, "name"), - resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.wildcard", "true"), resource.TestCheckResourceAttr(resourceName, "permissions.#", "7"), resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "7"), ), @@ -284,11 +376,10 @@ func testAccAWSLakeFormationPermissions_implicitTableWithColumnsPermissions(t *t }) } -func testAccAWSLakeFormationPermissions_implicitTablePermissions(t *testing.T) { +func testAccAWSLakeFormationPermissions_tableWildcardNoSelect(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_lakeformation_permissions.test" - roleName := "aws_iam_role.test" - tableName := "aws_glue_catalog_table.test" + databaseResourceName := "aws_glue_catalog_database.test" resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, @@ -297,22 +388,19 @@ func testAccAWSLakeFormationPermissions_implicitTablePermissions(t *testing.T) { CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_implicitTablePermissions(rName), + Config: testAccAWSLakeFormationPermissionsConfig_tableWildcardNoSelect(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationPermissionsExists(resourceName), - resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), resource.TestCheckResourceAttr(resourceName, "table.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "table.0.database_name", tableName, "database_name"), - resource.TestCheckResourceAttrPair(resourceName, "table.0.name", tableName, "name"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "7"), - resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "7"), + resource.TestCheckResourceAttrPair(resourceName, "table.0.database_name", databaseResourceName, "name"), + resource.TestCheckResourceAttr(resourceName, "table.0.wildcard", "true"), ), }, }, }) } -func testAccAWSLakeFormationPermissions_selectPermissions(t *testing.T) { +func testAccAWSLakeFormationPermissions_tableWildcardSelectOnly(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_lakeformation_permissions.test" roleName := "aws_iam_role.test" @@ -324,19 +412,20 @@ func testAccAWSLakeFormationPermissions_selectPermissions(t *testing.T) { CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_selectPermissions(rName), + Config: testAccAWSLakeFormationPermissionsConfig_tableWildcardSelectOnly(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationPermissionsExists(resourceName), resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "7"), - resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "7"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionSelect), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "0"), ), }, }, }) } -func testAccAWSLakeFormationPermissions_tableWildcardPermissions(t *testing.T) { +func testAccAWSLakeFormationPermissions_tableWildcardSelectPlus(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_lakeformation_permissions.test" roleName := "aws_iam_role.test" @@ -348,7 +437,7 @@ func testAccAWSLakeFormationPermissions_tableWildcardPermissions(t *testing.T) { CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_tableWildcardPermissions(rName), + Config: testAccAWSLakeFormationPermissionsConfig_tableWildcardSelectPlus(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationPermissionsExists(resourceName), resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), @@ -360,10 +449,11 @@ func testAccAWSLakeFormationPermissions_tableWildcardPermissions(t *testing.T) { }) } -func testAccAWSLakeFormationPermissions_columnWildcardPermissions(t *testing.T) { +func testAccAWSLakeFormationPermissions_twcBasic(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_lakeformation_permissions.test" roleName := "aws_iam_role.test" + tableName := "aws_glue_catalog_table.test" resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, @@ -372,22 +462,74 @@ func testAccAWSLakeFormationPermissions_columnWildcardPermissions(t *testing.T) CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_columnWildcardPermissions(rName), + Config: testAccAWSLakeFormationPermissionsConfig_twcBasic(rName, "\"event\", \"timestamp\""), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationPermissionsExists(resourceName), resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "7"), - resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "0"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.database_name", tableName, "database_name"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.name", tableName, "name"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.#", "2"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.0", "event"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.1", "timestamp"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionSelect), + ), + }, + { + Config: testAccAWSLakeFormationPermissionsConfig_twcBasic(rName, "\"timestamp\", \"event\""), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.database_name", tableName, "database_name"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.name", tableName, "name"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.#", "2"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.0", "event"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.1", "timestamp"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionSelect), + ), + }, + { + Config: testAccAWSLakeFormationPermissionsConfig_twcBasic(rName, "\"timestamp\", \"event\", \"transactionamount\""), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.database_name", tableName, "database_name"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.name", tableName, "name"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.#", "3"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.0", "event"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.1", "timestamp"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.2", "transactionamount"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionSelect), + ), + }, + { + Config: testAccAWSLakeFormationPermissionsConfig_twcBasic(rName, "\"event\""), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.database_name", tableName, "database_name"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.name", tableName, "name"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.#", "1"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.0", "event"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionSelect), ), }, }, }) } -func testAccAWSLakeFormationPermissions_columnWildcardExcludedColumnsPermissions(t *testing.T) { +func testAccAWSLakeFormationPermissions_twcImplicit(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_lakeformation_permissions.test" roleName := "aws_iam_role.test" + tableName := "aws_glue_catalog_table.test" resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, @@ -396,34 +538,116 @@ func testAccAWSLakeFormationPermissions_columnWildcardExcludedColumnsPermissions CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_columnWildcardExcludedColumnsPermissions(rName), + Config: testAccAWSLakeFormationPermissionsConfig_twcImplicit(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationPermissionsExists(resourceName), resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), - resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "0"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.database_name", tableName, "database_name"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.name", tableName, "name"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.wildcard", "true"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "7"), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "7"), ), }, }, }) } -func testAccCheckAWSLakeFormationPermissionsDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).lakeformationconn +func testAccAWSLakeFormationPermissions_twcWildcardExcludedColumns(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + roleName := "aws_iam_role.test" - for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_lakeformation_permissions" { - continue - } + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsConfig_twcWildcardExcludedColumns(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "0"), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissions_twcWildcardSelectOnly(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + roleName := "aws_iam_role.test" + tableName := "aws_glue_catalog_table.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsConfig_twcWildcardSelectOnly(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.database_name", tableName, "database_name"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.name", tableName, "name"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.#", "0"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.wildcard", "true"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionSelect), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissions_twcWildcardSelectPlus(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + roleName := "aws_iam_role.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsConfig_twcWildcardSelectPlus(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "7"), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "0"), + ), + }, + }, + }) +} + +func testAccCheckAWSLakeFormationPermissionsDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).lakeformationconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_lakeformation_permissions" { + continue + } permCount, err := permissionCountForLakeFormationResource(conn, rs) if err != nil { - return fmt.Errorf("error listing Lake Formation permissions (%s): %w", rs.Primary.ID, err) + return fmt.Errorf("acceptance test: error listing Lake Formation permissions (%s): %w", rs.Primary.ID, err) } if permCount != 0 { - return fmt.Errorf("Lake Formation permissions (%s) still exist: %d", rs.Primary.ID, permCount) + return fmt.Errorf("acceptance test: Lake Formation permissions (%s) still exist: %d", rs.Primary.ID, permCount) } return nil @@ -435,8 +659,9 @@ func testAccCheckAWSLakeFormationPermissionsDestroy(s *terraform.State) error { func testAccCheckAWSLakeFormationPermissionsExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] + if !ok { - return fmt.Errorf("resource not found: %s", resourceName) + return fmt.Errorf("acceptance test: resource not found: %s", resourceName) } conn := testAccProvider.Meta().(*AWSClient).lakeformationconn @@ -444,11 +669,11 @@ func testAccCheckAWSLakeFormationPermissionsExists(resourceName string) resource permCount, err := permissionCountForLakeFormationResource(conn, rs) if err != nil { - return fmt.Errorf("error listing Lake Formation permissions (%s): %w", rs.Primary.ID, err) + return fmt.Errorf("acceptance test: error listing Lake Formation permissions (%s): %w", rs.Primary.ID, err) } if permCount == 0 { - return fmt.Errorf("Lake Formation permissions (%s) do not exist or could not be found", rs.Primary.ID) + return fmt.Errorf("acceptance test: Lake Formation permissions (%s) do not exist or could not be found", rs.Primary.ID) } return nil @@ -463,12 +688,18 @@ func permissionCountForLakeFormationResource(conn *lakeformation.LakeFormation, Resource: &lakeformation.Resource{}, } + noResource := true + if v, ok := rs.Primary.Attributes["catalog_id"]; ok && v != "" { input.CatalogId = aws.String(v) + + noResource = false } if v, ok := rs.Primary.Attributes["catalog_resource"]; ok && v != "" && v == "true" { input.Resource.Catalog = expandLakeFormationCatalogResource() + + noResource = false } if v, ok := rs.Primary.Attributes["data_location.#"]; ok && v != "" && v != "0" { @@ -483,6 +714,8 @@ func permissionCountForLakeFormationResource(conn *lakeformation.LakeFormation, } input.Resource.DataLocation = expandLakeFormationDataLocationResource(tfMap) + + noResource = false } if v, ok := rs.Primary.Attributes["database.#"]; ok && v != "" && v != "0" { @@ -497,6 +730,8 @@ func permissionCountForLakeFormationResource(conn *lakeformation.LakeFormation, } input.Resource.Database = expandLakeFormationDatabaseResource(tfMap) + + noResource = false } tableType := "" @@ -523,6 +758,8 @@ func permissionCountForLakeFormationResource(conn *lakeformation.LakeFormation, } input.Resource.Table = expandLakeFormationTableResource(tfMap) + + noResource = false } if v, ok := rs.Primary.Attributes["table_with_columns.#"]; ok && v != "" && v != "0" { @@ -543,6 +780,13 @@ func permissionCountForLakeFormationResource(conn *lakeformation.LakeFormation, } input.Resource.Table = expandLakeFormationTableWithColumnsResourceAsTable(tfMap) + + noResource = false + } + + if noResource { + // if after read, there is no resource, it has been deleted + return 0, nil } log.Printf("[DEBUG] Reading Lake Formation permissions: %v", input) @@ -565,7 +809,7 @@ func permissionCountForLakeFormationResource(conn *lakeformation.LakeFormation, return resource.RetryableError(err) } - return resource.NonRetryableError(fmt.Errorf("error listing Lake Formation Permissions: %w", err)) + return resource.NonRetryableError(fmt.Errorf("acceptance test: error listing Lake Formation Permissions getting permission count: %w", err)) } return nil }) @@ -592,7 +836,7 @@ func permissionCountForLakeFormationResource(conn *lakeformation.LakeFormation, } if err != nil { - return 0, fmt.Errorf("error listing Lake Formation permissions after retry: %w", err) + return 0, fmt.Errorf("acceptance test: error listing Lake Formation permissions after retry %v: %w", input, err) } columnNames := make([]*string, 0) @@ -610,7 +854,7 @@ func permissionCountForLakeFormationResource(conn *lakeformation.LakeFormation, colCount, err = strconv.Atoi(rs.Primary.Attributes["table_with_columns.0.column_names.#"]) if err != nil { - return 0, fmt.Errorf("could not convert string (%s) Atoi for column_names: %w", rs.Primary.Attributes["table_with_columns.0.column_names.#"], err) + return 0, fmt.Errorf("acceptance test: could not convert string (%s) Atoi for column_names: %w", rs.Primary.Attributes["table_with_columns.0.column_names.#"], err) } } @@ -624,7 +868,7 @@ func permissionCountForLakeFormationResource(conn *lakeformation.LakeFormation, colCount, err = strconv.Atoi(rs.Primary.Attributes["table_with_columns.0.excluded_column_names.#"]) if err != nil { - return 0, fmt.Errorf("could not convert string (%s) Atoi for excluded_column_names: %w", rs.Primary.Attributes["table_with_columns.0.excluded_column_names.#"], err) + return 0, fmt.Errorf("acceptance test: could not convert string (%s) Atoi for excluded_column_names: %w", rs.Primary.Attributes["table_with_columns.0.excluded_column_names.#"], err) } } @@ -643,36 +887,455 @@ func testAccAWSLakeFormationPermissionsConfig_basic(rName string) string { return fmt.Sprintf(` data "aws_partition" "current" {} -resource "aws_iam_role" "test" { - name = %[1]q +resource "aws_iam_role" "test" { + name = %[1]q + + assume_role_policy = jsonencode({ + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "glue.${data.aws_partition.current.dns_suffix}" + } + }] + Version = "2012-10-17" + }) +} + +data "aws_caller_identity" "current" {} + +data "aws_iam_session_context" "current" { + arn = data.aws_caller_identity.current.arn +} + +resource "aws_lakeformation_data_lake_settings" "test" { + admins = [data.aws_iam_session_context.current.issuer_arn] +} + +resource "aws_lakeformation_permissions" "test" { + principal = aws_iam_role.test.arn + permissions = ["CREATE_DATABASE"] + catalog_resource = true + + # for consistency, ensure that admins are setup before testing + depends_on = [aws_lakeformation_data_lake_settings.test] +} +`, rName) +} + +func testAccAWSLakeFormationPermissionsConfig_database(rName string) string { + return fmt.Sprintf(` +data "aws_partition" "current" {} + +resource "aws_iam_role" "test" { + name = %[1]q + path = "/" + + assume_role_policy = jsonencode({ + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "glue.${data.aws_partition.current.dns_suffix}" + } + }] + Version = "2012-10-17" + }) +} + +resource "aws_glue_catalog_database" "test" { + name = %[1]q +} + +data "aws_caller_identity" "current" {} + +data "aws_iam_session_context" "current" { + arn = data.aws_caller_identity.current.arn +} + +resource "aws_lakeformation_data_lake_settings" "test" { + admins = [data.aws_iam_session_context.current.issuer_arn] +} + +resource "aws_lakeformation_permissions" "test" { + permissions = ["ALTER", "CREATE_TABLE", "DROP"] + permissions_with_grant_option = ["CREATE_TABLE"] + principal = aws_iam_role.test.arn + + database { + name = aws_glue_catalog_database.test.name + } + + # for consistency, ensure that admins are setup before testing + depends_on = [aws_lakeformation_data_lake_settings.test] +} +`, rName) +} + +func testAccAWSLakeFormationPermissionsConfig_databaseIAMAllowed(rName string) string { + return fmt.Sprintf(` +data "aws_partition" "current" {} + +data "aws_caller_identity" "current" {} + +data "aws_iam_session_context" "current" { + arn = data.aws_caller_identity.current.arn +} + +resource "aws_lakeformation_data_lake_settings" "test" { + admins = [data.aws_iam_session_context.current.issuer_arn] +} + +resource "aws_glue_catalog_database" "test" { + name = %[1]q +} + +resource "aws_glue_catalog_table" "test" { + name = %[1]q + database_name = aws_glue_catalog_database.test.name + + storage_descriptor { + columns { + name = "event" + type = "string" + } + + columns { + name = "timestamp" + type = "date" + } + + columns { + name = "transactionamount" + type = "double" + } + } +} + +resource "aws_lakeformation_permissions" "test" { + permissions = ["ALL"] + principal = "IAM_ALLOWED_PRINCIPALS" + + database { + name = aws_glue_catalog_database.test.name + } + + # for consistency, ensure that admins are setup before testing + depends_on = [aws_lakeformation_data_lake_settings.test] +} +`, rName) +} + +func testAccAWSLakeFormationPermissionsConfig_databaseMultiple(rName string) string { + return fmt.Sprintf(` +data "aws_partition" "current" {} + +resource "aws_iam_role" "test" { + name = %[1]q + path = "/" + + assume_role_policy = jsonencode({ + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "glue.${data.aws_partition.current.dns_suffix}" + } + }] + Version = "2012-10-17" + }) +} + +resource "aws_iam_role" "test2" { + name = "%[1]s-2" + path = "/" + + assume_role_policy = jsonencode({ + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "glue.${data.aws_partition.current.dns_suffix}" + } + }] + Version = "2012-10-17" + }) +} + +resource "aws_glue_catalog_database" "test" { + name = %[1]q +} + +data "aws_caller_identity" "current" {} + +data "aws_iam_session_context" "current" { + arn = data.aws_caller_identity.current.arn +} + +resource "aws_lakeformation_data_lake_settings" "test" { + admins = [data.aws_iam_session_context.current.issuer_arn] +} + +resource "aws_lakeformation_permissions" "test" { + permissions = ["ALTER", "CREATE_TABLE", "DROP"] + permissions_with_grant_option = ["CREATE_TABLE"] + principal = aws_iam_role.test.arn + + database { + name = aws_glue_catalog_database.test.name + } + + # for consistency, ensure that admins are setup before testing + depends_on = [aws_lakeformation_data_lake_settings.test] +} + +resource "aws_lakeformation_permissions" "test2" { + permissions = ["ALTER", "DROP"] + principal = aws_iam_role.test2.arn + + database { + name = aws_glue_catalog_database.test.name + } + + # for consistency, ensure that admins are setup before testing + depends_on = [aws_lakeformation_data_lake_settings.test] +} +`, rName) +} + +func testAccAWSLakeFormationPermissionsConfig_dataLocation(rName string) string { + return fmt.Sprintf(` +data "aws_partition" "current" {} + +resource "aws_iam_role" "test" { + name = %[1]q + path = "/" + + assume_role_policy = jsonencode({ + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "glue.${data.aws_partition.current.dns_suffix}" + } + },{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "s3.${data.aws_partition.current.dns_suffix}" + } + }] + Version = "2012-10-17" + }) +} + +resource "aws_s3_bucket" "test" { + bucket = %[1]q + acl = "private" + force_destroy = true +} + +resource "aws_lakeformation_resource" "test" { + arn = aws_s3_bucket.test.arn + role_arn = aws_iam_role.test.arn +} + +data "aws_caller_identity" "current" {} + +data "aws_iam_session_context" "current" { + arn = data.aws_caller_identity.current.arn +} + +resource "aws_lakeformation_data_lake_settings" "test" { + admins = [data.aws_iam_session_context.current.issuer_arn] +} + +resource "aws_lakeformation_permissions" "test" { + principal = aws_iam_role.test.arn + permissions = ["DATA_LOCATION_ACCESS"] + + data_location { + arn = aws_s3_bucket.test.arn + } + + # for consistency, ensure that admins are setup before testing + depends_on = [aws_lakeformation_data_lake_settings.test] +} +`, rName) +} + +func testAccAWSLakeFormationPermissionsConfig_tableBasic(rName string) string { + return fmt.Sprintf(` +data "aws_partition" "current" {} + +resource "aws_iam_role" "test" { + name = %[1]q + path = "/" + + assume_role_policy = jsonencode({ + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "glue.${data.aws_partition.current.dns_suffix}" + } + }] + Version = "2012-10-17" + }) +} + +resource "aws_glue_catalog_database" "test" { + name = %[1]q +} + +resource "aws_glue_catalog_table" "test" { + name = %[1]q + database_name = aws_glue_catalog_database.test.name +} + +data "aws_caller_identity" "current" {} + +data "aws_iam_session_context" "current" { + arn = data.aws_caller_identity.current.arn +} + +resource "aws_lakeformation_data_lake_settings" "test" { + admins = [data.aws_iam_session_context.current.issuer_arn] +} + +resource "aws_lakeformation_permissions" "test" { + permissions = ["ALTER", "DELETE", "DESCRIBE"] + principal = aws_iam_role.test.arn + + table { + database_name = aws_glue_catalog_table.test.database_name + name = aws_glue_catalog_table.test.name + } + + # for consistency, ensure that admins are setup before testing + depends_on = [aws_lakeformation_data_lake_settings.test] +} +`, rName) +} + +func testAccAWSLakeFormationPermissionsConfig_tableIAMAllowed(rName string) string { + return fmt.Sprintf(` +data "aws_partition" "current" {} + +data "aws_caller_identity" "current" {} + +data "aws_iam_session_context" "current" { + arn = data.aws_caller_identity.current.arn +} + +resource "aws_lakeformation_data_lake_settings" "test" { + admins = [data.aws_iam_session_context.current.issuer_arn] +} + +resource "aws_glue_catalog_database" "test" { + name = %[1]q +} + +resource "aws_glue_catalog_table" "test" { + name = %[1]q + database_name = aws_glue_catalog_database.test.name + + storage_descriptor { + columns { + name = "event" + type = "string" + } + + columns { + name = "timestamp" + type = "date" + } + + columns { + name = "transactionamount" + type = "double" + } + } +} + +resource "aws_lakeformation_permissions" "test" { + permissions = ["ALL"] + principal = "IAM_ALLOWED_PRINCIPALS" + + table { + database_name = aws_glue_catalog_database.test.name + name = aws_glue_catalog_table.test.name + } +} +`, rName) +} + +func testAccAWSLakeFormationPermissionsConfig_tableImplicit(rName string) string { + return fmt.Sprintf(` +data "aws_partition" "current" {} + +resource "aws_iam_role" "test" { + name = %[1]q + path = "/" + + assume_role_policy = jsonencode({ + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "glue.${data.aws_partition.current.dns_suffix}" + } + }] + Version = "2012-10-17" + }) +} + +resource "aws_glue_catalog_database" "test" { + name = %[1]q +} + +resource "aws_glue_catalog_table" "test" { + name = %[1]q + database_name = aws_glue_catalog_database.test.name + + storage_descriptor { + columns { + name = "event" + type = "string" + } + + columns { + name = "timestamp" + type = "date" + } - assume_role_policy = < Date: Thu, 8 Jul 2021 14:30:02 -0400 Subject: [PATCH 206/398] r/lakeformation_permissions: Fix bugs --- aws/resource_aws_lakeformation_permissions.go | 48 ++++++++++++++++++- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_lakeformation_permissions.go b/aws/resource_aws_lakeformation_permissions.go index 93dc4690158..98c454df7ed 100644 --- a/aws/resource_aws_lakeformation_permissions.go +++ b/aws/resource_aws_lakeformation_permissions.go @@ -484,6 +484,18 @@ func resourceAwsLakeFormationPermissionsRead(d *schema.ResourceData, meta interf if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 { // since perm list could include TableWithColumns, get the right one for _, perm := range cleanPermissions { + if perm.Resource == nil { + continue + } + + if perm.Resource.TableWithColumns != nil && perm.Resource.TableWithColumns.ColumnWildcard != nil { + if err := d.Set("table", []interface{}{flattenLakeFormationTableWithColumnsResourceAsTable(perm.Resource.TableWithColumns)}); err != nil { + return fmt.Errorf("error setting table: %w", err) + } + tableSet = true + break + } + if perm.Resource.Table != nil { if err := d.Set("table", []interface{}{flattenLakeFormationTableResource(perm.Resource.Table)}); err != nil { return fmt.Errorf("error setting table: %w", err) @@ -491,6 +503,7 @@ func resourceAwsLakeFormationPermissionsRead(d *schema.ResourceData, meta interf tableSet = true break } + } } @@ -585,7 +598,7 @@ func resourceAwsLakeFormationPermissionsDelete(d *schema.ResourceData, meta inte _, err = conn.RevokePermissions(input) } - if tfawserr.ErrMessageContains(err, lakeformation.ErrCodeInvalidInputException, "No permissions revoked. Grantee has no") { + if tfawserr.ErrMessageContains(err, lakeformation.ErrCodeInvalidInputException, "No permissions revoked. Grantee") { return nil } @@ -616,7 +629,11 @@ func resourceAwsLakeFormationPermissionsDelete(d *schema.ResourceData, meta inte _, err = conn.RevokePermissions(input) } - if err != nil && !tfawserr.ErrMessageContains(err, lakeformation.ErrCodeInvalidInputException, "No permissions revoked. Grantee has no") { + if tfawserr.ErrMessageContains(err, lakeformation.ErrCodeInvalidInputException, "No permissions revoked. Grantee") { + return nil + } + + if err != nil { return fmt.Errorf("unable to revoke LakeFormation Permissions (input: %v): %w", input, err) } @@ -844,6 +861,33 @@ func flattenLakeFormationTableWithColumnsResource(apiObject *lakeformation.Table return tfMap } +// This only happens in very specific situations: +// (Select) TWC + ColumnWildcard = (Select) Table +// (Select) TWC + ColumnWildcard + ALL_TABLES = (Select) Table + TableWildcard +func flattenLakeFormationTableWithColumnsResourceAsTable(apiObject *lakeformation.TableWithColumnsResource) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.CatalogId; v != nil { + tfMap["catalog_id"] = aws.StringValue(v) + } + + if v := apiObject.DatabaseName; v != nil { + tfMap["database_name"] = aws.StringValue(v) + } + + if v := apiObject.Name; v != nil && aws.StringValue(v) == tflakeformation.TableNameAllTables && apiObject.ColumnWildcard != nil { + tfMap["wildcard"] = true + } else if v := apiObject.Name; v != nil { + tfMap["name"] = aws.StringValue(v) + } + + return tfMap +} + func flattenLakeFormationPermissions(apiObjects []*lakeformation.PrincipalResourcePermissions) []string { if apiObjects == nil { return nil From 2199d55c8390d720be0ce28674e431d73bd10ab0 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 8 Jul 2021 14:30:34 -0400 Subject: [PATCH 207/398] tests/lakeformation_data_lake_setting: Rework for STS --- ...s_lakeformation_data_lake_settings_test.go | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/aws/resource_aws_lakeformation_data_lake_settings_test.go b/aws/resource_aws_lakeformation_data_lake_settings_test.go index 3c3ec5375ae..fcbfc1cb636 100644 --- a/aws/resource_aws_lakeformation_data_lake_settings_test.go +++ b/aws/resource_aws_lakeformation_data_lake_settings_test.go @@ -12,7 +12,6 @@ import ( ) func testAccAWSLakeFormationDataLakeSettings_basic(t *testing.T) { - callerIdentityName := "data.aws_caller_identity.current" resourceName := "aws_lakeformation_data_lake_settings.test" resource.Test(t, resource.TestCase{ @@ -25,9 +24,9 @@ func testAccAWSLakeFormationDataLakeSettings_basic(t *testing.T) { Config: testAccAWSLakeFormationDataLakeSettingsConfig_basic, Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationDataLakeSettingsExists(resourceName), - resource.TestCheckResourceAttrPair(resourceName, "catalog_id", callerIdentityName, "account_id"), + resource.TestCheckResourceAttrPair(resourceName, "catalog_id", "data.aws_caller_identity.current", "account_id"), resource.TestCheckResourceAttr(resourceName, "admins.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "admins.0", callerIdentityName, "arn"), + resource.TestCheckResourceAttrPair(resourceName, "admins.0", "data.aws_iam_session_context.current", "issuer_arn"), ), }, }, @@ -56,7 +55,6 @@ func testAccAWSLakeFormationDataLakeSettings_disappears(t *testing.T) { } func testAccAWSLakeFormationDataLakeSettings_withoutCatalogId(t *testing.T) { - callerIdentityName := "data.aws_caller_identity.current" resourceName := "aws_lakeformation_data_lake_settings.test" resource.Test(t, resource.TestCase{ @@ -70,7 +68,7 @@ func testAccAWSLakeFormationDataLakeSettings_withoutCatalogId(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationDataLakeSettingsExists(resourceName), resource.TestCheckResourceAttr(resourceName, "admins.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "admins.0", callerIdentityName, "arn"), + resource.TestCheckResourceAttrPair(resourceName, "admins.0", "data.aws_iam_session_context.current", "issuer_arn"), ), }, }, @@ -137,6 +135,10 @@ func testAccCheckAWSLakeFormationDataLakeSettingsExists(resourceName string) res const testAccAWSLakeFormationDataLakeSettingsConfig_basic = ` data "aws_caller_identity" "current" {} +data "aws_iam_session_context" "current" { + arn = data.aws_caller_identity.current.arn +} + resource "aws_lakeformation_data_lake_settings" "test" { catalog_id = data.aws_caller_identity.current.account_id @@ -150,7 +152,7 @@ resource "aws_lakeformation_data_lake_settings" "test" { permissions = ["ALL"] } - admins = [data.aws_caller_identity.current.arn] + admins = [data.aws_iam_session_context.current.issuer_arn] trusted_resource_owners = [data.aws_caller_identity.current.account_id] } ` @@ -158,7 +160,11 @@ resource "aws_lakeformation_data_lake_settings" "test" { const testAccAWSLakeFormationDataLakeSettingsConfig_withoutCatalogId = ` data "aws_caller_identity" "current" {} +data "aws_iam_session_context" "current" { + arn = data.aws_caller_identity.current.arn +} + resource "aws_lakeformation_data_lake_settings" "test" { - admins = [data.aws_caller_identity.current.arn] + admins = [data.aws_iam_session_context.current.issuer_arn] } ` From ab8c63fc7bd50a6bc2789c91910b1f3dd4bedcd2 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 8 Jul 2021 14:31:30 -0400 Subject: [PATCH 208/398] i/lakeformation: Reduce permissions delete TO --- aws/internal/service/lakeformation/waiter/waiter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/internal/service/lakeformation/waiter/waiter.go b/aws/internal/service/lakeformation/waiter/waiter.go index 80e66a21159..ba7343e01ab 100644 --- a/aws/internal/service/lakeformation/waiter/waiter.go +++ b/aws/internal/service/lakeformation/waiter/waiter.go @@ -9,7 +9,7 @@ import ( const ( PermissionsReadyTimeout = 1 * time.Minute - PermissionsDeleteRetryTimeout = 3 * time.Minute + PermissionsDeleteRetryTimeout = 30 * time.Second StatusAvailable = "AVAILABLE" StatusNotFound = "NOT FOUND" From ddedf2ebc25c2973128ba2de81a82934f54df86f Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 8 Jul 2021 14:32:12 -0400 Subject: [PATCH 209/398] i/lakeformation: Ensure same principal --- aws/internal/service/lakeformation/waiter/status.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/aws/internal/service/lakeformation/waiter/status.go b/aws/internal/service/lakeformation/waiter/status.go index fca281e9216..80beaa016a8 100644 --- a/aws/internal/service/lakeformation/waiter/status.go +++ b/aws/internal/service/lakeformation/waiter/status.go @@ -3,6 +3,7 @@ package waiter import ( "fmt" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/lakeformation" "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -19,6 +20,10 @@ func PermissionsStatus(conn *lakeformation.LakeFormation, input *lakeformation.L continue } + if aws.StringValue(input.Principal.DataLakePrincipalIdentifier) != aws.StringValue(permission.Principal.DataLakePrincipalIdentifier) { + continue + } + permissions = append(permissions, permission) } return !lastPage From 1a3c9faad1b7deb112ec439ccb74b4fded8338d8 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 8 Jul 2021 14:33:09 -0400 Subject: [PATCH 210/398] i/lakeformation: Filter out different principals --- aws/internal/service/lakeformation/filter.go | 46 +++++++++++++------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/aws/internal/service/lakeformation/filter.go b/aws/internal/service/lakeformation/filter.go index 916df407ebd..1d53fb8fd6d 100644 --- a/aws/internal/service/lakeformation/filter.go +++ b/aws/internal/service/lakeformation/filter.go @@ -8,12 +8,6 @@ import ( "github.com/aws/aws-sdk-go/service/lakeformation" ) -const ( - TableNameAllTables = "ALL_TABLES" - TableTypeTable = "Table" - TableTypeTableWithColumns = "TableWithColumns" -) - func FilterPermissions(input *lakeformation.ListPermissionsInput, tableType string, columnNames []*string, excludedColumnNames []*string, columnWildcard bool, allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { // For most Lake Formation resources, filtering within the provider is unnecessary. The input // contains everything for AWS to give you back exactly what you want. However, many challenges @@ -29,29 +23,29 @@ func FilterPermissions(input *lakeformation.ListPermissionsInput, tableType stri // permissions in the special cases to avoid extra permissions being included. if input.Resource.Catalog != nil { - return FilterLakeFormationCatalogPermissions(allPermissions) + return FilterLakeFormationCatalogPermissions(input.Principal.DataLakePrincipalIdentifier, allPermissions) } if input.Resource.DataLocation != nil { - return FilterLakeFormationDataLocationPermissions(allPermissions) + return FilterLakeFormationDataLocationPermissions(input.Principal.DataLakePrincipalIdentifier, allPermissions) } if input.Resource.Database != nil { - return FilterLakeFormationDatabasePermissions(allPermissions) + return FilterLakeFormationDatabasePermissions(input.Principal.DataLakePrincipalIdentifier, allPermissions) } if tableType == TableTypeTableWithColumns { - return FilterLakeFormationTableWithColumnsPermissions(input.Resource.Table, columnNames, excludedColumnNames, columnWildcard, allPermissions) + return FilterLakeFormationTableWithColumnsPermissions(input.Principal.DataLakePrincipalIdentifier, input.Resource.Table, columnNames, excludedColumnNames, columnWildcard, allPermissions) } if input.Resource.Table != nil || tableType == TableTypeTable { - return FilterLakeFormationTablePermissions(input.Resource.Table, allPermissions) + return FilterLakeFormationTablePermissions(input.Principal.DataLakePrincipalIdentifier, input.Resource.Table, allPermissions) } return nil } -func FilterLakeFormationTablePermissions(table *lakeformation.TableResource, allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { +func FilterLakeFormationTablePermissions(principal *string, table *lakeformation.TableResource, allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { // CREATE PERMS (in) = ALL, ALTER, DELETE, DESCRIBE, DROP, INSERT, SELECT on Table, Name = (Table Name) // LIST PERMS (out) = ALL, ALTER, DELETE, DESCRIBE, DROP, INSERT on Table, Name = (Table Name) // LIST PERMS (out) = SELECT on TableWithColumns, Name = (Table Name), ColumnWildcard @@ -63,6 +57,10 @@ func FilterLakeFormationTablePermissions(table *lakeformation.TableResource, all var cleanPermissions []*lakeformation.PrincipalResourcePermissions for _, perm := range allPermissions { + if aws.StringValue(principal) != aws.StringValue(perm.Principal.DataLakePrincipalIdentifier) { + continue + } + if perm.Resource.TableWithColumns != nil && perm.Resource.TableWithColumns.ColumnWildcard != nil { if aws.StringValue(perm.Resource.TableWithColumns.Name) == aws.StringValue(table.Name) || (table.TableWildcard != nil && aws.StringValue(perm.Resource.TableWithColumns.Name) == TableNameAllTables) { if len(perm.Permissions) > 0 && aws.StringValue(perm.Permissions[0]) == lakeformation.PermissionSelect { @@ -94,7 +92,7 @@ func FilterLakeFormationTablePermissions(table *lakeformation.TableResource, all return cleanPermissions } -func FilterLakeFormationTableWithColumnsPermissions(twc *lakeformation.TableResource, columnNames []*string, excludedColumnNames []*string, columnWildcard bool, allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { +func FilterLakeFormationTableWithColumnsPermissions(principal *string, twc *lakeformation.TableResource, columnNames []*string, excludedColumnNames []*string, columnWildcard bool, allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { // CREATE PERMS (in) = ALL, ALTER, DELETE, DESCRIBE, DROP, INSERT, SELECT on TableWithColumns, Name = (Table Name), ColumnWildcard // LIST PERMS (out) = ALL, ALTER, DELETE, DESCRIBE, DROP, INSERT on Table, Name = (Table Name) // LIST PERMS (out) = SELECT on TableWithColumns, Name = (Table Name), ColumnWildcard @@ -102,6 +100,10 @@ func FilterLakeFormationTableWithColumnsPermissions(twc *lakeformation.TableReso var cleanPermissions []*lakeformation.PrincipalResourcePermissions for _, perm := range allPermissions { + if aws.StringValue(principal) != aws.StringValue(perm.Principal.DataLakePrincipalIdentifier) { + continue + } + if perm.Resource.TableWithColumns != nil && perm.Resource.TableWithColumns.ColumnNames != nil { if StringSlicesEqualIgnoreOrder(perm.Resource.TableWithColumns.ColumnNames, columnNames) { cleanPermissions = append(cleanPermissions, perm) @@ -130,10 +132,14 @@ func FilterLakeFormationTableWithColumnsPermissions(twc *lakeformation.TableReso return cleanPermissions } -func FilterLakeFormationCatalogPermissions(allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { +func FilterLakeFormationCatalogPermissions(principal *string, allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { var cleanPermissions []*lakeformation.PrincipalResourcePermissions for _, perm := range allPermissions { + if aws.StringValue(principal) != aws.StringValue(perm.Principal.DataLakePrincipalIdentifier) { + continue + } + if perm.Resource.Catalog != nil { cleanPermissions = append(cleanPermissions, perm) } @@ -142,10 +148,14 @@ func FilterLakeFormationCatalogPermissions(allPermissions []*lakeformation.Princ return cleanPermissions } -func FilterLakeFormationDataLocationPermissions(allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { +func FilterLakeFormationDataLocationPermissions(principal *string, allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { var cleanPermissions []*lakeformation.PrincipalResourcePermissions for _, perm := range allPermissions { + if aws.StringValue(principal) != aws.StringValue(perm.Principal.DataLakePrincipalIdentifier) { + continue + } + if perm.Resource.DataLocation != nil { cleanPermissions = append(cleanPermissions, perm) } @@ -154,10 +164,14 @@ func FilterLakeFormationDataLocationPermissions(allPermissions []*lakeformation. return cleanPermissions } -func FilterLakeFormationDatabasePermissions(allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { +func FilterLakeFormationDatabasePermissions(principal *string, allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { var cleanPermissions []*lakeformation.PrincipalResourcePermissions for _, perm := range allPermissions { + if aws.StringValue(principal) != aws.StringValue(perm.Principal.DataLakePrincipalIdentifier) { + continue + } + if perm.Resource.Database != nil { cleanPermissions = append(cleanPermissions, perm) } From db714c3a32299f7a71d9810d400bf8a53af1480f Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 8 Jul 2021 14:34:17 -0400 Subject: [PATCH 211/398] tests/d/lakeformation_data_lake_settings: Rework for STS --- ...ource_aws_lakeformation_data_lake_settings_test.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/aws/data_source_aws_lakeformation_data_lake_settings_test.go b/aws/data_source_aws_lakeformation_data_lake_settings_test.go index fa1fe4ab7d4..09b424fe346 100644 --- a/aws/data_source_aws_lakeformation_data_lake_settings_test.go +++ b/aws/data_source_aws_lakeformation_data_lake_settings_test.go @@ -8,7 +8,6 @@ import ( ) func testAccAWSLakeFormationDataLakeSettingsDataSource_basic(t *testing.T) { - callerIdentityName := "data.aws_caller_identity.current" resourceName := "data.aws_lakeformation_data_lake_settings.test" resource.Test(t, resource.TestCase{ @@ -20,9 +19,9 @@ func testAccAWSLakeFormationDataLakeSettingsDataSource_basic(t *testing.T) { { Config: testAccAWSLakeFormationDataLakeSettingsDataSourceConfig_basic, Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrPair(resourceName, "catalog_id", callerIdentityName, "account_id"), + resource.TestCheckResourceAttrPair(resourceName, "catalog_id", "data.aws_caller_identity.current", "account_id"), resource.TestCheckResourceAttr(resourceName, "admins.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "admins.0", callerIdentityName, "arn"), + resource.TestCheckResourceAttrPair(resourceName, "admins.0", "data.aws_iam_session_context.current", "issuer_arn"), ), }, }, @@ -32,9 +31,13 @@ func testAccAWSLakeFormationDataLakeSettingsDataSource_basic(t *testing.T) { const testAccAWSLakeFormationDataLakeSettingsDataSourceConfig_basic = ` data "aws_caller_identity" "current" {} +data "aws_iam_session_context" "current" { + arn = data.aws_caller_identity.current.arn +} + resource "aws_lakeformation_data_lake_settings" "test" { catalog_id = data.aws_caller_identity.current.account_id - admins = [data.aws_caller_identity.current.arn] + admins = [data.aws_iam_session_context.current.issuer_arn] } data "aws_lakeformation_data_lake_settings" "test" { From 259c816a79ebcbd833120e67463f4f64569e3b11 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 8 Jul 2021 14:34:47 -0400 Subject: [PATCH 212/398] tests/d/lakeformation_permissions: Rework for STS --- ...urce_aws_lakeformation_permissions_test.go | 168 +++++++++--------- 1 file changed, 82 insertions(+), 86 deletions(-) diff --git a/aws/data_source_aws_lakeformation_permissions_test.go b/aws/data_source_aws_lakeformation_permissions_test.go index fe111e051e2..47d2a77ca5a 100644 --- a/aws/data_source_aws_lakeformation_permissions_test.go +++ b/aws/data_source_aws_lakeformation_permissions_test.go @@ -148,28 +148,28 @@ data "aws_partition" "current" {} resource "aws_iam_role" "test" { name = %[1]q + path = "/" - assume_role_policy = < Date: Thu, 8 Jul 2021 14:35:15 -0400 Subject: [PATCH 213/398] d/lakeformation_permissions: Fix bugs --- aws/data_source_aws_lakeformation_permissions.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/aws/data_source_aws_lakeformation_permissions.go b/aws/data_source_aws_lakeformation_permissions.go index 9a9c48e777c..97963b21715 100644 --- a/aws/data_source_aws_lakeformation_permissions.go +++ b/aws/data_source_aws_lakeformation_permissions.go @@ -277,6 +277,18 @@ func dataSourceAwsLakeFormationPermissionsRead(d *schema.ResourceData, meta inte if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 { // since perm list could include TableWithColumns, get the right one for _, perm := range cleanPermissions { + if perm.Resource == nil { + continue + } + + if perm.Resource.TableWithColumns != nil && perm.Resource.TableWithColumns.ColumnWildcard != nil { + if err := d.Set("table", []interface{}{flattenLakeFormationTableWithColumnsResourceAsTable(perm.Resource.TableWithColumns)}); err != nil { + return fmt.Errorf("error setting table: %w", err) + } + tableSet = true + break + } + if perm.Resource.Table != nil { if err := d.Set("table", []interface{}{flattenLakeFormationTableResource(perm.Resource.Table)}); err != nil { return fmt.Errorf("error setting table: %w", err) From 8abe9aa435ee949a8de211ac01e64f2af44ad886 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 8 Jul 2021 14:38:45 -0400 Subject: [PATCH 214/398] r/lakeformation_permissions: Add changelog --- .changelog/20108.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changelog/20108.txt diff --git a/.changelog/20108.txt b/.changelog/20108.txt new file mode 100644 index 00000000000..5edc7273ce8 --- /dev/null +++ b/.changelog/20108.txt @@ -0,0 +1,7 @@ +```release-note:bug +resource/aws_lakeformation_permissions: Fix various problems with permissions including select-only +``` + +```release-note:bug +data-source/aws_lakeformation_permissions: Fix various problems with permissions including select-only +``` \ No newline at end of file From 08bba8df4db7b24c6c4ea0fd798611957116f319 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 8 Jul 2021 14:42:56 -0400 Subject: [PATCH 215/398] r/lakeformation_permissions: Linty McLintface --- aws/resource_aws_lakeformation_permissions_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_lakeformation_permissions_test.go b/aws/resource_aws_lakeformation_permissions_test.go index 8fcbb28dcc4..1299494e0df 100644 --- a/aws/resource_aws_lakeformation_permissions_test.go +++ b/aws/resource_aws_lakeformation_permissions_test.go @@ -2013,7 +2013,7 @@ resource "aws_lakeformation_permissions" "test" { table_with_columns { database_name = aws_glue_catalog_table.test.database_name name = aws_glue_catalog_table.test.name - wildcard = true + wildcard = true } # for consistency, ensure that admins are setup before testing From 0538764c74c173f82a5fb2530dcc3bf65b9cce64 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 8 Jul 2021 14:45:06 -0400 Subject: [PATCH 216/398] r/lakeformation_permissions: Linty McLintface --- aws/resource_aws_lakeformation_permissions_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_lakeformation_permissions_test.go b/aws/resource_aws_lakeformation_permissions_test.go index 1299494e0df..70803f348e5 100644 --- a/aws/resource_aws_lakeformation_permissions_test.go +++ b/aws/resource_aws_lakeformation_permissions_test.go @@ -1015,7 +1015,7 @@ resource "aws_glue_catalog_table" "test" { resource "aws_lakeformation_permissions" "test" { permissions = ["ALL"] principal = "IAM_ALLOWED_PRINCIPALS" - + database { name = aws_glue_catalog_database.test.name } @@ -1261,7 +1261,7 @@ resource "aws_glue_catalog_table" "test" { resource "aws_lakeformation_permissions" "test" { permissions = ["ALL"] principal = "IAM_ALLOWED_PRINCIPALS" - + table { database_name = aws_glue_catalog_database.test.name name = aws_glue_catalog_table.test.name From ede6a1a40bc820a72bc1a7dc0842412371d7666c Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 8 Jul 2021 14:52:03 -0400 Subject: [PATCH 217/398] r/lakeformation_permissions: Linty McLintface --- aws/resource_aws_lakeformation_permissions_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/aws/resource_aws_lakeformation_permissions_test.go b/aws/resource_aws_lakeformation_permissions_test.go index 70803f348e5..57db85a834a 100644 --- a/aws/resource_aws_lakeformation_permissions_test.go +++ b/aws/resource_aws_lakeformation_permissions_test.go @@ -1090,8 +1090,8 @@ resource "aws_lakeformation_permissions" "test" { } resource "aws_lakeformation_permissions" "test2" { - permissions = ["ALTER", "DROP"] - principal = aws_iam_role.test2.arn + permissions = ["ALTER", "DROP"] + principal = aws_iam_role.test2.arn database { name = aws_glue_catalog_database.test.name @@ -1118,13 +1118,13 @@ resource "aws_iam_role" "test" { Principal = { Service = "glue.${data.aws_partition.current.dns_suffix}" } - },{ + }, { Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "s3.${data.aws_partition.current.dns_suffix}" } - }] + }] Version = "2012-10-17" }) } From 9c50c5cb97f2a9ef58003e7062eca14c97ccb74f Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 8 Jul 2021 14:53:58 -0400 Subject: [PATCH 218/398] docs/r/lakeformation_permissions: Curse you trailing whitespace --- website/docs/r/lakeformation_permissions.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/lakeformation_permissions.html.markdown b/website/docs/r/lakeformation_permissions.html.markdown index d6b116689bf..a73c9e6bf31 100644 --- a/website/docs/r/lakeformation_permissions.html.markdown +++ b/website/docs/r/lakeformation_permissions.html.markdown @@ -39,7 +39,7 @@ resource "aws_lakeformation_data_lake_settings" "test" { To remove existing `IAMAllowedPrincipals` permissions, use the [AWS Lake Formation Console](https://console.aws.amazon.com/lakeformation/) or [AWS CLI](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lakeformation/batch-revoke-permissions.html). -`IAMAllowedPrincipals` is a hook to maintain backwards compatibility with AWS Glue. `IAMAllowedPrincipals` is a pseudo-entity group that acts like a Lake Formation principal. The group includes any IAM users and roles that are allowed access to your Data Catalog resources by your IAM policies. +`IAMAllowedPrincipals` is a hook to maintain backwards compatibility with AWS Glue. `IAMAllowedPrincipals` is a pseudo-entity group that acts like a Lake Formation principal. The group includes any IAM users and roles that are allowed access to your Data Catalog resources by your IAM policies. This is Lake Formation's default behavior: From b60dbffe10e0cb1a53b18747fa42a4dd3f3f6e19 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 8 Jul 2021 14:55:38 -0400 Subject: [PATCH 219/398] docs/r/lakeformation_permissions: Clarify behavior --- website/docs/r/lakeformation_permissions.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/lakeformation_permissions.html.markdown b/website/docs/r/lakeformation_permissions.html.markdown index a73c9e6bf31..fa119544576 100644 --- a/website/docs/r/lakeformation_permissions.html.markdown +++ b/website/docs/r/lakeformation_permissions.html.markdown @@ -85,7 +85,7 @@ The resulting permissions depend on whether the table had `IAMAllowedPrincipals` | Result With IAP | Result Without IAP | | ---- | ---- | -| `SELECT` on table with columns with column wildcard (i.e., all columns) | `SELECT` on `"event"` (as expected) | +| `SELECT` column wildcard (i.e., all columns) | `SELECT` on `"event"` (as expected) | ## Using Lake Formation Permissions From b880067a1fc07c1d3ccf7b2df319e25996fef593 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 8 Jul 2021 15:00:32 -0400 Subject: [PATCH 220/398] docs/r/lakeformation_permissions: Heading --- website/docs/r/lakeformation_permissions.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/lakeformation_permissions.html.markdown b/website/docs/r/lakeformation_permissions.html.markdown index fa119544576..7d98355ec11 100644 --- a/website/docs/r/lakeformation_permissions.html.markdown +++ b/website/docs/r/lakeformation_permissions.html.markdown @@ -48,7 +48,7 @@ This is Lake Formation's default behavior: For more details, see [Changing the Default Security Settings for Your Data Lake](https://docs.aws.amazon.com/lake-formation/latest/dg/change-settings.html). -### Example Problem Using `IAMAllowedPrincipals` +### Problem Using `IAMAllowedPrincipals` AWS does not support combining `IAMAllowedPrincipals` permissions and non-`IAMAllowedPrincipals` permissions. Doing so results in unexpected permissions. For example, this configuration grants a user `SELECT` on a column in a table. From ccddd60945c379e41167259fa62cf402a6a7c1ef Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 8 Jul 2021 12:43:42 -0400 Subject: [PATCH 221/398] r/aws_dx_gateway_association: Use internal finder and waiter packages. --- .../service/directconnect/finder/finder.go | 9 + aws/resource_aws_dx_gateway_association.go | 213 +++++++----------- ...urce_aws_dx_gateway_association_migrate.go | 17 +- ...rce_aws_dx_gateway_association_proposal.go | 4 +- ...ws_dx_gateway_association_proposal_test.go | 2 +- ...esource_aws_dx_gateway_association_test.go | 91 ++++---- 6 files changed, 141 insertions(+), 195 deletions(-) diff --git a/aws/internal/service/directconnect/finder/finder.go b/aws/internal/service/directconnect/finder/finder.go index 9a97f05d34d..a688455108f 100644 --- a/aws/internal/service/directconnect/finder/finder.go +++ b/aws/internal/service/directconnect/finder/finder.go @@ -23,6 +23,15 @@ func GatewayAssociationByDirectConnectGatewayIDAndAssociatedGatewayID(conn *dire return GatewayAssociation(conn, input) } +func GatewayAssociationByDirectConnectGatewayIDAndVirtualGatewayID(conn *directconnect.DirectConnect, directConnectGatewayID, virtualGatewayID string) (*directconnect.GatewayAssociation, error) { + input := &directconnect.DescribeDirectConnectGatewayAssociationsInput{ + DirectConnectGatewayId: aws.String(directConnectGatewayID), + VirtualGatewayId: aws.String(virtualGatewayID), + } + + return GatewayAssociation(conn, input) +} + func GatewayAssociation(conn *directconnect.DirectConnect, input *directconnect.DescribeDirectConnectGatewayAssociationsInput) (*directconnect.GatewayAssociation, error) { output, err := conn.DescribeDirectConnectGatewayAssociations(input) diff --git a/aws/resource_aws_dx_gateway_association.go b/aws/resource_aws_dx_gateway_association.go index 19ba70b50bf..a11e6276b91 100644 --- a/aws/resource_aws_dx_gateway_association.go +++ b/aws/resource_aws_dx_gateway_association.go @@ -9,13 +9,11 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/directconnect" "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" + tfdirectconnect "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/directconnect" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/directconnect/finder" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/directconnect/waiter" -) - -const ( - gatewayAssociationStateDeleted = "deleted" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func resourceAwsDxGatewayAssociation() *schema.Resource { @@ -114,60 +112,57 @@ func resourceAwsDxGatewayAssociation() *schema.Resource { func resourceAwsDxGatewayAssociationCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).dxconn - dxgwId := d.Get("dx_gateway_id").(string) - gwIdRaw, gwIdOk := d.GetOk("associated_gateway_id") - gwAcctIdRaw, gwAcctIdOk := d.GetOk("associated_gateway_owner_account_id") - proposalIdRaw, proposalIdOk := d.GetOk("proposal_id") + var associationID string + directConnectGatewayID := d.Get("dx_gateway_id").(string) - if gwAcctIdOk || proposalIdOk { - // Cross-account association. - if !(gwAcctIdOk && proposalIdOk) { - return fmt.Errorf("associated_gateway_owner_account_id and proposal_id must be configured") + if associatedGatewayOwnerAccount := d.Get("associated_gateway_owner_account_id").(string); associatedGatewayOwnerAccount != "" { + proposalID := d.Get("proposal_id").(string) + input := &directconnect.AcceptDirectConnectGatewayAssociationProposalInput{ + AssociatedGatewayOwnerAccount: aws.String(associatedGatewayOwnerAccount), + DirectConnectGatewayId: aws.String(directConnectGatewayID), + ProposalId: aws.String(proposalID), } - } else if !gwIdOk { - return fmt.Errorf("either associated_gateway_owner_account_id and proposal_id, or associated_gateway_id, must be configured") - } - associationId := "" - if gwAcctIdOk { - req := &directconnect.AcceptDirectConnectGatewayAssociationProposalInput{ - AssociatedGatewayOwnerAccount: aws.String(gwAcctIdRaw.(string)), - DirectConnectGatewayId: aws.String(dxgwId), - OverrideAllowedPrefixesToDirectConnectGateway: expandDxRouteFilterPrefixes(d.Get("allowed_prefixes").(*schema.Set)), - ProposalId: aws.String(proposalIdRaw.(string)), + if v, ok := d.GetOk("allowed_prefixes"); ok && v.(*schema.Set).Len() > 0 { + input.OverrideAllowedPrefixesToDirectConnectGateway = expandDirectConnectRouteFilterPrefixes(v.(*schema.Set).List()) } - log.Printf("[DEBUG] Accepting Direct Connect gateway association proposal: %#v", req) - resp, err := conn.AcceptDirectConnectGatewayAssociationProposal(req) + log.Printf("[DEBUG] Accepting Direct Connect Gateway Association Proposal: %s", input) + output, err := conn.AcceptDirectConnectGatewayAssociationProposal(input) + if err != nil { - return fmt.Errorf("error accepting Direct Connect gateway association proposal: %s", err) + return fmt.Errorf("error accepting Direct Connect Gateway Association Proposal (%s): %w", proposalID, err) } // For historical reasons the resource ID isn't set to the association ID returned from the API. - associationId = aws.StringValue(resp.DirectConnectGatewayAssociation.AssociationId) - d.SetId(dxGatewayAssociationId(dxgwId, aws.StringValue(resp.DirectConnectGatewayAssociation.AssociatedGateway.Id))) + associationID = aws.StringValue(output.DirectConnectGatewayAssociation.AssociationId) + d.SetId(tfdirectconnect.GatewayAssociationCreateResourceID(directConnectGatewayID, aws.StringValue(output.DirectConnectGatewayAssociation.AssociatedGateway.Id))) } else { - gwId := gwIdRaw.(string) + associatedGatewayID := d.Get("associated_gateway_id").(string) + input := &directconnect.CreateDirectConnectGatewayAssociationInput{ + DirectConnectGatewayId: aws.String(directConnectGatewayID), + GatewayId: aws.String(associatedGatewayID), + } - req := &directconnect.CreateDirectConnectGatewayAssociationInput{ - AddAllowedPrefixesToDirectConnectGateway: expandDxRouteFilterPrefixes(d.Get("allowed_prefixes").(*schema.Set)), - DirectConnectGatewayId: aws.String(dxgwId), - GatewayId: aws.String(gwId), + if v, ok := d.GetOk("allowed_prefixes"); ok && v.(*schema.Set).Len() > 0 { + input.AddAllowedPrefixesToDirectConnectGateway = expandDirectConnectRouteFilterPrefixes(v.(*schema.Set).List()) } - log.Printf("[DEBUG] Creating Direct Connect gateway association: %#v", req) - resp, err := conn.CreateDirectConnectGatewayAssociation(req) + log.Printf("[DEBUG] Creating Direct Connect Gateway Association: %s", input) + output, err := conn.CreateDirectConnectGatewayAssociation(input) + if err != nil { - return fmt.Errorf("error creating Direct Connect gateway association: %s", err) + return fmt.Errorf("error creating Direct Connect Gateway Association (%s/%s): %w", directConnectGatewayID, associatedGatewayID, err) } // For historical reasons the resource ID isn't set to the association ID returned from the API. - associationId = aws.StringValue(resp.DirectConnectGatewayAssociation.AssociationId) - d.SetId(dxGatewayAssociationId(dxgwId, gwId)) + associationID = aws.StringValue(output.DirectConnectGatewayAssociation.AssociationId) + d.SetId(tfdirectconnect.GatewayAssociationCreateResourceID(directConnectGatewayID, associatedGatewayID)) } - d.Set("dx_gateway_association_id", associationId) - if _, err := waiter.GatewayAssociationCreated(conn, associationId, d.Timeout(schema.TimeoutCreate)); err != nil { + d.Set("dx_gateway_association_id", associationID) + + if _, err := waiter.GatewayAssociationCreated(conn, associationID, d.Timeout(schema.TimeoutCreate)); err != nil { return fmt.Errorf("error waiting for Direct Connect Gateway Association (%s) to create: %w", d.Id(), err) } @@ -177,30 +172,30 @@ func resourceAwsDxGatewayAssociationCreate(d *schema.ResourceData, meta interfac func resourceAwsDxGatewayAssociationRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).dxconn - associationId := d.Get("dx_gateway_association_id").(string) - assocRaw, state, err := dxGatewayAssociationStateRefresh(conn, associationId)() - if err != nil { - return fmt.Errorf("error reading Direct Connect gateway association (%s): %s", d.Id(), err) - } - if state == gatewayAssociationStateDeleted { - log.Printf("[WARN] Direct Connect gateway association (%s) not found, removing from state", d.Id()) + associationID := d.Get("dx_gateway_association_id").(string) + + output, err := finder.GatewayAssociationByID(conn, associationID) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] Direct Connect Gateway Association (%s) not found, removing from state", d.Id()) d.SetId("") return nil } - assoc := assocRaw.(*directconnect.GatewayAssociation) - - err = d.Set("allowed_prefixes", flattenDxRouteFilterPrefixes(assoc.AllowedPrefixesToDirectConnectGateway)) if err != nil { - return fmt.Errorf("error setting allowed_prefixes: %s", err) + return fmt.Errorf("error reading Direct Connect Gateway Association (%s): %w", d.Id(), err) + } + + if err := d.Set("allowed_prefixes", flattenDirectConnectRouteFilterPrefixes(output.AllowedPrefixesToDirectConnectGateway)); err != nil { + return fmt.Errorf("error setting allowed_prefixes: %w", err) } - d.Set("associated_gateway_id", assoc.AssociatedGateway.Id) - d.Set("associated_gateway_owner_account_id", assoc.AssociatedGateway.OwnerAccount) - d.Set("associated_gateway_type", assoc.AssociatedGateway.Type) - d.Set("dx_gateway_association_id", assoc.AssociationId) - d.Set("dx_gateway_id", assoc.DirectConnectGatewayId) - d.Set("dx_gateway_owner_account_id", assoc.DirectConnectGatewayOwnerAccount) + d.Set("associated_gateway_id", output.AssociatedGateway.Id) + d.Set("associated_gateway_owner_account_id", output.AssociatedGateway.OwnerAccount) + d.Set("associated_gateway_type", output.AssociatedGateway.Type) + d.Set("dx_gateway_association_id", output.AssociationId) + d.Set("dx_gateway_id", output.DirectConnectGatewayId) + d.Set("dx_gateway_owner_account_id", output.DirectConnectGatewayOwnerAccount) return nil } @@ -208,30 +203,31 @@ func resourceAwsDxGatewayAssociationRead(d *schema.ResourceData, meta interface{ func resourceAwsDxGatewayAssociationUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).dxconn - if d.HasChange("allowed_prefixes") { - associationId := d.Get("dx_gateway_association_id").(string) + associationID := d.Get("dx_gateway_association_id").(string) + input := &directconnect.UpdateDirectConnectGatewayAssociationInput{ + AssociationId: aws.String(associationID), + } - oraw, nraw := d.GetChange("allowed_prefixes") - o := oraw.(*schema.Set) - n := nraw.(*schema.Set) - del := o.Difference(n) - add := n.Difference(o) + oraw, nraw := d.GetChange("allowed_prefixes") + o, n := oraw.(*schema.Set), nraw.(*schema.Set) - req := &directconnect.UpdateDirectConnectGatewayAssociationInput{ - AddAllowedPrefixesToDirectConnectGateway: expandDxRouteFilterPrefixes(add), - AssociationId: aws.String(associationId), - RemoveAllowedPrefixesToDirectConnectGateway: expandDxRouteFilterPrefixes(del), - } + if add := n.Difference(o); add.Len() > 0 { + input.AddAllowedPrefixesToDirectConnectGateway = expandDirectConnectRouteFilterPrefixes(add.List()) + } - log.Printf("[DEBUG] Updating Direct Connect gateway association: %#v", req) - _, err := conn.UpdateDirectConnectGatewayAssociation(req) - if err != nil { - return fmt.Errorf("error updating Direct Connect gateway association (%s): %s", d.Id(), err) - } + if del := o.Difference(n); del.Len() > 0 { + input.RemoveAllowedPrefixesToDirectConnectGateway = expandDirectConnectRouteFilterPrefixes(del.List()) + } - if _, err := waiter.GatewayAssociationUpdated(conn, associationId, d.Timeout(schema.TimeoutUpdate)); err != nil { - return fmt.Errorf("error waiting for Direct Connect Gateway Association (%s) to update: %w", d.Id(), err) - } + log.Printf("[DEBUG] Updating Direct Connect Gateway Association: %s", input) + _, err := conn.UpdateDirectConnectGatewayAssociation(input) + + if err != nil { + return fmt.Errorf("error updating Direct Connect Gateway Association (%s): %w", d.Id(), err) + } + + if _, err := waiter.GatewayAssociationUpdated(conn, associationID, d.Timeout(schema.TimeoutUpdate)); err != nil { + return fmt.Errorf("error waiting for Direct Connect Gateway Association (%s) to update: %w", d.Id(), err) } return resourceAwsDxGatewayAssociationRead(d, meta) @@ -263,69 +259,26 @@ func resourceAwsDxGatewayAssociationDelete(d *schema.ResourceData, meta interfac } func resourceAwsDxGatewayAssociationImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + conn := meta.(*AWSClient).dxconn + parts := strings.Split(d.Id(), "/") + if len(parts) != 2 { - return []*schema.ResourceData{}, fmt.Errorf("Wrong format of resource: %s. Please follow 'dx-gw-id/gw-id'", d.Id()) + return nil, fmt.Errorf("Incorrect resource ID format: %q. Expected DXGATEWAYID/ASSOCIATEDGATEWAYID", d.Id()) } - dxgwId := parts[0] - gwId := parts[1] - id := dxGatewayAssociationId(dxgwId, gwId) - log.Printf("[DEBUG] Importing Direct Connect gateway association %s/%s", dxgwId, gwId) + directConnectGatewayID := parts[0] + associatedGatewayID := parts[1] - conn := meta.(*AWSClient).dxconn + output, err := finder.GatewayAssociationByDirectConnectGatewayIDAndAssociatedGatewayID(conn, directConnectGatewayID, associatedGatewayID) - resp, err := conn.DescribeDirectConnectGatewayAssociations(&directconnect.DescribeDirectConnectGatewayAssociationsInput{ - AssociatedGatewayId: aws.String(gwId), - DirectConnectGatewayId: aws.String(dxgwId), - }) if err != nil { return nil, err } - if n := len(resp.DirectConnectGatewayAssociations); n != 1 { - return nil, fmt.Errorf("Found %d Direct Connect gateway associations for %s, expected 1", n, id) - } - d.SetId(id) - d.Set("dx_gateway_id", resp.DirectConnectGatewayAssociations[0].DirectConnectGatewayId) - d.Set("dx_gateway_association_id", resp.DirectConnectGatewayAssociations[0].AssociationId) + d.SetId(tfdirectconnect.GatewayAssociationCreateResourceID(directConnectGatewayID, associatedGatewayID)) + d.Set("dx_gateway_id", output.DirectConnectGatewayId) + d.Set("dx_gateway_association_id", output.AssociationId) return []*schema.ResourceData{d}, nil } - -func dxGatewayAssociationStateRefresh(conn *directconnect.DirectConnect, associationId string) resource.StateRefreshFunc { - return func() (interface{}, string, error) { - resp, err := conn.DescribeDirectConnectGatewayAssociations(&directconnect.DescribeDirectConnectGatewayAssociationsInput{ - AssociationId: aws.String(associationId), - }) - if err != nil { - return nil, "", err - } - - n := len(resp.DirectConnectGatewayAssociations) - switch n { - case 0: - return "", gatewayAssociationStateDeleted, nil - - case 1: - assoc := resp.DirectConnectGatewayAssociations[0] - - if stateChangeError := aws.StringValue(assoc.StateChangeError); stateChangeError != "" { - id := dxGatewayAssociationId( - aws.StringValue(resp.DirectConnectGatewayAssociations[0].DirectConnectGatewayId), - aws.StringValue(resp.DirectConnectGatewayAssociations[0].AssociatedGateway.Id)) - log.Printf("[INFO] Direct Connect gateway association (%s) state change error: %s", id, stateChangeError) - } - - return assoc, aws.StringValue(assoc.AssociationState), nil - - default: - return nil, "", fmt.Errorf("Found %d Direct Connect gateway associations for %s, expected 1", n, associationId) - } - } -} - -// Terraform resource ID. -func dxGatewayAssociationId(dxgwId, gwId string) string { - return fmt.Sprintf("ga-%s%s", dxgwId, gwId) -} diff --git a/aws/resource_aws_dx_gateway_association_migrate.go b/aws/resource_aws_dx_gateway_association_migrate.go index 2fd6cfd57b4..1920c97153e 100644 --- a/aws/resource_aws_dx_gateway_association_migrate.go +++ b/aws/resource_aws_dx_gateway_association_migrate.go @@ -2,12 +2,11 @@ package aws import ( "context" - "fmt" "log" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/directconnect/finder" ) func resourceAwsDxGatewayAssociationResourceV0() *schema.Resource { @@ -78,23 +77,17 @@ func resourceAwsDxGatewayAssociationResourceV0() *schema.Resource { func resourceAwsDxGatewayAssociationStateUpgradeV0(_ context.Context, rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) { conn := meta.(*AWSClient).dxconn - log.Println("[INFO] Found Direct Connect gateway association state v0; migrating to v1") + log.Println("[INFO] Found Direct Connect Gateway Association state v0; migrating to v1") // dx_gateway_association_id was introduced in v2.8.0. Handle the case where it's not yet present. if v, ok := rawState["dx_gateway_association_id"]; !ok || v == nil { - resp, err := conn.DescribeDirectConnectGatewayAssociations(&directconnect.DescribeDirectConnectGatewayAssociationsInput{ - DirectConnectGatewayId: aws.String(rawState["dx_gateway_id"].(string)), - VirtualGatewayId: aws.String(rawState["vpn_gateway_id"].(string)), - }) + output, err := finder.GatewayAssociationByDirectConnectGatewayIDAndVirtualGatewayID(conn, rawState["dx_gateway_id"].(string), rawState["vpn_gateway_id"].(string)) + if err != nil { return nil, err } - if len(resp.DirectConnectGatewayAssociations) == 0 { - return nil, fmt.Errorf("Direct Connect gateway association not found, remove from state using 'terraform state rm'") - } - - rawState["dx_gateway_association_id"] = aws.StringValue(resp.DirectConnectGatewayAssociations[0].AssociationId) + rawState["dx_gateway_association_id"] = aws.StringValue(output.AssociationId) } return rawState, nil diff --git a/aws/resource_aws_dx_gateway_association_proposal.go b/aws/resource_aws_dx_gateway_association_proposal.go index b5fc3d40997..07be87a10da 100644 --- a/aws/resource_aws_dx_gateway_association_proposal.go +++ b/aws/resource_aws_dx_gateway_association_proposal.go @@ -242,7 +242,7 @@ func resourceAwsDxGatewayAssociationProposalImport(d *schema.ResourceData, meta associatedGatewayID := parts[2] if proposalID == "" || directConnectGatewayID == "" || associatedGatewayID == "" { - return nil, fmt.Errorf("Incorrect resource ID format: %q. PROPOSALID, DXGATEWAYID and TARGETGATEWAYID must not be empty strings", d.Id()) + return nil, fmt.Errorf("Incorrect resource ID format: %q. PROPOSALID, DXGATEWAYID and ASSOCIATEDGATEWAYID must not be empty strings", d.Id()) } // Use pseudo-proposal ID and actual DirectConnectGatewayId and AssociatedGatewayId. @@ -251,7 +251,7 @@ func resourceAwsDxGatewayAssociationProposalImport(d *schema.ResourceData, meta d.Set("dx_gateway_id", directConnectGatewayID) default: - return nil, fmt.Errorf("Incorrect resource ID format: %q. Expected PROPOSALID or PROPOSALID/DXGATEWAYID/TARGETGATEWAYID", d.Id()) + return nil, fmt.Errorf("Incorrect resource ID format: %q. Expected PROPOSALID or PROPOSALID/DXGATEWAYID/ASSOCIATEDGATEWAYID", d.Id()) } return []*schema.ResourceData{d}, nil diff --git a/aws/resource_aws_dx_gateway_association_proposal_test.go b/aws/resource_aws_dx_gateway_association_proposal_test.go index c77eed169a1..a696eddccb3 100644 --- a/aws/resource_aws_dx_gateway_association_proposal_test.go +++ b/aws/resource_aws_dx_gateway_association_proposal_test.go @@ -324,7 +324,7 @@ func testAccCheckAwsDxGatewayAssociationProposalExists(resourceName string, gate } if rs.Primary.ID == "" { - return fmt.Errorf("No Direct Connect Gateway Association Proposal ID is set") + return fmt.Errorf("No ID is set") } conn := testAccProvider.Meta().(*AWSClient).dxconn diff --git a/aws/resource_aws_dx_gateway_association_test.go b/aws/resource_aws_dx_gateway_association_test.go index c57fc0efaac..cbfe01d1303 100644 --- a/aws/resource_aws_dx_gateway_association_test.go +++ b/aws/resource_aws_dx_gateway_association_test.go @@ -208,14 +208,14 @@ func TestAccAwsDxGatewayAssociation_basicVpnGatewaySingleAccount(t *testing.T) { Config: testAccDxGatewayAssociationConfig_basicVpnGatewaySingleAccount(rName, rBgpAsn), Check: resource.ComposeTestCheckFunc( testAccCheckAwsDxGatewayAssociationExists(resourceName, &ga, &gap), - resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", resourceNameDxGw, "id"), + resource.TestCheckResourceAttr(resourceName, "allowed_prefixes.#", "1"), + resource.TestCheckTypeSetElemAttr(resourceName, "allowed_prefixes.*", "10.255.255.0/28"), resource.TestCheckResourceAttrPair(resourceName, "associated_gateway_id", resourceNameVgw, "id"), - resource.TestCheckResourceAttrSet(resourceName, "dx_gateway_association_id"), - resource.TestCheckResourceAttr(resourceName, "associated_gateway_type", "virtualPrivateGateway"), testAccCheckResourceAttrAccountID(resourceName, "associated_gateway_owner_account_id"), + resource.TestCheckResourceAttr(resourceName, "associated_gateway_type", "virtualPrivateGateway"), + resource.TestCheckResourceAttrSet(resourceName, "dx_gateway_association_id"), + resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", resourceNameDxGw, "id"), testAccCheckResourceAttrAccountID(resourceName, "dx_gateway_owner_account_id"), - resource.TestCheckResourceAttr(resourceName, "allowed_prefixes.#", "1"), - resource.TestCheckTypeSetElemAttr(resourceName, "allowed_prefixes.*", "10.255.255.0/28"), ), }, { @@ -239,10 +239,7 @@ func TestAccAwsDxGatewayAssociation_basicVpnGatewayCrossAccount(t *testing.T) { var gap directconnect.GatewayAssociationProposal resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { - testAccPreCheck(t) - testAccAlternateAccountPreCheck(t) - }, + PreCheck: func() { testAccPreCheck(t); testAccAlternateAccountPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, directconnect.EndpointsID), ProviderFactories: testAccProviderFactoriesAlternate(&providers), CheckDestroy: testAccCheckAwsDxGatewayAssociationDestroy, @@ -251,15 +248,15 @@ func TestAccAwsDxGatewayAssociation_basicVpnGatewayCrossAccount(t *testing.T) { Config: testAccDxGatewayAssociationConfig_basicVpnGatewayCrossAccount(rName, rBgpAsn), Check: resource.ComposeTestCheckFunc( testAccCheckAwsDxGatewayAssociationExists(resourceName, &ga, &gap), - resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", resourceNameDxGw, "id"), + resource.TestCheckResourceAttr(resourceName, "allowed_prefixes.#", "1"), + resource.TestCheckTypeSetElemAttr(resourceName, "allowed_prefixes.*", "10.255.255.0/28"), resource.TestCheckResourceAttrPair(resourceName, "associated_gateway_id", resourceNameVgw, "id"), - resource.TestCheckResourceAttrSet(resourceName, "dx_gateway_association_id"), - resource.TestCheckResourceAttr(resourceName, "associated_gateway_type", "virtualPrivateGateway"), testAccCheckResourceAttrAccountID(resourceName, "associated_gateway_owner_account_id"), + resource.TestCheckResourceAttr(resourceName, "associated_gateway_type", "virtualPrivateGateway"), + resource.TestCheckResourceAttrSet(resourceName, "dx_gateway_association_id"), + resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", resourceNameDxGw, "id"), // dx_gateway_owner_account_id is the "awsalternate" provider's account ID. // testAccCheckResourceAttrAccountID(resourceName, "dx_gateway_owner_account_id"), - resource.TestCheckResourceAttr(resourceName, "allowed_prefixes.#", "1"), - resource.TestCheckTypeSetElemAttr(resourceName, "allowed_prefixes.*", "10.255.255.0/28"), ), }, }, @@ -285,15 +282,15 @@ func TestAccAwsDxGatewayAssociation_basicTransitGatewaySingleAccount(t *testing. Config: testAccDxGatewayAssociationConfig_basicTransitGatewaySingleAccount(rName, rBgpAsn), Check: resource.ComposeTestCheckFunc( testAccCheckAwsDxGatewayAssociationExists(resourceName, &ga, &gap), - resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", resourceNameDxGw, "id"), - resource.TestCheckResourceAttrPair(resourceName, "associated_gateway_id", resourceNameTgw, "id"), - resource.TestCheckResourceAttrSet(resourceName, "dx_gateway_association_id"), - resource.TestCheckResourceAttr(resourceName, "associated_gateway_type", "transitGateway"), - testAccCheckResourceAttrAccountID(resourceName, "associated_gateway_owner_account_id"), - testAccCheckResourceAttrAccountID(resourceName, "dx_gateway_owner_account_id"), resource.TestCheckResourceAttr(resourceName, "allowed_prefixes.#", "2"), resource.TestCheckTypeSetElemAttr(resourceName, "allowed_prefixes.*", "10.255.255.0/30"), resource.TestCheckTypeSetElemAttr(resourceName, "allowed_prefixes.*", "10.255.255.8/30"), + resource.TestCheckResourceAttrPair(resourceName, "associated_gateway_id", resourceNameTgw, "id"), + testAccCheckResourceAttrAccountID(resourceName, "associated_gateway_owner_account_id"), + resource.TestCheckResourceAttr(resourceName, "associated_gateway_type", "transitGateway"), + resource.TestCheckResourceAttrSet(resourceName, "dx_gateway_association_id"), + resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", resourceNameDxGw, "id"), + testAccCheckResourceAttrAccountID(resourceName, "dx_gateway_owner_account_id"), ), }, { @@ -317,10 +314,7 @@ func TestAccAwsDxGatewayAssociation_basicTransitGatewayCrossAccount(t *testing.T var gap directconnect.GatewayAssociationProposal resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { - testAccPreCheck(t) - testAccAlternateAccountPreCheck(t) - }, + PreCheck: func() { testAccPreCheck(t); testAccAlternateAccountPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, directconnect.EndpointsID), ProviderFactories: testAccProviderFactoriesAlternate(&providers), CheckDestroy: testAccCheckAwsDxGatewayAssociationDestroy, @@ -329,16 +323,16 @@ func TestAccAwsDxGatewayAssociation_basicTransitGatewayCrossAccount(t *testing.T Config: testAccDxGatewayAssociationConfig_basicTransitGatewayCrossAccount(rName, rBgpAsn), Check: resource.ComposeTestCheckFunc( testAccCheckAwsDxGatewayAssociationExists(resourceName, &ga, &gap), - resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", resourceNameDxGw, "id"), + resource.TestCheckResourceAttr(resourceName, "allowed_prefixes.#", "2"), + resource.TestCheckTypeSetElemAttr(resourceName, "allowed_prefixes.*", "10.255.255.0/30"), + resource.TestCheckTypeSetElemAttr(resourceName, "allowed_prefixes.*", "10.255.255.8/30"), resource.TestCheckResourceAttrPair(resourceName, "associated_gateway_id", resourceNameTgw, "id"), - resource.TestCheckResourceAttrSet(resourceName, "dx_gateway_association_id"), - resource.TestCheckResourceAttr(resourceName, "associated_gateway_type", "transitGateway"), testAccCheckResourceAttrAccountID(resourceName, "associated_gateway_owner_account_id"), + resource.TestCheckResourceAttr(resourceName, "associated_gateway_type", "transitGateway"), + resource.TestCheckResourceAttrSet(resourceName, "dx_gateway_association_id"), + resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", resourceNameDxGw, "id"), // dx_gateway_owner_account_id is the "awsalternate" provider's account ID. // testAccCheckResourceAttrAccountID(resourceName, "dx_gateway_owner_account_id"), - resource.TestCheckResourceAttr(resourceName, "allowed_prefixes.#", "2"), - resource.TestCheckTypeSetElemAttr(resourceName, "allowed_prefixes.*", "10.255.255.0/30"), - resource.TestCheckTypeSetElemAttr(resourceName, "allowed_prefixes.*", "10.255.255.8/30"), ), }, }, @@ -364,12 +358,12 @@ func TestAccAwsDxGatewayAssociation_multiVpnGatewaysSingleAccount(t *testing.T) Check: resource.ComposeTestCheckFunc( testAccCheckAwsDxGatewayAssociationExists(resourceName1, &ga, &gap), testAccCheckAwsDxGatewayAssociationExists(resourceName2, &ga, &gap), - resource.TestCheckResourceAttrSet(resourceName1, "dx_gateway_association_id"), resource.TestCheckResourceAttr(resourceName1, "allowed_prefixes.#", "1"), resource.TestCheckTypeSetElemAttr(resourceName1, "allowed_prefixes.*", "10.255.255.0/28"), - resource.TestCheckResourceAttrSet(resourceName2, "dx_gateway_association_id"), + resource.TestCheckResourceAttrSet(resourceName1, "dx_gateway_association_id"), resource.TestCheckResourceAttr(resourceName2, "allowed_prefixes.#", "1"), resource.TestCheckTypeSetElemAttr(resourceName2, "allowed_prefixes.*", "10.255.255.16/28"), + resource.TestCheckResourceAttrSet(resourceName2, "dx_gateway_association_id"), ), }, }, @@ -395,12 +389,12 @@ func TestAccAwsDxGatewayAssociation_allowedPrefixesVpnGatewaySingleAccount(t *te Config: testAccDxGatewayAssociationConfig_allowedPrefixesVpnGatewaySingleAccount(rName, rBgpAsn), Check: resource.ComposeTestCheckFunc( testAccCheckAwsDxGatewayAssociationExists(resourceName, &ga, &gap), - resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", resourceNameDxGw, "id"), - resource.TestCheckResourceAttrPair(resourceName, "associated_gateway_id", resourceNameVgw, "id"), - resource.TestCheckResourceAttrSet(resourceName, "dx_gateway_association_id"), resource.TestCheckResourceAttr(resourceName, "allowed_prefixes.#", "2"), resource.TestCheckTypeSetElemAttr(resourceName, "allowed_prefixes.*", "10.255.255.0/30"), resource.TestCheckTypeSetElemAttr(resourceName, "allowed_prefixes.*", "10.255.255.8/30"), + resource.TestCheckResourceAttrPair(resourceName, "associated_gateway_id", resourceNameVgw, "id"), + resource.TestCheckResourceAttrSet(resourceName, "dx_gateway_association_id"), + resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", resourceNameDxGw, "id"), ), }, { @@ -432,10 +426,7 @@ func TestAccAwsDxGatewayAssociation_allowedPrefixesVpnGatewayCrossAccount(t *tes var gap directconnect.GatewayAssociationProposal resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { - testAccPreCheck(t) - testAccAlternateAccountPreCheck(t) - }, + PreCheck: func() { testAccPreCheck(t); testAccAlternateAccountPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, directconnect.EndpointsID), ProviderFactories: testAccProviderFactoriesAlternate(&providers), CheckDestroy: testAccCheckAwsDxGatewayAssociationDestroy, @@ -444,11 +435,11 @@ func TestAccAwsDxGatewayAssociation_allowedPrefixesVpnGatewayCrossAccount(t *tes Config: testAccDxGatewayAssociationConfig_allowedPrefixesVpnGatewayCrossAccount(rName, rBgpAsn), Check: resource.ComposeTestCheckFunc( testAccCheckAwsDxGatewayAssociationExists(resourceName, &ga, &gap), - resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", resourceNameDxGw, "id"), - resource.TestCheckResourceAttrPair(resourceName, "associated_gateway_id", resourceNameVgw, "id"), - resource.TestCheckResourceAttrSet(resourceName, "dx_gateway_association_id"), resource.TestCheckResourceAttr(resourceName, "allowed_prefixes.#", "1"), resource.TestCheckTypeSetElemAttr(resourceName, "allowed_prefixes.*", "10.255.255.8/29"), + resource.TestCheckResourceAttrPair(resourceName, "associated_gateway_id", resourceNameVgw, "id"), + resource.TestCheckResourceAttrSet(resourceName, "dx_gateway_association_id"), + resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", resourceNameDxGw, "id"), ), // Accepting the proposal with overridden prefixes changes the returned RequestedAllowedPrefixesToDirectConnectGateway value (allowed_prefixes attribute). ExpectNonEmptyPlan: true, @@ -457,12 +448,12 @@ func TestAccAwsDxGatewayAssociation_allowedPrefixesVpnGatewayCrossAccount(t *tes Config: testAccDxGatewayAssociationConfig_allowedPrefixesVpnGatewayCrossAccountUpdated(rName, rBgpAsn), Check: resource.ComposeTestCheckFunc( testAccCheckAwsDxGatewayAssociationExists(resourceName, &ga, &gap), - resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", resourceNameDxGw, "id"), - resource.TestCheckResourceAttrPair(resourceName, "associated_gateway_id", resourceNameVgw, "id"), - resource.TestCheckResourceAttrSet(resourceName, "dx_gateway_association_id"), resource.TestCheckResourceAttr(resourceName, "allowed_prefixes.#", "2"), resource.TestCheckTypeSetElemAttr(resourceName, "allowed_prefixes.*", "10.255.255.0/30"), resource.TestCheckTypeSetElemAttr(resourceName, "allowed_prefixes.*", "10.255.255.8/30"), + resource.TestCheckResourceAttrPair(resourceName, "associated_gateway_id", resourceNameVgw, "id"), + resource.TestCheckResourceAttrSet(resourceName, "dx_gateway_association_id"), + resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", resourceNameDxGw, "id"), ), }, }, @@ -472,16 +463,14 @@ func TestAccAwsDxGatewayAssociation_allowedPrefixesVpnGatewayCrossAccount(t *tes func TestAccAwsDxGatewayAssociation_recreateProposal(t *testing.T) { var providers []*schema.Provider resourceName := "aws_dx_gateway_association.test" + resourceNameProposal := "aws_dx_gateway_association_proposal.test" rName := acctest.RandomWithPrefix("tf-acc-test") rBgpAsn := acctest.RandIntRange(64512, 65534) var ga1, ga2 directconnect.GatewayAssociation var gap1, gap2 directconnect.GatewayAssociationProposal resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { - testAccPreCheck(t) - testAccAlternateAccountPreCheck(t) - }, + PreCheck: func() { testAccPreCheck(t); testAccAlternateAccountPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, directconnect.EndpointsID), ProviderFactories: testAccProviderFactoriesAlternate(&providers), CheckDestroy: testAccCheckAwsDxGatewayAssociationDestroy, @@ -490,7 +479,7 @@ func TestAccAwsDxGatewayAssociation_recreateProposal(t *testing.T) { Config: testAccDxGatewayAssociationConfig_basicVpnGatewayCrossAccount(rName, rBgpAsn), Check: resource.ComposeTestCheckFunc( testAccCheckAwsDxGatewayAssociationExists(resourceName, &ga1, &gap1), - testAccCheckResourceDisappears(testAccProvider, resourceAwsDxGatewayAssociationProposal(), resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsDxGatewayAssociationProposal(), resourceNameProposal), ), ExpectNonEmptyPlan: true, }, @@ -592,6 +581,7 @@ func testAccCheckAwsDxGatewayAssociationStateUpgradeV0(name string) resource.Tes if !ok { return fmt.Errorf("Not found: %s", name) } + if rs.Primary.ID == "" { return fmt.Errorf("No ID is set") } @@ -602,6 +592,7 @@ func testAccCheckAwsDxGatewayAssociationStateUpgradeV0(name string) resource.Tes } updatedRawState, err := resourceAwsDxGatewayAssociationStateUpgradeV0(context.Background(), rawState, testAccProvider.Meta()) + if err != nil { return err } From 9a5d4db5492ab615d0cf4245908dcb1300fb4664 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 8 Jul 2021 15:03:06 -0400 Subject: [PATCH 222/398] r/aws_dx_gateway: Use internal finder and waiter packages. --- .../service/directconnect/finder/finder.go | 33 ++++ .../service/directconnect/waiter/status.go | 16 ++ .../service/directconnect/waiter/waiter.go | 38 ++++ aws/resource_aws_dx_gateway.go | 125 +++++-------- aws/resource_aws_dx_gateway_test.go | 166 +++++++++++------- 5 files changed, 232 insertions(+), 146 deletions(-) diff --git a/aws/internal/service/directconnect/finder/finder.go b/aws/internal/service/directconnect/finder/finder.go index a688455108f..5bfca4c06d5 100644 --- a/aws/internal/service/directconnect/finder/finder.go +++ b/aws/internal/service/directconnect/finder/finder.go @@ -6,6 +6,39 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) +func GatewayByID(conn *directconnect.DirectConnect, id string) (*directconnect.Gateway, error) { + input := &directconnect.DescribeDirectConnectGatewaysInput{ + DirectConnectGatewayId: aws.String(id), + } + + output, err := conn.DescribeDirectConnectGateways(input) + + if err != nil { + return nil, err + } + + if output == nil || len(output.DirectConnectGateways) == 0 || output.DirectConnectGateways[0] == nil { + return nil, &resource.NotFoundError{ + Message: "Empty result", + LastRequest: input, + } + } + + // TODO Check for multiple results. + // TODO https://github.com/hashicorp/terraform-provider-aws/pull/17613. + + gateway := output.DirectConnectGateways[0] + + if state := aws.StringValue(gateway.DirectConnectGatewayState); state == directconnect.GatewayStateDeleted { + return nil, &resource.NotFoundError{ + Message: state, + LastRequest: input, + } + } + + return gateway, nil +} + func GatewayAssociationByID(conn *directconnect.DirectConnect, id string) (*directconnect.GatewayAssociation, error) { input := &directconnect.DescribeDirectConnectGatewayAssociationsInput{ AssociationId: aws.String(id), diff --git a/aws/internal/service/directconnect/waiter/status.go b/aws/internal/service/directconnect/waiter/status.go index 98e90d4097b..2513c615b17 100644 --- a/aws/internal/service/directconnect/waiter/status.go +++ b/aws/internal/service/directconnect/waiter/status.go @@ -8,6 +8,22 @@ import ( "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) +func GatewayState(conn *directconnect.DirectConnect, id string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := finder.GatewayByID(conn, id) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, aws.StringValue(output.DirectConnectGatewayState), nil + } +} + func GatewayAssociationState(conn *directconnect.DirectConnect, id string) resource.StateRefreshFunc { return func() (interface{}, string, error) { output, err := finder.GatewayAssociationByID(conn, id) diff --git a/aws/internal/service/directconnect/waiter/waiter.go b/aws/internal/service/directconnect/waiter/waiter.go index ab54c49cb62..01fe6ad7971 100644 --- a/aws/internal/service/directconnect/waiter/waiter.go +++ b/aws/internal/service/directconnect/waiter/waiter.go @@ -10,6 +10,44 @@ import ( "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) +func GatewayCreated(conn *directconnect.DirectConnect, id string, timeout time.Duration) (*directconnect.Gateway, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{directconnect.GatewayStatePending}, + Target: []string{directconnect.GatewayStateAvailable}, + Refresh: GatewayState(conn, id), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*directconnect.Gateway); ok { + tfresource.SetLastError(err, errors.New(aws.StringValue(output.StateChangeError))) + + return output, err + } + + return nil, err +} + +func GatewayDeleted(conn *directconnect.DirectConnect, id string, timeout time.Duration) (*directconnect.Gateway, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{directconnect.GatewayStatePending, directconnect.GatewayStateAvailable, directconnect.GatewayStateDeleting}, + Target: []string{}, + Refresh: GatewayState(conn, id), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*directconnect.Gateway); ok { + tfresource.SetLastError(err, errors.New(aws.StringValue(output.StateChangeError))) + + return output, err + } + + return nil, err +} + func GatewayAssociationCreated(conn *directconnect.DirectConnect, id string, timeout time.Duration) (*directconnect.GatewayAssociation, error) { stateConf := &resource.StateChangeConf{ Pending: []string{directconnect.GatewayAssociationStateAssociating}, diff --git a/aws/resource_aws_dx_gateway.go b/aws/resource_aws_dx_gateway.go index 8a2b70de309..e27598f9cca 100644 --- a/aws/resource_aws_dx_gateway.go +++ b/aws/resource_aws_dx_gateway.go @@ -8,8 +8,11 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/directconnect" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/directconnect/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/directconnect/waiter" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func resourceAwsDxGateway() *schema.Resource { @@ -17,22 +20,25 @@ func resourceAwsDxGateway() *schema.Resource { Create: resourceAwsDxGatewayCreate, Read: resourceAwsDxGatewayRead, Delete: resourceAwsDxGatewayDelete, + Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, "amazon_side_asn": { Type: schema.TypeString, Required: true, ForceNew: true, ValidateFunc: validateAmazonSideAsn, }, + + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "owner_account_id": { Type: schema.TypeString, Computed: true, @@ -49,36 +55,28 @@ func resourceAwsDxGateway() *schema.Resource { func resourceAwsDxGatewayCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).dxconn - req := &directconnect.CreateDirectConnectGatewayInput{ - DirectConnectGatewayName: aws.String(d.Get("name").(string)), + name := d.Get("name").(string) + input := &directconnect.CreateDirectConnectGatewayInput{ + DirectConnectGatewayName: aws.String(name), } - if asn, ok := d.GetOk("amazon_side_asn"); ok { - i, err := strconv.ParseInt(asn.(string), 10, 64) - if err != nil { - return err + + if v, ok := d.Get("amazon_side_asn").(string); ok && v != "" { + if v, err := strconv.ParseInt(v, 10, 64); err == nil { + input.AmazonSideAsn = aws.Int64(v) } - req.AmazonSideAsn = aws.Int64(i) } - log.Printf("[DEBUG] Creating Direct Connect gateway: %#v", req) - resp, err := conn.CreateDirectConnectGateway(req) + log.Printf("[DEBUG] Creating Direct Connect Gateway: %s", input) + resp, err := conn.CreateDirectConnectGateway(input) + if err != nil { - return fmt.Errorf("Error creating Direct Connect gateway: %s", err) + return fmt.Errorf("error creating Direct Connect Gateway (%s): %w", name, err) } d.SetId(aws.StringValue(resp.DirectConnectGateway.DirectConnectGatewayId)) - stateConf := &resource.StateChangeConf{ - Pending: []string{directconnect.GatewayStatePending}, - Target: []string{directconnect.GatewayStateAvailable}, - Refresh: dxGatewayStateRefresh(conn, d.Id()), - Timeout: d.Timeout(schema.TimeoutCreate), - Delay: 10 * time.Second, - MinTimeout: 5 * time.Second, - } - _, err = stateConf.WaitForState() - if err != nil { - return fmt.Errorf("Error waiting for Direct Connect gateway (%s) to become available: %s", d.Id(), err) + if _, err := waiter.GatewayCreated(conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { + return fmt.Errorf("error waiting for Direct Connect Gateway (%s) to create: %w", d.Id(), err) } return resourceAwsDxGatewayRead(d, meta) @@ -87,20 +85,21 @@ func resourceAwsDxGatewayCreate(d *schema.ResourceData, meta interface{}) error func resourceAwsDxGatewayRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).dxconn - dxGwRaw, state, err := dxGatewayStateRefresh(conn, d.Id())() - if err != nil { - return fmt.Errorf("Error reading Direct Connect gateway: %s", err) - } - if state == directconnect.GatewayStateDeleted { - log.Printf("[WARN] Direct Connect gateway (%s) not found, removing from state", d.Id()) + output, err := finder.GatewayByID(conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] Direct Connect Gateway (%s) not found, removing from state", d.Id()) d.SetId("") return nil } - dxGw := dxGwRaw.(*directconnect.Gateway) - d.Set("name", dxGw.DirectConnectGatewayName) - d.Set("amazon_side_asn", strconv.FormatInt(aws.Int64Value(dxGw.AmazonSideAsn), 10)) - d.Set("owner_account_id", dxGw.OwnerAccount) + if err != nil { + return fmt.Errorf("error reading Direct Connect Gateway (%s): %w", d.Id(), err) + } + + d.Set("amazon_side_asn", strconv.FormatInt(aws.Int64Value(output.AmazonSideAsn), 10)) + d.Set("name", output.DirectConnectGatewayName) + d.Set("owner_account_id", output.OwnerAccount) return nil } @@ -108,58 +107,22 @@ func resourceAwsDxGatewayRead(d *schema.ResourceData, meta interface{}) error { func resourceAwsDxGatewayDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).dxconn + log.Printf("[DEBUG] Deleting Direct Connect Gateway: %s", d.Id()) _, err := conn.DeleteDirectConnectGateway(&directconnect.DeleteDirectConnectGatewayInput{ DirectConnectGatewayId: aws.String(d.Id()), }) - if err != nil { - if isAWSErr(err, "DirectConnectClientException", "does not exist") { - return nil - } - return fmt.Errorf("Error deleting Direct Connect gateway: %s", err) - } - if err := waitForDirectConnectGatewayDeletion(conn, d.Id(), d.Timeout(schema.TimeoutDelete)); err != nil { - return fmt.Errorf("Error waiting for Direct Connect gateway (%s) to be deleted: %s", d.Id(), err) + if tfawserr.ErrMessageContains(err, directconnect.ErrCodeClientException, "does not exist") { + return nil } - return nil -} - -func dxGatewayStateRefresh(conn *directconnect.DirectConnect, dxgwId string) resource.StateRefreshFunc { - return func() (interface{}, string, error) { - resp, err := conn.DescribeDirectConnectGateways(&directconnect.DescribeDirectConnectGatewaysInput{ - DirectConnectGatewayId: aws.String(dxgwId), - }) - if err != nil { - return nil, "", err - } - - n := len(resp.DirectConnectGateways) - switch n { - case 0: - return "", directconnect.GatewayStateDeleted, nil - - case 1: - dxgw := resp.DirectConnectGateways[0] - return dxgw, aws.StringValue(dxgw.DirectConnectGatewayState), nil - - default: - return nil, "", fmt.Errorf("Found %d Direct Connect gateways for %s, expected 1", n, dxgwId) - } + if err != nil { + return fmt.Errorf("error deleting Direct Connect Gateway (%s): %w", d.Id(), err) } -} -func waitForDirectConnectGatewayDeletion(conn *directconnect.DirectConnect, gatewayID string, timeout time.Duration) error { - stateConf := &resource.StateChangeConf{ - Pending: []string{directconnect.GatewayStatePending, directconnect.GatewayStateAvailable, directconnect.GatewayStateDeleting}, - Target: []string{directconnect.GatewayStateDeleted}, - Refresh: dxGatewayStateRefresh(conn, gatewayID), - Timeout: timeout, - Delay: 10 * time.Second, - MinTimeout: 5 * time.Second, + if _, err := waiter.GatewayDeleted(conn, d.Id(), d.Timeout(schema.TimeoutDelete)); err != nil { + return fmt.Errorf("error waiting for Direct Connect Gateway (%s) to delete: %w", d.Id(), err) } - _, err := stateConf.WaitForState() - - return err + return nil } diff --git a/aws/resource_aws_dx_gateway_test.go b/aws/resource_aws_dx_gateway_test.go index 4809545c442..00a66f3dbe8 100644 --- a/aws/resource_aws_dx_gateway_test.go +++ b/aws/resource_aws_dx_gateway_test.go @@ -4,13 +4,16 @@ import ( "fmt" "log" "testing" - "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/directconnect" + multierror "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/directconnect/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/directconnect/lister" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func init() { @@ -26,95 +29,91 @@ func init() { func testSweepDirectConnectGateways(region string) error { client, err := sharedClientForRegion(region) if err != nil { - return fmt.Errorf("error getting client: %s", err) + return fmt.Errorf("error getting client: %w", err) } conn := client.(*AWSClient).dxconn input := &directconnect.DescribeDirectConnectGatewaysInput{} + var sweeperErrs *multierror.Error + sweepResources := make([]*testSweepResource, 0) - for { - output, err := conn.DescribeDirectConnectGateways(input) - - if testSweepSkipSweepError(err) { - log.Printf("[WARN] Skipping Direct Connect Gateway sweep for %s: %s", region, err) - return nil - } - - if err != nil { - return fmt.Errorf("error retrieving Direct Connect Gateways: %s", err) + err = lister.DescribeDirectConnectGatewaysPages(conn, input, func(page *directconnect.DescribeDirectConnectGatewaysOutput, lastPage bool) bool { + if page == nil { + return !lastPage } - for _, gateway := range output.DirectConnectGateways { - id := aws.StringValue(gateway.DirectConnectGatewayId) + for _, gateway := range page.DirectConnectGateways { + directConnectGatewayID := aws.StringValue(gateway.DirectConnectGatewayId) - if aws.StringValue(gateway.DirectConnectGatewayState) != directconnect.GatewayStateAvailable { - log.Printf("[INFO] Skipping Direct Connect Gateway in non-available (%s) state: %s", aws.StringValue(gateway.DirectConnectGatewayState), id) + if state := aws.StringValue(gateway.DirectConnectGatewayState); state != directconnect.GatewayStateAvailable { + log.Printf("[INFO] Skipping Direct Connect Gateway in non-available (%s) state: %s", state, directConnectGatewayID) continue } - var associations bool - associationInput := &directconnect.DescribeDirectConnectGatewayAssociationsInput{ - DirectConnectGatewayId: gateway.DirectConnectGatewayId, + input := &directconnect.DescribeDirectConnectGatewayAssociationsInput{ + DirectConnectGatewayId: aws.String(directConnectGatewayID), } - for { - associationOutput, err := conn.DescribeDirectConnectGatewayAssociations(associationInput) + var associations bool - if err != nil { - return fmt.Errorf("error retrieving Direct Connect Gateway (%s) Associations: %s", id, err) + err := lister.DescribeDirectConnectGatewayAssociationsPages(conn, input, func(page *directconnect.DescribeDirectConnectGatewayAssociationsOutput, lastPage bool) bool { + if page == nil { + return !lastPage } // If associations still remain, its likely that our region is not the home // region of those associations and the previous sweepers skipped them. // When we hit this condition, we skip trying to delete the gateway as it // will go from deleting -> available after a few minutes and timeout. - if len(associationOutput.DirectConnectGatewayAssociations) > 0 { + if len(page.DirectConnectGatewayAssociations) > 0 { associations = true - break - } - if aws.StringValue(associationOutput.NextToken) == "" { - break + return false } - associationInput.NextToken = associationOutput.NextToken + return !lastPage + }) + + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing Direct Connect Gateway Associations (%s): %w", region, err)) } if associations { - log.Printf("[INFO] Skipping Direct Connect Gateway with remaining associations: %s", id) + log.Printf("[INFO] Skipping Direct Connect Gateway with remaining associations: %s", directConnectGatewayID) continue } - input := &directconnect.DeleteDirectConnectGatewayInput{ - DirectConnectGatewayId: aws.String(id), - } + r := resourceAwsDxGateway() + d := r.Data(nil) + d.SetId(directConnectGatewayID) - log.Printf("[INFO] Deleting Direct Connect Gateway: %s", id) - _, err := conn.DeleteDirectConnectGateway(input) + sweepResources = append(sweepResources, NewTestSweepResource(r, d, client)) + } - if isAWSErr(err, directconnect.ErrCodeClientException, "does not exist") { - continue - } + return !lastPage + }) - if err != nil { - return fmt.Errorf("error deleting Direct Connect Gateway (%s): %s", id, err) - } + if testSweepSkipSweepError(err) { + log.Print(fmt.Errorf("[WARN] Skipping Direct Connect Gateway sweep for %s: %w", region, err)) + return sweeperErrs // In case we have completed some pages, but had errors + } - if err := waitForDirectConnectGatewayDeletion(conn, id, 20*time.Minute); err != nil { - return fmt.Errorf("error waiting for Direct Connect Gateway (%s) to be deleted: %s", id, err) - } - } + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing Direct Connect Gateways (%s): %w", region, err)) + } - if aws.StringValue(output.NextToken) == "" { - break - } + err = testSweepResourceOrchestrator(sweepResources) - input.NextToken = output.NextToken + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping Direct Connect Gateways (%s): %w", region, err)) } - return nil + return sweeperErrs.ErrorOrNil() } func TestAccAwsDxGateway_basic(t *testing.T) { + var v directconnect.Gateway + rName := acctest.RandomWithPrefix("tf-acc-test") + rBgpAsn := acctest.RandIntRange(64512, 65534) resourceName := "aws_dx_gateway.test" resource.ParallelTest(t, resource.TestCase{ @@ -124,9 +123,9 @@ func TestAccAwsDxGateway_basic(t *testing.T) { CheckDestroy: testAccCheckAwsDxGatewayDestroy, Steps: []resource.TestStep{ { - Config: testAccDxGatewayConfig(acctest.RandString(5), acctest.RandIntRange(64512, 65534)), + Config: testAccDxGatewayConfig(rName, rBgpAsn), Check: resource.ComposeTestCheckFunc( - testAccCheckAwsDxGatewayExists(resourceName), + testAccCheckAwsDxGatewayExists(resourceName, &v), testAccCheckResourceAttrAccountID(resourceName, "owner_account_id"), ), }, @@ -139,7 +138,32 @@ func TestAccAwsDxGateway_basic(t *testing.T) { }) } +func TestAccAwsDxGateway_disappears(t *testing.T) { + var v directconnect.Gateway + rName := acctest.RandomWithPrefix("tf-acc-test") + rBgpAsn := acctest.RandIntRange(64512, 65534) + resourceName := "aws_dx_gateway.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, directconnect.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsDxGatewayDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDxGatewayConfig(rName, rBgpAsn), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsDxGatewayExists(resourceName, &v), + testAccCheckResourceDisappears(testAccProvider, resourceAwsDxGateway(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + func TestAccAwsDxGateway_complex(t *testing.T) { + var v directconnect.Gateway rName := acctest.RandomWithPrefix("tf-acc-test") rBgpAsn := acctest.RandIntRange(64512, 65534) resourceName := "aws_dx_gateway.test" @@ -153,7 +177,7 @@ func TestAccAwsDxGateway_complex(t *testing.T) { { Config: testAccDxGatewayAssociationConfig_multiVpnGatewaysSingleAccount(rName, rBgpAsn), Check: resource.ComposeTestCheckFunc( - testAccCheckAwsDxGatewayExists(resourceName), + testAccCheckAwsDxGatewayExists(resourceName, &v), testAccCheckResourceAttrAccountID(resourceName, "owner_account_id"), ), }, @@ -174,30 +198,42 @@ func testAccCheckAwsDxGatewayDestroy(s *terraform.State) error { continue } - input := &directconnect.DescribeDirectConnectGatewaysInput{ - DirectConnectGatewayId: aws.String(rs.Primary.ID), + _, err := finder.GatewayByID(conn, rs.Primary.ID) + + if tfresource.NotFound(err) { + continue } - resp, err := conn.DescribeDirectConnectGateways(input) if err != nil { return err } - for _, v := range resp.DirectConnectGateways { - if *v.DirectConnectGatewayId == rs.Primary.ID && !(*v.DirectConnectGatewayState == directconnect.GatewayStateDeleted) { - return fmt.Errorf("[DESTROY ERROR] DX Gateway (%s) not deleted", rs.Primary.ID) - } - } + + return fmt.Errorf("Direct Connect Gateway %s still exists", rs.Primary.ID) } return nil } -func testAccCheckAwsDxGatewayExists(name string) resource.TestCheckFunc { +func testAccCheckAwsDxGatewayExists(name string, v *directconnect.Gateway) resource.TestCheckFunc { return func(s *terraform.State) error { - _, ok := s.RootModule().Resources[name] + rs, ok := s.RootModule().Resources[name] if !ok { return fmt.Errorf("Not found: %s", name) } + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).dxconn + + output, err := finder.GatewayByID(conn, rs.Primary.ID) + + if err != nil { + return err + } + + *v = *output + return nil } } @@ -205,8 +241,8 @@ func testAccCheckAwsDxGatewayExists(name string) resource.TestCheckFunc { func testAccDxGatewayConfig(rName string, rBgpAsn int) string { return fmt.Sprintf(` resource "aws_dx_gateway" "test" { - name = "terraform-testacc-dxgw-%s" - amazon_side_asn = "%d" + name = %[1]q + amazon_side_asn = "%[2]d" } `, rName, rBgpAsn) } From 306bd032dd2ba78fb7f64dff9d687c637da05d79 Mon Sep 17 00:00:00 2001 From: Dirk Avery <31492422+YakDriver@users.noreply.github.com> Date: Thu, 8 Jul 2021 15:16:38 -0400 Subject: [PATCH 223/398] docs/r/lakeformation_permissions: Soften language Co-authored-by: Mary Elizabeth --- website/docs/r/lakeformation_permissions.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/lakeformation_permissions.html.markdown b/website/docs/r/lakeformation_permissions.html.markdown index 7d98355ec11..75cb4c478af 100644 --- a/website/docs/r/lakeformation_permissions.html.markdown +++ b/website/docs/r/lakeformation_permissions.html.markdown @@ -18,7 +18,7 @@ Grants permissions to the principal to access metadata in the Data Catalog and d **_Lake Formation permissions are not in effect by default within AWS._** `IAMAllowedPrincipals` (i.e., `IAM_ALLOWED_PRINCIPALS`) conflicts with individual Lake Formation permissions (i.e., non-`IAMAllowedPrincipals` permissions), will cause unexpected behavior, and may result in errors. -When using Lake Formation, you need to choose between these two mutually exclusive options: +When using Lake Formation, choose ONE of the following options as they are mutually exclusive: 1. Use this resource (`aws_lakeformation_permissions`), change the default security settings using [`aws_lakeformation_data_lake_settings`](/docs/providers/aws/r/lakeformation_data_lake_settings.html), and remove existing `IAMAllowedPrincipals` permissions 2. Use `IAMAllowedPrincipals` and not use this resource From ddab58c36c488d4d77de8adece1ef2f7b7551ce3 Mon Sep 17 00:00:00 2001 From: Dirk Avery <31492422+YakDriver@users.noreply.github.com> Date: Thu, 8 Jul 2021 15:17:08 -0400 Subject: [PATCH 224/398] docs/r/lakeformation_permissions: Clarify Co-authored-by: Mary Elizabeth --- website/docs/r/lakeformation_permissions.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/lakeformation_permissions.html.markdown b/website/docs/r/lakeformation_permissions.html.markdown index 75cb4c478af..81e5bb583f0 100644 --- a/website/docs/r/lakeformation_permissions.html.markdown +++ b/website/docs/r/lakeformation_permissions.html.markdown @@ -21,7 +21,7 @@ Grants permissions to the principal to access metadata in the Data Catalog and d When using Lake Formation, choose ONE of the following options as they are mutually exclusive: 1. Use this resource (`aws_lakeformation_permissions`), change the default security settings using [`aws_lakeformation_data_lake_settings`](/docs/providers/aws/r/lakeformation_data_lake_settings.html), and remove existing `IAMAllowedPrincipals` permissions -2. Use `IAMAllowedPrincipals` and not use this resource +2. Use `IAMAllowedPrincipals` without `aws_lakeformation_permissions` ``` This example shows removing the `IAMAllowedPrincipals` default security settings and making the caller a Lake Formation admin. Since `create_database_default_permissions` and `create_table_default_permissions` are not set in the [`aws_lakeformation_data_lake_settings`](/docs/providers/aws/r/lakeformation_data_lake_settings.html) resource, they are cleared. From fb040dd8f7429b1172616eb9423be5356e3f1298 Mon Sep 17 00:00:00 2001 From: Dirk Avery <31492422+YakDriver@users.noreply.github.com> Date: Thu, 8 Jul 2021 15:17:36 -0400 Subject: [PATCH 225/398] docs/r/lakeformation_permissions: Clean up language Co-authored-by: Mary Elizabeth --- website/docs/r/lakeformation_permissions.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/lakeformation_permissions.html.markdown b/website/docs/r/lakeformation_permissions.html.markdown index 81e5bb583f0..c23c637c16d 100644 --- a/website/docs/r/lakeformation_permissions.html.markdown +++ b/website/docs/r/lakeformation_permissions.html.markdown @@ -50,7 +50,7 @@ For more details, see [Changing the Default Security Settings for Your Data Lake ### Problem Using `IAMAllowedPrincipals` -AWS does not support combining `IAMAllowedPrincipals` permissions and non-`IAMAllowedPrincipals` permissions. Doing so results in unexpected permissions. For example, this configuration grants a user `SELECT` on a column in a table. +AWS does not support combining `IAMAllowedPrincipals` permissions and non-`IAMAllowedPrincipals` permissions. Doing so results in unexpected permissions and behaviors. For example, this configuration grants a user `SELECT` on a column in a table. ```terraform resource "aws_glue_catalog_database" "example" { From ad016d2b0c2930ef853fdc29de7f6eb1f94eb8b1 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 8 Jul 2021 15:20:37 -0400 Subject: [PATCH 226/398] r/lakeformation_permissions: Errant newline --- aws/resource_aws_lakeformation_permissions.go | 1 - 1 file changed, 1 deletion(-) diff --git a/aws/resource_aws_lakeformation_permissions.go b/aws/resource_aws_lakeformation_permissions.go index 98c454df7ed..0b95cc9ab0d 100644 --- a/aws/resource_aws_lakeformation_permissions.go +++ b/aws/resource_aws_lakeformation_permissions.go @@ -503,7 +503,6 @@ func resourceAwsLakeFormationPermissionsRead(d *schema.ResourceData, meta interf tableSet = true break } - } } From 7d84773269330e64c0f1c4dd158f8b458f8fab90 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 8 Jul 2021 15:27:04 -0400 Subject: [PATCH 227/398] Add CHANGELOG entry. --- .changelog/19741.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/19741.txt diff --git a/.changelog/19741.txt b/.changelog/19741.txt new file mode 100644 index 00000000000..83d9fc756e9 --- /dev/null +++ b/.changelog/19741.txt @@ -0,0 +1,3 @@ +```release-note:note +resource/aws_dx_gateway_association_proposal: If an accepted Proposal reaches end-of-life and is removed by AWS do not recreate the resource, instead refreshing Terraform state from the resource's Direct Connect Gateway ID and Associated Gateway ID. +``` \ No newline at end of file From d1165875915bf1a2953cace069e54110b9f7557e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 8 Jul 2021 15:28:00 -0400 Subject: [PATCH 228/398] 'release-note' not 'release-notes' for CHANGELOG entries --- docs/contributing/pullrequest-submission-and-lifecycle.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/contributing/pullrequest-submission-and-lifecycle.md b/docs/contributing/pullrequest-submission-and-lifecycle.md index 19a8ea6f9b7..a3cd2b85cc9 100644 --- a/docs/contributing/pullrequest-submission-and-lifecycle.md +++ b/docs/contributing/pullrequest-submission-and-lifecycle.md @@ -206,11 +206,11 @@ The changelog format requires an entry in the following format, where HEADER cor If a pull request should contain multiple changelog entries, then multiple blocks can be added to the same changelog file. For example: ``````markdown -```release-notes:note +```release-note:note resource/aws_example_thing: The `broken` attribute has been deprecated. All configurations using `broken` should be updated to use the new `not_broken` attribute instead. ``` -```release-notes:enhancement +```release-note:enhancement resource/aws_example_thing: Add `not_broken` attribute ``` `````` From 8b7cb8e715a8249f4eee3ce6aab60697673abbda Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 8 Jul 2021 16:07:18 -0400 Subject: [PATCH 229/398] docs/r/lakeformation_permissions: Remove errants ticks --- website/docs/r/lakeformation_permissions.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/lakeformation_permissions.html.markdown b/website/docs/r/lakeformation_permissions.html.markdown index c23c637c16d..18f7e326d19 100644 --- a/website/docs/r/lakeformation_permissions.html.markdown +++ b/website/docs/r/lakeformation_permissions.html.markdown @@ -21,7 +21,7 @@ Grants permissions to the principal to access metadata in the Data Catalog and d When using Lake Formation, choose ONE of the following options as they are mutually exclusive: 1. Use this resource (`aws_lakeformation_permissions`), change the default security settings using [`aws_lakeformation_data_lake_settings`](/docs/providers/aws/r/lakeformation_data_lake_settings.html), and remove existing `IAMAllowedPrincipals` permissions -2. Use `IAMAllowedPrincipals` without `aws_lakeformation_permissions` ``` +2. Use `IAMAllowedPrincipals` without `aws_lakeformation_permissions` This example shows removing the `IAMAllowedPrincipals` default security settings and making the caller a Lake Formation admin. Since `create_database_default_permissions` and `create_table_default_permissions` are not set in the [`aws_lakeformation_data_lake_settings`](/docs/providers/aws/r/lakeformation_data_lake_settings.html) resource, they are cleared. From 555664a272d68b9555dd81679ba6a8fe07ad4bb9 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Thu, 8 Jul 2021 13:42:22 -0700 Subject: [PATCH 230/398] Adds test for updating replica count on Global Replication Group secondary member --- ..._aws_elasticache_replication_group_test.go | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/aws/resource_aws_elasticache_replication_group_test.go b/aws/resource_aws_elasticache_replication_group_test.go index 5822a2db70d..3dbc8a65774 100644 --- a/aws/resource_aws_elasticache_replication_group_test.go +++ b/aws/resource_aws_elasticache_replication_group_test.go @@ -1551,6 +1551,8 @@ func TestAccAWSElasticacheReplicationGroup_GlobalReplicationGroupId_Basic(t *tes resource.TestCheckResourceAttrPair(resourceName, "engine", primaryGroupResourceName, "engine"), resource.TestCheckResourceAttrPair(resourceName, "engine_version", primaryGroupResourceName, "engine_version"), resource.TestMatchResourceAttr(resourceName, "parameter_group_name", regexp.MustCompile(fmt.Sprintf("^global-datastore-%s-", rName))), + resource.TestCheckResourceAttr(resourceName, "number_cache_clusters", "1"), + resource.TestCheckResourceAttr(primaryGroupResourceName, "number_cache_clusters", "2"), ), }, { @@ -1571,6 +1573,9 @@ func TestAccAWSElasticacheReplicationGroup_GlobalReplicationGroupId_Full(t *test resourceName := "aws_elasticache_replication_group.test" primaryGroupResourceName := "aws_elasticache_replication_group.primary" + initialNumCacheClusters := 2 + updatedNumCacheClusters := 3 + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) @@ -1581,7 +1586,7 @@ func TestAccAWSElasticacheReplicationGroup_GlobalReplicationGroupId_Full(t *test CheckDestroy: testAccCheckAWSElasticacheReplicationDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSElasticacheReplicationGroupConfig_GlobalReplicationGroupId_Full(rName), + Config: testAccAWSElasticacheReplicationGroupConfig_GlobalReplicationGroupId_Full(rName, initialNumCacheClusters), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSElasticacheReplicationGroupExists(resourceName, &rg), resource.TestCheckResourceAttrPair(resourceName, "global_replication_group_id", "aws_elasticache_global_replication_group.test", "global_replication_group_id"), @@ -1590,7 +1595,7 @@ func TestAccAWSElasticacheReplicationGroup_GlobalReplicationGroupId_Full(t *test resource.TestCheckResourceAttrPair(resourceName, "engine_version", primaryGroupResourceName, "engine_version"), resource.TestMatchResourceAttr(resourceName, "parameter_group_name", regexp.MustCompile(fmt.Sprintf("^global-datastore-%s-", rName))), - resource.TestCheckResourceAttrPair(resourceName, "number_cache_clusters", primaryGroupResourceName, "number_cache_clusters"), + resource.TestCheckResourceAttr(resourceName, "number_cache_clusters", strconv.Itoa(initialNumCacheClusters)), resource.TestCheckResourceAttrPair(resourceName, "multi_az_enabled", primaryGroupResourceName, "multi_az_enabled"), resource.TestCheckResourceAttrPair(resourceName, "automatic_failover_enabled", primaryGroupResourceName, "automatic_failover_enabled"), @@ -1607,6 +1612,13 @@ func TestAccAWSElasticacheReplicationGroup_GlobalReplicationGroupId_Full(t *test ImportStateVerify: true, ImportStateVerifyIgnore: []string{"apply_immediately"}, }, + { + Config: testAccAWSElasticacheReplicationGroupConfig_GlobalReplicationGroupId_Full(rName, updatedNumCacheClusters), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSElasticacheReplicationGroupExists(resourceName, &rg), + resource.TestCheckResourceAttr(resourceName, "number_cache_clusters", strconv.Itoa(updatedNumCacheClusters)), + ), + }, }, }) } @@ -2890,12 +2902,12 @@ resource "aws_elasticache_replication_group" "primary" { engine = "redis" engine_version = "5.0.6" - number_cache_clusters = 1 + number_cache_clusters = 2 } `, rName)) } -func testAccAWSElasticacheReplicationGroupConfig_GlobalReplicationGroupId_Full(rName string) string { +func testAccAWSElasticacheReplicationGroupConfig_GlobalReplicationGroupId_Full(rName string, numCacheClusters int) string { return composeConfig( testAccMultipleRegionProviderConfig(2), testAccElasticacheVpcBaseWithProvider(rName, "test", ProviderNameAws, 2), @@ -2908,7 +2920,7 @@ resource "aws_elasticache_replication_group" "test" { subnet_group_name = aws_elasticache_subnet_group.test.name - number_cache_clusters = 2 + number_cache_clusters = %[2]d automatic_failover_enabled = true multi_az_enabled = true @@ -2943,7 +2955,7 @@ resource "aws_elasticache_replication_group" "primary" { at_rest_encryption_enabled = true transit_encryption_enabled = true } -`, rName)) +`, rName, numCacheClusters)) } func testAccAWSElasticacheReplicationGroupConfig_GlobalReplicationGroupId_ClusterMode(rName string, primaryReplicaCount, secondaryReplicaCount int) string { From c800c67b4af4fc9374ab57e5678e7ca8a8659b74 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Thu, 8 Jul 2021 14:03:09 -0700 Subject: [PATCH 231/398] Adds CHANGELOG entry --- .changelog/20111.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/20111.txt diff --git a/.changelog/20111.txt b/.changelog/20111.txt new file mode 100644 index 00000000000..5768419145b --- /dev/null +++ b/.changelog/20111.txt @@ -0,0 +1,3 @@ +```release-note:bug +resource/aws_elasticache_replication_group: Cannot set `cluster_mode.replicas_per_node_group` when member of Global Replication Group +``` From 8cb22b4bf4187c2c40d9801455699a9eeed20f53 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 8 Jul 2021 17:16:08 -0400 Subject: [PATCH 232/398] Taint Proposal to test Gateway Association ForceNew. --- ...rce_aws_dx_gateway_association_proposal_test.go | 14 ++++++++++++-- aws/resource_aws_dx_gateway_association_test.go | 5 ++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/aws/resource_aws_dx_gateway_association_proposal_test.go b/aws/resource_aws_dx_gateway_association_proposal_test.go index a696eddccb3..700ae9ec673 100644 --- a/aws/resource_aws_dx_gateway_association_proposal_test.go +++ b/aws/resource_aws_dx_gateway_association_proposal_test.go @@ -351,6 +351,16 @@ func testAccCheckAwsDxGatewayAssociationProposalRecreated(old, new *directconnec } } +// func testAccCheckAwsDxGatewayAssociationProposalNotRecreated(old, new *directconnect.GatewayAssociationProposal) resource.TestCheckFunc { +// return func(s *terraform.State) error { +// if old, new := aws.StringValue(old.ProposalId), aws.StringValue(new.ProposalId); old != new { +// return fmt.Errorf("Direct Connect Gateway Association Proposal (%s) recreated (%s)", old, new) +// } + +// return nil +// } +// } + func testAccCheckAwsDxGatewayAssociationProposalAccepted(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] @@ -366,8 +376,8 @@ func testAccCheckAwsDxGatewayAssociationProposalAccepted(resourceName string) re return err } - if aws.StringValue(output.ProposalState) != directconnect.GatewayAssociationProposalStateAccepted { - return fmt.Errorf("Direct Connect Gateway Association Proposal (%s) not accepted", rs.Primary.ID) + if state := aws.StringValue(output.ProposalState); state != directconnect.GatewayAssociationProposalStateAccepted { + return fmt.Errorf("Direct Connect Gateway Association Proposal (%s) not accepted (%s)", rs.Primary.ID, state) } return nil diff --git a/aws/resource_aws_dx_gateway_association_test.go b/aws/resource_aws_dx_gateway_association_test.go index cbfe01d1303..6578f18265e 100644 --- a/aws/resource_aws_dx_gateway_association_test.go +++ b/aws/resource_aws_dx_gateway_association_test.go @@ -479,9 +479,7 @@ func TestAccAwsDxGatewayAssociation_recreateProposal(t *testing.T) { Config: testAccDxGatewayAssociationConfig_basicVpnGatewayCrossAccount(rName, rBgpAsn), Check: resource.ComposeTestCheckFunc( testAccCheckAwsDxGatewayAssociationExists(resourceName, &ga1, &gap1), - testAccCheckResourceDisappears(testAccProvider, resourceAwsDxGatewayAssociationProposal(), resourceNameProposal), ), - ExpectNonEmptyPlan: true, }, { Config: testAccDxGatewayAssociationConfig_basicVpnGatewayCrossAccount(rName, rBgpAsn), @@ -490,6 +488,7 @@ func TestAccAwsDxGatewayAssociation_recreateProposal(t *testing.T) { testAccCheckAwsDxGatewayAssociationNotRecreated(&ga1, &ga2), testAccCheckAwsDxGatewayAssociationProposalRecreated(&gap1, &gap2), ), + Taint: []string{resourceNameProposal}, }, }, }) @@ -566,7 +565,7 @@ func testAccCheckAwsDxGatewayAssociationExists(name string, ga *directconnect.Ga func testAccCheckAwsDxGatewayAssociationNotRecreated(old, new *directconnect.GatewayAssociation) resource.TestCheckFunc { return func(s *terraform.State) error { - if old, new := aws.StringValue(old.AssociationId), aws.StringValue(new.AssociationId); old == new { + if old, new := aws.StringValue(old.AssociationId), aws.StringValue(new.AssociationId); old != new { return fmt.Errorf("Direct Connect Gateway Association (%s) recreated (%s)", old, new) } From 7271ccbf34f28d383173f0d17ec4b54f00bbfedd Mon Sep 17 00:00:00 2001 From: changelogbot Date: Thu, 8 Jul 2021 21:22:26 +0000 Subject: [PATCH 233/398] Update CHANGELOG.md for #20108 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8117c545ea2..58062ef7fbe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,9 @@ ENHANCEMENTS: BUG FIXES: +* data-source/aws_lakeformation_permissions: Fix various problems with permissions including select-only ([#20108](https://github.com/hashicorp/terraform-provider-aws/issues/20108)) * resource/aws_eks_cluster: Don't associate an `encryption_config` if there's already one ([#19986](https://github.com/hashicorp/terraform-provider-aws/issues/19986)) +* resource/aws_lakeformation_permissions: Fix various problems with permissions including select-only ([#20108](https://github.com/hashicorp/terraform-provider-aws/issues/20108)) * resource/aws_ram_resource_share_accepter: Allow destroy even where AWS API provides no way to disassociate ([#19718](https://github.com/hashicorp/terraform-provider-aws/issues/19718)) ## 3.48.0 (July 02, 2021) From db2261520cacc36241cc72ea65aefc6b7891806d Mon Sep 17 00:00:00 2001 From: tf-release-bot Date: Thu, 8 Jul 2021 21:33:25 +0000 Subject: [PATCH 234/398] v3.49.0 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58062ef7fbe..f41dcdf719f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## 3.49.0 (Unreleased) +## 3.49.0 (July 08, 2021) FEATURES: From f93d5df7e7b81e2ee25bd4f8a438bb40a0f37ead Mon Sep 17 00:00:00 2001 From: changelogbot Date: Thu, 8 Jul 2021 21:51:23 +0000 Subject: [PATCH 235/398] Update CHANGELOG.md after v3.49.0 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f41dcdf719f..64ba9062ffd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +## 3.50.0 (Unreleased) ## 3.49.0 (July 08, 2021) FEATURES: From 45e79f704f799cff0a94847a5939425897e96085 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 16 Jun 2021 17:22:50 -0400 Subject: [PATCH 236/398] Add 'SubdirectoryFromLocationURI'. Test output: % go test -v ./aws/internal/service/datasync === RUN TestSubdirectoryFromLocationURI === RUN TestSubdirectoryFromLocationURI/empty_URI === RUN TestSubdirectoryFromLocationURI/invalid_URI_scheme === RUN TestSubdirectoryFromLocationURI/S3_bucket_URI_no_bucket_name_(1) === RUN TestSubdirectoryFromLocationURI/S3_bucket_URI_no_bucket_name_(2) === RUN TestSubdirectoryFromLocationURI/S3_bucket_URI_top_level === RUN TestSubdirectoryFromLocationURI/S3_bucket_URI_one_level === RUN TestSubdirectoryFromLocationURI/S3_bucket_URI_two_levels === RUN TestSubdirectoryFromLocationURI/S3_Outposts_ARN_URI_top_level === RUN TestSubdirectoryFromLocationURI/S3_Outposts_ARN_URI_one_level === RUN TestSubdirectoryFromLocationURI/S3_Outposts_ARN_URI_two_levels === RUN TestSubdirectoryFromLocationURI/EFS_URI_top_level === RUN TestSubdirectoryFromLocationURI/EFS_URI_one_level === RUN TestSubdirectoryFromLocationURI/EFS_URI_two_levels === RUN TestSubdirectoryFromLocationURI/NFS_URI_top_level === RUN TestSubdirectoryFromLocationURI/NFS_URI_one_level === RUN TestSubdirectoryFromLocationURI/NFS_URI_two_levels === RUN TestSubdirectoryFromLocationURI/SMB_URI_top_level === RUN TestSubdirectoryFromLocationURI/SMB_URI_one_level === RUN TestSubdirectoryFromLocationURI/SMB_URI_two_levels === RUN TestSubdirectoryFromLocationURI/FSx_Windows_URI_top_level === RUN TestSubdirectoryFromLocationURI/FSx_Windows_URI_one_level === RUN TestSubdirectoryFromLocationURI/FSx_Windows_URI_two_levels --- PASS: TestSubdirectoryFromLocationURI (0.00s) --- PASS: TestSubdirectoryFromLocationURI/empty_URI (0.00s) --- PASS: TestSubdirectoryFromLocationURI/invalid_URI_scheme (0.00s) --- PASS: TestSubdirectoryFromLocationURI/S3_bucket_URI_no_bucket_name_(1) (0.00s) --- PASS: TestSubdirectoryFromLocationURI/S3_bucket_URI_no_bucket_name_(2) (0.00s) --- PASS: TestSubdirectoryFromLocationURI/S3_bucket_URI_top_level (0.00s) --- PASS: TestSubdirectoryFromLocationURI/S3_bucket_URI_one_level (0.00s) --- PASS: TestSubdirectoryFromLocationURI/S3_bucket_URI_two_levels (0.00s) --- PASS: TestSubdirectoryFromLocationURI/S3_Outposts_ARN_URI_top_level (0.00s) --- PASS: TestSubdirectoryFromLocationURI/S3_Outposts_ARN_URI_one_level (0.00s) --- PASS: TestSubdirectoryFromLocationURI/S3_Outposts_ARN_URI_two_levels (0.00s) --- PASS: TestSubdirectoryFromLocationURI/EFS_URI_top_level (0.00s) --- PASS: TestSubdirectoryFromLocationURI/EFS_URI_one_level (0.00s) --- PASS: TestSubdirectoryFromLocationURI/EFS_URI_two_levels (0.00s) --- PASS: TestSubdirectoryFromLocationURI/NFS_URI_top_level (0.00s) --- PASS: TestSubdirectoryFromLocationURI/NFS_URI_one_level (0.00s) --- PASS: TestSubdirectoryFromLocationURI/NFS_URI_two_levels (0.00s) --- PASS: TestSubdirectoryFromLocationURI/SMB_URI_top_level (0.00s) --- PASS: TestSubdirectoryFromLocationURI/SMB_URI_one_level (0.00s) --- PASS: TestSubdirectoryFromLocationURI/SMB_URI_two_levels (0.00s) --- PASS: TestSubdirectoryFromLocationURI/FSx_Windows_URI_top_level (0.00s) --- PASS: TestSubdirectoryFromLocationURI/FSx_Windows_URI_one_level (0.00s) --- PASS: TestSubdirectoryFromLocationURI/FSx_Windows_URI_two_levels (0.00s) PASS ok github.com/terraform-providers/terraform-provider-aws/aws/internal/service/datasync 3.695s --- aws/internal/service/datasync/uri.go | 45 +++++++ aws/internal/service/datasync/uri_test.go | 145 ++++++++++++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 aws/internal/service/datasync/uri.go create mode 100644 aws/internal/service/datasync/uri_test.go diff --git a/aws/internal/service/datasync/uri.go b/aws/internal/service/datasync/uri.go new file mode 100644 index 00000000000..bac9e606a8e --- /dev/null +++ b/aws/internal/service/datasync/uri.go @@ -0,0 +1,45 @@ +package datasync + +import ( + "fmt" + "regexp" + + "github.com/aws/aws-sdk-go/aws/arn" +) + +var ( + locationURIPattern = regexp.MustCompile(`^(efs|nfs|s3|smb|fsxw)://(.+)$`) + locationURIGlobalIDAndSubdirPattern = regexp.MustCompile(`^([a-zA-Z0-9.\-]+)(/.*)$`) + s3OutpostsAccessPointARNResourcePattern = regexp.MustCompile(`^outpost/.*/accesspoint/.*?(/.*)$`) +) + +// SubdirectoryFromLocationURI extracts the subdirectory from a location URI. +// https://docs.aws.amazon.com/datasync/latest/userguide/API_LocationListEntry.html#DataSync-Type-LocationListEntry-LocationUri +func SubdirectoryFromLocationURI(uri string) (string, error) { + submatches := locationURIPattern.FindStringSubmatch(uri) + + if len(submatches) != 3 { + return "", fmt.Errorf("location URI (%s) does not match pattern %q", uri, locationURIPattern) + } + + globalIDAndSubdir := submatches[2] + parsedARN, err := arn.Parse(globalIDAndSubdir) + + if err == nil { + submatches = s3OutpostsAccessPointARNResourcePattern.FindStringSubmatch(parsedARN.Resource) + + if len(submatches) != 2 { + return "", fmt.Errorf("location URI S3 on Outposts access point ARN resource (%s) does not match pattern %q", parsedARN.Resource, s3OutpostsAccessPointARNResourcePattern) + } + + return submatches[1], nil + } + + submatches = locationURIGlobalIDAndSubdirPattern.FindStringSubmatch(globalIDAndSubdir) + + if len(submatches) != 3 { + return "", fmt.Errorf("location URI global ID and subdirectory (%s) does not match pattern %q", globalIDAndSubdir, locationURIGlobalIDAndSubdirPattern) + } + + return submatches[2], nil +} diff --git a/aws/internal/service/datasync/uri_test.go b/aws/internal/service/datasync/uri_test.go new file mode 100644 index 00000000000..6c90cf99a04 --- /dev/null +++ b/aws/internal/service/datasync/uri_test.go @@ -0,0 +1,145 @@ +package datasync_test + +import ( + "testing" + + tfdatasync "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/datasync" +) + +func TestSubdirectoryFromLocationURI(t *testing.T) { + testCases := []struct { + TestName string + InputURI string + ExpectedError bool + ExpectedSubdirectory string + }{ + { + TestName: "empty URI", + InputURI: "", + ExpectedError: true, + }, + { + TestName: "invalid URI scheme", + InputURI: "test://testing/", + ExpectedError: true, + }, + { + TestName: "S3 bucket URI no bucket name (1)", + InputURI: "s3://", + ExpectedError: true, + }, + { + TestName: "S3 bucket URI no bucket name (2)", + InputURI: "s3:///", + ExpectedError: true, + }, + { + TestName: "S3 bucket URI top level", + InputURI: "s3://bucket/", + ExpectedSubdirectory: "/", + }, + { + TestName: "S3 bucket URI one level", + InputURI: "s3://bucket/my-folder-1/", + ExpectedSubdirectory: "/my-folder-1/", + }, + { + TestName: "S3 bucket URI two levels", + InputURI: "s3://bucket/my-folder-1/my-folder-2", + ExpectedSubdirectory: "/my-folder-1/my-folder-2", + }, + { + TestName: "S3 Outposts ARN URI top level", + InputURI: "s3://arn:aws:s3-outposts:eu-west-3:123456789012:outpost/op-YYYYYYYYYY/accesspoint/my-access-point/", + ExpectedSubdirectory: "/", + }, + { + TestName: "S3 Outposts ARN URI one level", + InputURI: "s3://arn:aws:s3-outposts:eu-west-3:123456789012:outpost/op-YYYYYYYYYY/accesspoint/my-access-point/my-folder-1/", + ExpectedSubdirectory: "/my-folder-1/", + }, + { + TestName: "S3 Outposts ARN URI two levels", + InputURI: "s3://arn:aws:s3-outposts:eu-west-3:123456789012:outpost/op-YYYYYYYYYY/accesspoint/my-access-point/my-folder-1/my-folder-2", + ExpectedSubdirectory: "/my-folder-1/my-folder-2", + }, + { + TestName: "EFS URI top level", + InputURI: "efs://us-west-2.fs-abcdef01/", + ExpectedSubdirectory: "/", + }, + { + TestName: "EFS URI one level", + InputURI: "efs://us-west-2.fs-abcdef01/my-folder-1/", + ExpectedSubdirectory: "/my-folder-1/", + }, + { + TestName: "EFS URI two levels", + InputURI: "efs://us-west-2.fs-abcdef01/my-folder-1/my-folder-2", + ExpectedSubdirectory: "/my-folder-1/my-folder-2", + }, + { + TestName: "NFS URI top level", + InputURI: "nfs://example.com/", + ExpectedSubdirectory: "/", + }, + { + TestName: "NFS URI one level", + InputURI: "nfs://example.com/my-folder-1/", + ExpectedSubdirectory: "/my-folder-1/", + }, + { + TestName: "NFS URI two levels", + InputURI: "nfs://example.com/my-folder-1/my-folder-2", + ExpectedSubdirectory: "/my-folder-1/my-folder-2", + }, + { + TestName: "SMB URI top level", + InputURI: "smb://192.168.1.1/", + ExpectedSubdirectory: "/", + }, + { + TestName: "SMB URI one level", + InputURI: "smb://192.168.1.1/my-folder-1/", + ExpectedSubdirectory: "/my-folder-1/", + }, + { + TestName: "SMB URI two levels", + InputURI: "smb://192.168.1.1/my-folder-1/my-folder-2", + ExpectedSubdirectory: "/my-folder-1/my-folder-2", + }, + { + TestName: "FSx Windows URI top level", + InputURI: "fsxw://us-west-2.fs-abcdef012345678901/", + ExpectedSubdirectory: "/", + }, + { + TestName: "FSx Windows URI one level", + InputURI: "fsxw://us-west-2.fs-abcdef012345678901/my-folder-1/", + ExpectedSubdirectory: "/my-folder-1/", + }, + { + TestName: "FSx Windows URI two levels", + InputURI: "fsxw://us-west-2.fs-abcdef012345678901/my-folder-1/my-folder-2", + ExpectedSubdirectory: "/my-folder-1/my-folder-2", + }, + } + + for _, testCase := range testCases { + t.Run(testCase.TestName, func(t *testing.T) { + got, err := tfdatasync.SubdirectoryFromLocationURI(testCase.InputURI) + + if err == nil && testCase.ExpectedError { + t.Fatalf("expected error") + } + + if err != nil && !testCase.ExpectedError { + t.Fatalf("unexpected error: %s", err) + } + + if got != testCase.ExpectedSubdirectory { + t.Errorf("got %s, expected %s", got, testCase.ExpectedSubdirectory) + } + }) + } +} From 5882630cb5b04c7d8020052022b4626e134945c2 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 17 Jun 2021 10:45:20 -0400 Subject: [PATCH 237/398] Replace 'dataSyncParseLocationURI' with 'SubdirectoryFromLocationURI'. --- aws/datasync.go | 12 ----- aws/datasync_test.go | 47 ------------------- aws/resource_aws_datasync_location_efs.go | 5 +- ...tasync_location_fsx_windows_file_system.go | 5 +- aws/resource_aws_datasync_location_nfs.go | 5 +- aws/resource_aws_datasync_location_s3.go | 5 +- aws/resource_aws_datasync_location_smb.go | 5 +- 7 files changed, 15 insertions(+), 69 deletions(-) delete mode 100644 aws/datasync_test.go diff --git a/aws/datasync.go b/aws/datasync.go index 117ede5c4ef..ec7e10887b4 100644 --- a/aws/datasync.go +++ b/aws/datasync.go @@ -1,23 +1,11 @@ package aws import ( - "net/url" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/datasync" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) -func dataSyncParseLocationURI(uri string) (string, error) { - parsedURL, err := url.ParseRequestURI(uri) - - if err != nil { - return "", err - } - - return parsedURL.Path, nil -} - func expandDataSyncEc2Config(l []interface{}) *datasync.Ec2Config { if len(l) == 0 || l[0] == nil { return nil diff --git a/aws/datasync_test.go b/aws/datasync_test.go deleted file mode 100644 index f56cf7484fc..00000000000 --- a/aws/datasync_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package aws - -import ( - "testing" -) - -func TestDataSyncParseLocationURI(t *testing.T) { - testCases := []struct { - LocationURI string - Subdirectory string - }{ - { - LocationURI: "efs://us-east-2.fs-abcd1234/", // lintignore:AWSAT003 - Subdirectory: "/", - }, - { - LocationURI: "efs://us-east-2.fs-abcd1234/path", // lintignore:AWSAT003 - Subdirectory: "/path", - }, - { - LocationURI: "nfs://example.com/", - Subdirectory: "/", - }, - { - LocationURI: "nfs://example.com/path", - Subdirectory: "/path", - }, - { - LocationURI: "s3://myBucket/", - Subdirectory: "/", - }, - { - LocationURI: "s3://myBucket/path", - Subdirectory: "/path", - }, - } - - for i, tc := range testCases { - subdirectory, err := dataSyncParseLocationURI(tc.LocationURI) - if err != nil { - t.Fatalf("%d: received error parsing (%s): %s", i, tc.LocationURI, err) - } - if subdirectory != tc.Subdirectory { - t.Fatalf("%d: expected subdirectory (%s), received: %s", i, tc.Subdirectory, subdirectory) - } - } -} diff --git a/aws/resource_aws_datasync_location_efs.go b/aws/resource_aws_datasync_location_efs.go index a8dc8f9782f..45bba360789 100644 --- a/aws/resource_aws_datasync_location_efs.go +++ b/aws/resource_aws_datasync_location_efs.go @@ -10,6 +10,7 @@ import ( "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" + tfdatasync "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/datasync" ) func resourceAwsDataSyncLocationEfs() *schema.Resource { @@ -128,10 +129,10 @@ func resourceAwsDataSyncLocationEfsRead(d *schema.ResourceData, meta interface{} return fmt.Errorf("error reading DataSync Location EFS (%s): %s", d.Id(), err) } - subdirectory, err := dataSyncParseLocationURI(aws.StringValue(output.LocationUri)) + subdirectory, err := tfdatasync.SubdirectoryFromLocationURI(aws.StringValue(output.LocationUri)) if err != nil { - return fmt.Errorf("error parsing Location EFS (%s) URI (%s): %s", d.Id(), aws.StringValue(output.LocationUri), err) + return err } d.Set("arn", output.LocationArn) diff --git a/aws/resource_aws_datasync_location_fsx_windows_file_system.go b/aws/resource_aws_datasync_location_fsx_windows_file_system.go index b2e1651d793..95b5ed9697c 100644 --- a/aws/resource_aws_datasync_location_fsx_windows_file_system.go +++ b/aws/resource_aws_datasync_location_fsx_windows_file_system.go @@ -11,6 +11,7 @@ import ( "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" + tfdatasync "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/datasync" ) func resourceAwsDataSyncLocationFsxWindowsFileSystem() *schema.Resource { @@ -155,10 +156,10 @@ func resourceAwsDataSyncLocationFsxWindowsFileSystemRead(d *schema.ResourceData, return fmt.Errorf("error reading DataSync Location Fsx Windows (%s): %w", d.Id(), err) } - subdirectory, err := dataSyncParseLocationURI(aws.StringValue(output.LocationUri)) + subdirectory, err := tfdatasync.SubdirectoryFromLocationURI(aws.StringValue(output.LocationUri)) if err != nil { - return fmt.Errorf("error parsing Location Fsx Windows File System (%s) URI (%s): %w", d.Id(), aws.StringValue(output.LocationUri), err) + return err } d.Set("arn", output.LocationArn) diff --git a/aws/resource_aws_datasync_location_nfs.go b/aws/resource_aws_datasync_location_nfs.go index 33a48542a06..fd00dceccb8 100644 --- a/aws/resource_aws_datasync_location_nfs.go +++ b/aws/resource_aws_datasync_location_nfs.go @@ -10,6 +10,7 @@ import ( "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" + tfdatasync "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/datasync" ) func resourceAwsDataSyncLocationNfs() *schema.Resource { @@ -144,10 +145,10 @@ func resourceAwsDataSyncLocationNfsRead(d *schema.ResourceData, meta interface{} return fmt.Errorf("error reading DataSync Location NFS (%s): %w", d.Id(), err) } - subdirectory, err := dataSyncParseLocationURI(aws.StringValue(output.LocationUri)) + subdirectory, err := tfdatasync.SubdirectoryFromLocationURI(aws.StringValue(output.LocationUri)) if err != nil { - return fmt.Errorf("error parsing Location NFS (%s) URI (%s): %w", d.Id(), aws.StringValue(output.LocationUri), err) + return err } d.Set("arn", output.LocationArn) diff --git a/aws/resource_aws_datasync_location_s3.go b/aws/resource_aws_datasync_location_s3.go index 8fa5bf11f88..27bb510e251 100644 --- a/aws/resource_aws_datasync_location_s3.go +++ b/aws/resource_aws_datasync_location_s3.go @@ -11,6 +11,7 @@ import ( "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" + tfdatasync "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/datasync" iamwaiter "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/iam/waiter" ) @@ -172,10 +173,10 @@ func resourceAwsDataSyncLocationS3Read(d *schema.ResourceData, meta interface{}) return fmt.Errorf("error reading DataSync Location S3 (%s): %s", d.Id(), err) } - subdirectory, err := dataSyncParseLocationURI(aws.StringValue(output.LocationUri)) + subdirectory, err := tfdatasync.SubdirectoryFromLocationURI(aws.StringValue(output.LocationUri)) if err != nil { - return fmt.Errorf("error parsing Location S3 (%s) URI (%s): %s", d.Id(), aws.StringValue(output.LocationUri), err) + return err } d.Set("agent_arns", flattenStringSet(output.AgentArns)) diff --git a/aws/resource_aws_datasync_location_smb.go b/aws/resource_aws_datasync_location_smb.go index ed0a76cb264..211520eb7d4 100644 --- a/aws/resource_aws_datasync_location_smb.go +++ b/aws/resource_aws_datasync_location_smb.go @@ -9,6 +9,7 @@ import ( "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" + tfdatasync "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/datasync" ) func resourceAwsDataSyncLocationSmb() *schema.Resource { @@ -164,10 +165,10 @@ func resourceAwsDataSyncLocationSmbRead(d *schema.ResourceData, meta interface{} return fmt.Errorf("error reading DataSync Location SMB (%s) tags: %w", d.Id(), err) } - subdirectory, err := dataSyncParseLocationURI(aws.StringValue(output.LocationUri)) + subdirectory, err := tfdatasync.SubdirectoryFromLocationURI(aws.StringValue(output.LocationUri)) if err != nil { - return fmt.Errorf("error parsing Location SMB (%s) URI (%s): %w", d.Id(), aws.StringValue(output.LocationUri), err) + return err } d.Set("agent_arns", flattenStringSet(output.AgentArns)) From 8cc7cd9cc5a46213760d542b6e67155061969db1 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 17 Jun 2021 10:49:49 -0400 Subject: [PATCH 238/398] Add CHANGELOG entry. --- .changelog/19859.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/19859.txt diff --git a/.changelog/19859.txt b/.changelog/19859.txt new file mode 100644 index 00000000000..92976607dfc --- /dev/null +++ b/.changelog/19859.txt @@ -0,0 +1,3 @@ +```release-note:bug +resource/aws_datasync_location_s3: Correctly parse S3 on Outposts location URI +``` \ No newline at end of file From 41c9ab8189cd39fda0d5f75ad4a2695edd0e6b3a Mon Sep 17 00:00:00 2001 From: Corentin Debains Date: Tue, 24 Mar 2020 18:50:26 -0700 Subject: [PATCH 239/398] RDS Bugfix - Replica creation with Allocated Storage and IOPS Ignore allocated storage while creating a Read Replica * Fixes Issue #12493 Tests: * The acceptance test (allocatedStorageAndIops that would be previously failing), shows the bug by setting up a Replica DB with both IOPS and Allocated storage. * Also Added an acceptance test for Iops modification to confirm code is ok because of the returned error message in the issue. Didn't show failures previously. Fix: * Ignore allocated storage when creating a read replica as this value cannot be different from the primary. * Update doc to reflect param handling difference. --- aws/resource_aws_db_instance.go | 8 +- aws/resource_aws_db_instance_test.go | 104 +++++++++++++++++++++++ website/docs/r/db_instance.html.markdown | 2 +- 3 files changed, 110 insertions(+), 4 deletions(-) diff --git a/aws/resource_aws_db_instance.go b/aws/resource_aws_db_instance.go index 91e5e0bd92f..4d1ac5e39ab 100644 --- a/aws/resource_aws_db_instance.go +++ b/aws/resource_aws_db_instance.go @@ -583,9 +583,11 @@ func resourceAwsDbInstanceCreate(d *schema.ResourceData, meta interface{}) error Tags: tags.IgnoreAws().RdsTags(), } - if attr, ok := d.GetOk("allocated_storage"); ok { - modifyDbInstanceInput.AllocatedStorage = aws.Int64(int64(attr.(int))) - requiresModifyDbInstance = true + if _, ok := d.GetOk("allocated_storage"); ok { + log.Printf("[INFO] allocated_storage was ignored for DB Instance (%s) because it inherits the primary's and cannot be changed at creation.", d.Id()) + // RDS doesn't allow modifying the storage of a replica within the first 6h of creation. + // allocated_storage is inherited from the primary so only the same value or no value is correct; a different value would fail the creation. + // A different value is possible, granted: the value is higher than the current, there has been 6h between } if attr, ok := d.GetOk("availability_zone"); ok { diff --git a/aws/resource_aws_db_instance_test.go b/aws/resource_aws_db_instance_test.go index 22130346aeb..5d59b8a29b9 100644 --- a/aws/resource_aws_db_instance_test.go +++ b/aws/resource_aws_db_instance_test.go @@ -649,6 +649,57 @@ func TestAccAWSDBInstance_ReplicateSourceDb_AllocatedStorage(t *testing.T) { }) } +func TestAccAWSDBInstance_ReplicateSourceDb_Iops(t *testing.T) { + var dbInstance, sourceDbInstance rds.DBInstance + + rName := acctest.RandomWithPrefix("tf-acc-test") + sourceResourceName := "aws_db_instance.source" + resourceName := "aws_db_instance.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSDBInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSDBInstanceConfig_ReplicateSourceDb_Iops(rName, 1000), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDBInstanceExists(sourceResourceName, &sourceDbInstance), + testAccCheckAWSDBInstanceExists(resourceName, &dbInstance), + testAccCheckAWSDBInstanceReplicaAttributes(&sourceDbInstance, &dbInstance), + resource.TestCheckResourceAttr(resourceName, "iops", "1000"), + ), + }, + }, + }) +} + +func TestAccAWSDBInstance_ReplicateSourceDb_AllocatedStorageAndIops(t *testing.T) { + var dbInstance, sourceDbInstance rds.DBInstance + + rName := acctest.RandomWithPrefix("tf-acc-test") + sourceResourceName := "aws_db_instance.source" + resourceName := "aws_db_instance.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSDBInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSDBInstanceConfig_ReplicateSourceDb_AllocatedStorageAndIops(rName, 220, 2200), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDBInstanceExists(sourceResourceName, &sourceDbInstance), + testAccCheckAWSDBInstanceExists(resourceName, &dbInstance), + testAccCheckAWSDBInstanceReplicaAttributes(&sourceDbInstance, &dbInstance), + resource.TestCheckResourceAttr(resourceName, "allocated_storage", "220"), + resource.TestCheckResourceAttr(resourceName, "iops", "2200"), + ), + }, + }, + }) +} + func TestAccAWSDBInstance_ReplicateSourceDb_AllowMajorVersionUpgrade(t *testing.T) { var dbInstance, sourceDbInstance rds.DBInstance @@ -5387,6 +5438,59 @@ resource "aws_db_instance" "test" { `, rName, allocatedStorage)) } +func testAccAWSDBInstanceConfig_ReplicateSourceDb_Iops(rName string, iops int) string { + return fmt.Sprintf(` +resource "aws_db_instance" "source" { + allocated_storage = 200 + backup_retention_period = 1 + engine = "mysql" + identifier = "%s-source" + instance_class = "db.t2.micro" + password = "avoid-plaintext-passwords" + username = "tfacctest" + skip_final_snapshot = true + iops = 1100 + storage_type = "io1" +} + +resource "aws_db_instance" "test" { + identifier = %q + instance_class = "${aws_db_instance.source.instance_class}" + replicate_source_db = "${aws_db_instance.source.id}" + skip_final_snapshot = true + iops = %d + storage_type = "io1" +} +`, rName, rName, iops) +} + +func testAccAWSDBInstanceConfig_ReplicateSourceDb_AllocatedStorageAndIops(rName string, allocatedStorage, iops int) string { + return fmt.Sprintf(` +resource "aws_db_instance" "source" { + allocated_storage = %[2]d + backup_retention_period = 1 + engine = "mysql" + identifier = "%[1]s-source" + instance_class = "db.t2.micro" + password = "avoid-plaintext-passwords" + username = "tfacctest" + skip_final_snapshot = true + iops = 1000 + storage_type = "io1" +} + +resource "aws_db_instance" "test" { + allocated_storage = %[2]d + identifier = %[1]q + instance_class = "${aws_db_instance.source.instance_class}" + replicate_source_db = "${aws_db_instance.source.id}" + skip_final_snapshot = true + iops = %[3]d + storage_type = "io1" +} +`, rName, allocatedStorage, iops) +} + func testAccAWSDBInstanceConfig_ReplicateSourceDb_AllowMajorVersionUpgrade(rName string, allowMajorVersionUpgrade bool) string { return composeConfig(testAccAWSDBInstanceConfig_orderableClassMysql(), fmt.Sprintf(` resource "aws_db_instance" "source" { diff --git a/website/docs/r/db_instance.html.markdown b/website/docs/r/db_instance.html.markdown index d24318f255d..174ef425855 100644 --- a/website/docs/r/db_instance.html.markdown +++ b/website/docs/r/db_instance.html.markdown @@ -74,7 +74,7 @@ documentation](http://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_Crea The following arguments are supported: -* `allocated_storage` - (Required unless a `snapshot_identifier` or `replicate_source_db` is provided) The allocated storage in gibibytes. If `max_allocated_storage` is configured, this argument represents the initial storage allocation and differences from the configuration will be ignored automatically when Storage Autoscaling occurs. +* `allocated_storage` - (Required unless a `snapshot_identifier` or `replicate_source_db` is provided) The allocated storage in gibibytes. If `max_allocated_storage` is configured, this argument represents the initial storage allocation and differences from the configuration will be ignored automatically when Storage Autoscaling occurs. If `replicate_source_db` is set, the value is ignored during the creation of the instance. * `allow_major_version_upgrade` - (Optional) Indicates that major version upgrades are allowed. Changing this parameter does not result in an outage and the change is asynchronously applied as soon as possible. From 7b2203ed051e3ee63db6d2b6e1e8568b4cd89633 Mon Sep 17 00:00:00 2001 From: Corentin Debains Date: Thu, 26 Mar 2020 16:33:58 -0700 Subject: [PATCH 240/398] Update aws/resource_aws_db_instance.go Co-Authored-By: Muffy Barkocy --- aws/resource_aws_db_instance.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_db_instance.go b/aws/resource_aws_db_instance.go index 4d1ac5e39ab..a9c2ed0be76 100644 --- a/aws/resource_aws_db_instance.go +++ b/aws/resource_aws_db_instance.go @@ -584,7 +584,7 @@ func resourceAwsDbInstanceCreate(d *schema.ResourceData, meta interface{}) error } if _, ok := d.GetOk("allocated_storage"); ok { - log.Printf("[INFO] allocated_storage was ignored for DB Instance (%s) because it inherits the primary's and cannot be changed at creation.", d.Id()) + log.Printf("[INFO] allocated_storage was ignored for DB Instance (%s) because a replica inherits the primary's allocated_storage and this cannot be changed at creation.", d.Id()) // RDS doesn't allow modifying the storage of a replica within the first 6h of creation. // allocated_storage is inherited from the primary so only the same value or no value is correct; a different value would fail the creation. // A different value is possible, granted: the value is higher than the current, there has been 6h between From 3030e8e48ccceeb73593459d31ef874f20844bab Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Thu, 8 Jul 2021 22:39:05 -0400 Subject: [PATCH 241/398] enhanced error with client handler and minor updates --- aws/config.go | 5 +- aws/configservice.go | 72 ++++++++++++++----- ...ws_config_organization_conformance_pack.go | 32 +++------ ...nfig_organization_conformance_pack_test.go | 14 ++-- ...rganization_conformance_pack.html.markdown | 1 + 5 files changed, 77 insertions(+), 47 deletions(-) diff --git a/aws/config.go b/aws/config.go index cf4a742d363..56cc8b205bc 100644 --- a/aws/config.go +++ b/aws/config.go @@ -729,8 +729,11 @@ func (c *Config) Client() (interface{}, error) { } else { r.Retryable = aws.Bool(false) } - case "PutOrganizationConformancePack", "DeleteOrganizationConformancePack", "DescribeOrganizationConformancePackStatuses": + case "DeleteOrganizationConformancePack", "DescribeOrganizationConformancePacks", "DescribeOrganizationConformancePackStatuses", "PutOrganizationConformancePack": if !tfawserr.ErrCodeEquals(r.Error, configservice.ErrCodeOrganizationAccessDeniedException) { + if r.Operation.Name == "DeleteOrganizationConformancePack" && tfawserr.ErrCodeEquals(err, configservice.ErrCodeResourceInUseException) { + r.Retryable = aws.Bool(true) + } return } diff --git a/aws/configservice.go b/aws/configservice.go index 124a5728c9a..9170c2af149 100644 --- a/aws/configservice.go +++ b/aws/configservice.go @@ -17,7 +17,7 @@ const ( ConfigOrganizationConformancePackCreateTimeout = 10 * time.Minute ConfigOrganizationConformancePackUpdateTimeout = 10 * time.Minute - ConfigOrganizationConformancePackDeleteTimeout = 10 * time.Minute + ConfigOrganizationConformancePackDeleteTimeout = 20 * time.Minute ConfigConformancePackStatusNotFound = "NotFound" ConfigConformancePackStatusUnknown = "Unknown" @@ -310,10 +310,16 @@ func configRefreshOrganizationConfigRuleStatus(conn *configservice.ConfigService } } -func configRefreshOrganizationConformancePackStatus(conn *configservice.ConfigService, name string) resource.StateRefreshFunc { +func configRefreshOrganizationConformancePackCreationStatus(conn *configservice.ConfigService, name string) resource.StateRefreshFunc { return func() (interface{}, string, error) { status, err := configDescribeOrganizationConformancePackStatus(conn, name) + // Transient ResourceDoesNotExist error after creation caught here + // in cases where the StateChangeConf's delay time is not sufficient + if tfawserr.ErrCodeEquals(err, configservice.ErrCodeNoSuchOrganizationConformancePackException) { + return nil, "", nil + } + if err != nil { return nil, "", err } @@ -326,28 +332,56 @@ func configRefreshOrganizationConformancePackStatus(conn *configservice.ConfigSe return status, aws.StringValue(status.Status), fmt.Errorf("%s: %s", aws.StringValue(status.ErrorCode), aws.StringValue(status.ErrorMessage)) } - switch aws.StringValue(status.Status) { + switch s := aws.StringValue(status.Status); s { case configservice.OrganizationResourceStatusCreateFailed, configservice.OrganizationResourceStatusDeleteFailed, configservice.OrganizationResourceStatusUpdateFailed: - // Display detailed errors for failed member accounts - memberAccountStatuses, err := configGetOrganizationConformancePackDetailedStatus(conn, name, aws.StringValue(status.Status)) + return status, s, configOrganizationConformancePackDetailedStatusError(conn, name, s) + } - if err != nil { - return status, aws.StringValue(status.Status), fmt.Errorf("unable to get Config Organization Conformance Pack detailed status for showing member account errors: %w", err) - } + return status, aws.StringValue(status.Status), nil + } +} - var errBuilder strings.Builder +func configRefreshOrganizationConformancePackStatus(conn *configservice.ConfigService, name string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + status, err := configDescribeOrganizationConformancePackStatus(conn, name) - for _, mas := range memberAccountStatuses { - errBuilder.WriteString(fmt.Sprintf("Account ID (%s): %s: %s\n", aws.StringValue(mas.AccountId), aws.StringValue(mas.ErrorCode), aws.StringValue(mas.ErrorMessage))) - } + if err != nil { + return nil, "", err + } + + if status == nil { + return nil, "", nil + } + + if status.ErrorCode != nil { + return status, aws.StringValue(status.Status), fmt.Errorf("%s: %s", aws.StringValue(status.ErrorCode), aws.StringValue(status.ErrorMessage)) + } - return status, aws.StringValue(status.Status), fmt.Errorf("Failed in %d account(s):\n\n%s", len(memberAccountStatuses), errBuilder.String()) + switch s := aws.StringValue(status.Status); s { + case configservice.OrganizationResourceStatusCreateFailed, configservice.OrganizationResourceStatusDeleteFailed, configservice.OrganizationResourceStatusUpdateFailed: + return status, s, configOrganizationConformancePackDetailedStatusError(conn, name, s) } return status, aws.StringValue(status.Status), nil } } +func configOrganizationConformancePackDetailedStatusError(conn *configservice.ConfigService, name, status string) error { + memberAccountStatuses, err := configGetOrganizationConformancePackDetailedStatus(conn, name, status) + + if err != nil { + return fmt.Errorf("unable to get Config Organization Conformance Pack detailed status for showing member account errors: %w", err) + } + + var errBuilder strings.Builder + + for _, mas := range memberAccountStatuses { + errBuilder.WriteString(fmt.Sprintf("Account ID (%s): %s: %s\n", aws.StringValue(mas.AccountId), aws.StringValue(mas.ErrorCode), aws.StringValue(mas.ErrorMessage))) + } + + return fmt.Errorf("Failed in %d account(s):\n\n%s", len(memberAccountStatuses), errBuilder.String()) +} + func configWaitForConformancePackStateCreateComplete(conn *configservice.ConfigService, name string) error { stateChangeConf := resource.StateChangeConf{ Pending: []string{configservice.ConformancePackStateCreateInProgress}, @@ -358,6 +392,10 @@ func configWaitForConformancePackStateCreateComplete(conn *configservice.ConfigS _, err := stateChangeConf.WaitForState() + if tfawserr.ErrCodeEquals(err, configservice.ErrCodeNoSuchConformancePackException) { + return nil + } + return err } @@ -384,8 +422,8 @@ func configWaitForOrganizationConformancePackStatusCreateSuccessful(conn *config Pending: []string{configservice.OrganizationResourceStatusCreateInProgress}, Target: []string{configservice.OrganizationResourceStatusCreateSuccessful}, Timeout: ConfigOrganizationConformancePackCreateTimeout, - Refresh: configRefreshOrganizationConformancePackStatus(conn, name), - // Include a Delay to avoid transient error + Refresh: configRefreshOrganizationConformancePackCreationStatus(conn, name), + // Include a delay to help avoid ResourceDoesNotExist errors Delay: 30 * time.Second, } @@ -418,10 +456,6 @@ func configWaitForOrganizationConformancePackStatusDeleteSuccessful(conn *config _, err := stateChangeConf.WaitForState() - if tfawserr.ErrCodeEquals(err, configservice.ErrCodeNoSuchOrganizationConformancePackException) { - return nil - } - return err } diff --git a/aws/resource_aws_config_organization_conformance_pack.go b/aws/resource_aws_config_organization_conformance_pack.go index 7f98d636f95..0e888a33263 100644 --- a/aws/resource_aws_config_organization_conformance_pack.go +++ b/aws/resource_aws_config_organization_conformance_pack.go @@ -8,10 +8,8 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/configservice" "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/tfresource" ) func resourceAwsConfigOrganizationConformancePack() *schema.Resource { @@ -106,7 +104,8 @@ func resourceAwsConfigOrganizationConformancePackCreate(d *schema.ResourceData, conn := meta.(*AWSClient).configconn name := d.Get("name").(string) - input := configservice.PutOrganizationConformancePackInput{ + + input := &configservice.PutOrganizationConformancePackInput{ OrganizationConformancePackName: aws.String(name), } @@ -134,7 +133,7 @@ func resourceAwsConfigOrganizationConformancePackCreate(d *schema.ResourceData, input.TemplateS3Uri = aws.String(v.(string)) } - _, err := conn.PutOrganizationConformancePack(&input) + _, err := conn.PutOrganizationConformancePack(input) if err != nil { return fmt.Errorf("error creating Config Organization Conformance Pack (%s): %w", name, err) @@ -193,7 +192,7 @@ func resourceAwsConfigOrganizationConformancePackRead(d *schema.ResourceData, me func resourceAwsConfigOrganizationConformancePackUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).configconn - input := configservice.PutOrganizationConformancePackInput{ + input := &configservice.PutOrganizationConformancePackInput{ OrganizationConformancePackName: aws.String(d.Id()), } @@ -221,7 +220,7 @@ func resourceAwsConfigOrganizationConformancePackUpdate(d *schema.ResourceData, input.TemplateS3Uri = aws.String(v.(string)) } - _, err := conn.PutOrganizationConformancePack(&input) + _, err := conn.PutOrganizationConformancePack(input) if err != nil { return fmt.Errorf("error updating Config Organization Conformance Pack (%s): %w", d.Id(), err) @@ -241,23 +240,7 @@ func resourceAwsConfigOrganizationConformancePackDelete(d *schema.ResourceData, OrganizationConformancePackName: aws.String(d.Id()), } - err := resource.Retry(ConfigOrganizationConformancePackDeleteTimeout, func() *resource.RetryError { - _, err := conn.DeleteOrganizationConformancePack(input) - - if err != nil { - if tfawserr.ErrCodeEquals(err, configservice.ErrCodeResourceInUseException) { - return resource.RetryableError(err) - } - - return resource.NonRetryableError(err) - } - - return nil - }) - - if tfresource.TimedOut(err) { - _, err = conn.DeleteOrganizationConformancePack(input) - } + _, err := conn.DeleteOrganizationConformancePack(input) if tfawserr.ErrCodeEquals(err, configservice.ErrCodeNoSuchOrganizationConformancePackException) { return nil @@ -268,6 +251,9 @@ func resourceAwsConfigOrganizationConformancePackDelete(d *schema.ResourceData, } if err := configWaitForOrganizationConformancePackStatusDeleteSuccessful(conn, d.Id()); err != nil { + if tfawserr.ErrCodeEquals(err, configservice.ErrCodeNoSuchOrganizationConformancePackException) { + return nil + } return fmt.Errorf("error waiting for Config Organization Conformance Pack (%s) to be deleted: %w", d.Id(), err) } diff --git a/aws/resource_aws_config_organization_conformance_pack_test.go b/aws/resource_aws_config_organization_conformance_pack_test.go index d4ac03ea8d7..585431da032 100644 --- a/aws/resource_aws_config_organization_conformance_pack_test.go +++ b/aws/resource_aws_config_organization_conformance_pack_test.go @@ -32,7 +32,7 @@ func testAccConfigOrganizationConformancePack_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "delivery_s3_bucket", ""), resource.TestCheckResourceAttr(resourceName, "delivery_s3_key_prefix", ""), - resource.TestCheckResourceAttr(resourceName, "input_parameters.#", "0"), + resource.TestCheckResourceAttr(resourceName, "input_parameter.#", "0"), resource.TestCheckResourceAttr(resourceName, "excluded_accounts.#", "0"), ), }, @@ -175,8 +175,8 @@ func testAccConfigOrganizationConformancePack_inputParameters(t *testing.T) { Config: testAccConfigOrganizationConformancePackInputParameterConfig(rName, pKey, pValue), Check: resource.ComposeTestCheckFunc( testAccCheckConfigOrganizationConformancePackExists(resourceName, &pack), - resource.TestCheckResourceAttr(resourceName, "input_parameters.#", "1"), - resource.TestCheckTypeSetElemNestedAttrs(resourceName, "input_parameters.*", map[string]string{ + resource.TestCheckResourceAttr(resourceName, "input_parameter.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "input_parameter.*", map[string]string{ "parameter_name": pKey, "parameter_value": pValue, }), @@ -241,7 +241,7 @@ func testAccConfigOrganizationConformancePack_S3Template(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "delivery_s3_bucket", ""), resource.TestCheckResourceAttr(resourceName, "delivery_s3_key_prefix", ""), - resource.TestCheckResourceAttr(resourceName, "input_parameters.#", "0"), + resource.TestCheckResourceAttr(resourceName, "input_parameter.#", "0"), resource.TestCheckResourceAttr(resourceName, "excluded_accounts.#", "0"), ), }, @@ -424,6 +424,12 @@ func testAccCheckConfigOrganizationConformancePackDestroy(s *terraform.State) er continue } + // In the event the Organizations Organization is deleted first, its Conformance Packs + // are deleted and we can continue through the loop + if tfawserr.ErrCodeEquals(err, configservice.ErrCodeOrganizationAccessDeniedException) { + continue + } + if err != nil { return fmt.Errorf("error describing Config Organization Conformance Pack (%s): %w", rs.Primary.ID, err) } diff --git a/website/docs/r/config_organization_conformance_pack.html.markdown b/website/docs/r/config_organization_conformance_pack.html.markdown index b3798996558..27d5ba5adf8 100644 --- a/website/docs/r/config_organization_conformance_pack.html.markdown +++ b/website/docs/r/config_organization_conformance_pack.html.markdown @@ -107,6 +107,7 @@ The `input_parameter` configuration block supports the following arguments: In addition to all arguments above, the following attributes are exported: * `arn` - Amazon Resource Name (ARN) of the organization conformance pack. +* `id` - The name of the organization conformance pack. ## Import From 3a527d4ecab7d0716d3c1bbcc009810578e72bc3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Jul 2021 06:09:33 +0000 Subject: [PATCH 242/398] build(deps): bump github.com/aws/aws-sdk-go in /awsproviderlint Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.39.2 to 1.39.3. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Changelog](https://github.com/aws/aws-sdk-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.39.2...v1.39.3) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- awsproviderlint/go.mod | 2 +- awsproviderlint/go.sum | 4 +-- .../aws/aws-sdk-go/aws/endpoints/defaults.go | 30 +++++++++++++++++-- .../github.com/aws/aws-sdk-go/aws/version.go | 2 +- awsproviderlint/vendor/modules.txt | 2 +- 5 files changed, 32 insertions(+), 8 deletions(-) diff --git a/awsproviderlint/go.mod b/awsproviderlint/go.mod index bdd8e849828..c457e5dd1a1 100644 --- a/awsproviderlint/go.mod +++ b/awsproviderlint/go.mod @@ -3,7 +3,7 @@ module github.com/terraform-providers/terraform-provider-aws/awsproviderlint go 1.16 require ( - github.com/aws/aws-sdk-go v1.39.2 + github.com/aws/aws-sdk-go v1.39.3 github.com/bflad/tfproviderlint v0.27.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.7.0 golang.org/x/tools v0.0.0-20201028111035-eafbe7b904eb diff --git a/awsproviderlint/go.sum b/awsproviderlint/go.sum index 834e806f661..4816e45d0e3 100644 --- a/awsproviderlint/go.sum +++ b/awsproviderlint/go.sum @@ -70,8 +70,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= github.com/aws/aws-sdk-go v1.25.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.39.2 h1:t+n2j0QfAmGqSQVb1VIGulhSMjfaZ/RqSGlcRKGED9Y= -github.com/aws/aws-sdk-go v1.39.2/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= +github.com/aws/aws-sdk-go v1.39.3 h1:JMDk7p+AV89MdVy/ZcFWAGivWIE3vXOsRriFjFWVcIY= +github.com/aws/aws-sdk-go v1.39.3/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/bflad/gopaniccheck v0.1.0 h1:tJftp+bv42ouERmUMWLoUn/5bi/iQZjHPznM00cP/bU= github.com/bflad/gopaniccheck v0.1.0/go.mod h1:ZCj2vSr7EqVeDaqVsWN4n2MwdROx1YL+LFo47TSWtsA= github.com/bflad/tfproviderlint v0.27.0 h1:KXF+dYaWJ/OSVyWIrk2NIYgQBMDDSOC4VQB/P+P5nhI= diff --git a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go index ec4a25cd3f3..cebbe397201 100644 --- a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go +++ b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go @@ -648,9 +648,33 @@ var awsPartition = partition{ "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, - "us-east-1": endpoint{}, - "us-east-2": endpoint{}, - "us-west-2": endpoint{}, + "fips-ca-central-1": endpoint{ + Hostname: "api.fleethub.iot-fips.ca-central-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ca-central-1", + }, + }, + "fips-us-east-1": endpoint{ + Hostname: "api.fleethub.iot-fips.us-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "fips-us-east-2": endpoint{ + Hostname: "api.fleethub.iot-fips.us-east-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-2", + }, + }, + "fips-us-west-2": endpoint{ + Hostname: "api.fleethub.iot-fips.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-2": endpoint{}, }, }, "api.mediatailor": service{ diff --git a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/version.go b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/version.go index 3fb3be1d54d..19a602480f0 100644 --- a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/version.go +++ b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/version.go @@ -5,4 +5,4 @@ package aws const SDKName = "aws-sdk-go" // SDKVersion is the version of this SDK -const SDKVersion = "1.39.2" +const SDKVersion = "1.39.3" diff --git a/awsproviderlint/vendor/modules.txt b/awsproviderlint/vendor/modules.txt index 87f7b43fbb1..6f58fbb8f97 100644 --- a/awsproviderlint/vendor/modules.txt +++ b/awsproviderlint/vendor/modules.txt @@ -14,7 +14,7 @@ github.com/agext/levenshtein github.com/apparentlymart/go-textseg/v12/textseg # github.com/apparentlymart/go-textseg/v13 v13.0.0 github.com/apparentlymart/go-textseg/v13/textseg -# github.com/aws/aws-sdk-go v1.39.2 +# github.com/aws/aws-sdk-go v1.39.3 ## explicit github.com/aws/aws-sdk-go/aws github.com/aws/aws-sdk-go/aws/arn From 4711564e98cc51efba4e08430f8b0bfead1db445 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Jul 2021 06:12:30 +0000 Subject: [PATCH 243/398] build(deps): bump github.com/aws/aws-sdk-go from 1.39.2 to 1.39.3 Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.39.2 to 1.39.3. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Changelog](https://github.com/aws/aws-sdk-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.39.2...v1.39.3) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f8902716b7c..4e23c51ff63 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.16 require ( github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect - github.com/aws/aws-sdk-go v1.39.2 + github.com/aws/aws-sdk-go v1.39.3 github.com/beevik/etree v1.1.0 github.com/fatih/color v1.9.0 // indirect github.com/hashicorp/aws-sdk-go-base v0.7.1 diff --git a/go.sum b/go.sum index d87b7997d6f..c0acb2cd1b6 100644 --- a/go.sum +++ b/go.sum @@ -66,8 +66,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= github.com/aws/aws-sdk-go v1.25.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.31.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/aws/aws-sdk-go v1.39.2 h1:t+n2j0QfAmGqSQVb1VIGulhSMjfaZ/RqSGlcRKGED9Y= -github.com/aws/aws-sdk-go v1.39.2/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= +github.com/aws/aws-sdk-go v1.39.3 h1:JMDk7p+AV89MdVy/ZcFWAGivWIE3vXOsRriFjFWVcIY= +github.com/aws/aws-sdk-go v1.39.3/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= From 1914930b037d0d275abd7f099f4c92af55a7c543 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 9 Jul 2021 11:41:17 -0400 Subject: [PATCH 244/398] r/aws_directory_service_directory: Add and use internal finder and waiter packages. --- .../service/directoryservice/finder/finder.go | 48 ++++++++++ .../service/directoryservice/waiter/status.go | 25 +++++ .../service/directoryservice/waiter/waiter.go | 54 +++++++++++ ...esource_aws_directory_service_directory.go | 93 ++++--------------- 4 files changed, 143 insertions(+), 77 deletions(-) create mode 100644 aws/internal/service/directoryservice/finder/finder.go create mode 100644 aws/internal/service/directoryservice/waiter/status.go create mode 100644 aws/internal/service/directoryservice/waiter/waiter.go diff --git a/aws/internal/service/directoryservice/finder/finder.go b/aws/internal/service/directoryservice/finder/finder.go new file mode 100644 index 00000000000..3ce7a870786 --- /dev/null +++ b/aws/internal/service/directoryservice/finder/finder.go @@ -0,0 +1,48 @@ +package finder + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/directoryservice" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func DirectoryByID(conn *directoryservice.DirectoryService, id string) (*directoryservice.DirectoryDescription, error) { + input := &directoryservice.DescribeDirectoriesInput{ + DirectoryIds: aws.StringSlice([]string{id}), + } + + output, err := conn.DescribeDirectories(input) + + if tfawserr.ErrCodeEquals(err, directoryservice.ErrCodeEntityDoesNotExistException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || len(output.DirectoryDescriptions) == 0 || output.DirectoryDescriptions[0] == nil { + return nil, &resource.NotFoundError{ + Message: "Empty result", + LastRequest: input, + } + } + + // TODO Check for multiple results. + // TODO https://github.com/hashicorp/terraform-provider-aws/pull/17613. + + directory := output.DirectoryDescriptions[0] + + if stage := aws.StringValue(directory.Stage); stage == directoryservice.DirectoryStageDeleted { + return nil, &resource.NotFoundError{ + Message: stage, + LastRequest: input, + } + } + + return directory, nil +} diff --git a/aws/internal/service/directoryservice/waiter/status.go b/aws/internal/service/directoryservice/waiter/status.go new file mode 100644 index 00000000000..7f6796238b2 --- /dev/null +++ b/aws/internal/service/directoryservice/waiter/status.go @@ -0,0 +1,25 @@ +package waiter + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/directoryservice" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/directoryservice/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" +) + +func DirectoryStage(conn *directoryservice.DirectoryService, id string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := finder.DirectoryByID(conn, id) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, aws.StringValue(output.Stage), nil + } +} diff --git a/aws/internal/service/directoryservice/waiter/waiter.go b/aws/internal/service/directoryservice/waiter/waiter.go new file mode 100644 index 00000000000..47ad07a5606 --- /dev/null +++ b/aws/internal/service/directoryservice/waiter/waiter.go @@ -0,0 +1,54 @@ +package waiter + +import ( + "errors" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/directoryservice" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" +) + +const ( + DirectoryCreatedTimeout = 60 * time.Minute + DirectoryDeletedTimeout = 60 * time.Minute +) + +func DirectoryCreated(conn *directoryservice.DirectoryService, id string) (*directoryservice.DirectoryDescription, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{directoryservice.DirectoryStageRequested, directoryservice.DirectoryStageCreating, directoryservice.DirectoryStageCreated}, + Target: []string{directoryservice.DirectoryStageActive}, + Refresh: DirectoryStage(conn, id), + Timeout: DirectoryCreatedTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*directoryservice.DirectoryDescription); ok { + tfresource.SetLastError(err, errors.New(aws.StringValue(output.StageReason))) + + return output, err + } + + return nil, err +} + +func DirectoryDeleted(conn *directoryservice.DirectoryService, id string) (*directoryservice.DirectoryDescription, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{directoryservice.DirectoryStageActive, directoryservice.DirectoryStageDeleting}, + Target: []string{}, + Refresh: DirectoryStage(conn, id), + Timeout: DirectoryDeletedTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*directoryservice.DirectoryDescription); ok { + tfresource.SetLastError(err, errors.New(aws.StringValue(output.StageReason))) + + return output, err + } + + return nil, err +} diff --git a/aws/resource_aws_directory_service_directory.go b/aws/resource_aws_directory_service_directory.go index ca74e62535e..ba64d803008 100644 --- a/aws/resource_aws_directory_service_directory.go +++ b/aws/resource_aws_directory_service_directory.go @@ -3,15 +3,16 @@ package aws import ( "fmt" "log" - "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/directoryservice" "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" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/directoryservice/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/directoryservice/waiter" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func resourceAwsDirectoryServiceDirectory() *schema.Resource { @@ -387,35 +388,10 @@ func resourceAwsDirectoryServiceDirectoryCreate(d *schema.ResourceData, meta int d.SetId(directoryId) - // Wait for creation - log.Printf("[DEBUG] Waiting for DS (%q) to become available", d.Id()) - stateConf := &resource.StateChangeConf{ - Pending: []string{ - directoryservice.DirectoryStageRequested, - directoryservice.DirectoryStageCreating, - directoryservice.DirectoryStageCreated, - }, - Target: []string{directoryservice.DirectoryStageActive}, - Refresh: func() (interface{}, string, error) { - resp, err := dsconn.DescribeDirectories(&directoryservice.DescribeDirectoriesInput{ - DirectoryIds: []*string{aws.String(d.Id())}, - }) - if err != nil { - log.Printf("Error during creation of DS: %q", err.Error()) - return nil, "", err - } - - ds := resp.DirectoryDescriptions[0] - log.Printf("[DEBUG] Creation of DS %q is in following stage: %q.", - d.Id(), *ds.Stage) - return ds, *ds.Stage, nil - }, - Timeout: 60 * time.Minute, - } - if _, err := stateConf.WaitForState(); err != nil { - return fmt.Errorf( - "Error waiting for Directory Service (%s) to become available: %s", - d.Id(), err) + _, err = waiter.DirectoryCreated(dsconn, d.Id()) + + if err != nil { + return fmt.Errorf("error waiting for Directory Service Directory (%s) to create: %w", d.Id(), err) } if v, ok := d.GetOk("alias"); ok { @@ -468,22 +444,18 @@ func resourceAwsDirectoryServiceDirectoryRead(d *schema.ResourceData, meta inter defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig - input := directoryservice.DescribeDirectoriesInput{ - DirectoryIds: []*string{aws.String(d.Id())}, - } - out, err := dsconn.DescribeDirectories(&input) - if err != nil { - return err - - } + dir, err := finder.DirectoryByID(dsconn, d.Id()) - if len(out.DirectoryDescriptions) == 0 { - log.Printf("[WARN] Directory %s not found", d.Id()) + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] Directory Service Directory (%s) not found, removing from state", d.Id()) d.SetId("") return nil } - dir := out.DirectoryDescriptions[0] + if err != nil { + return fmt.Errorf("error reading Directory Service Directory (%s): %w", d.Id(), err) + } + log.Printf("[DEBUG] Received DS directory: %s", dir) d.Set("access_url", dir.AccessUrl) @@ -554,44 +526,11 @@ func resourceAwsDirectoryServiceDirectoryDelete(d *schema.ResourceData, meta int return fmt.Errorf("error deleting Directory Service Directory (%s): %w", d.Id(), err) } - err = waitForDirectoryServiceDirectoryDeletion(conn, d.Id()) + _, err = waiter.DirectoryDeleted(conn, d.Id()) if err != nil { - return fmt.Errorf("error waiting for Directory Service (%s) to be deleted: %w", d.Id(), err) + return fmt.Errorf("error waiting for Directory Service Directory (%s) to delete: %w", d.Id(), err) } return nil } - -func waitForDirectoryServiceDirectoryDeletion(conn *directoryservice.DirectoryService, directoryID string) error { - stateConf := &resource.StateChangeConf{ - Pending: []string{ - directoryservice.DirectoryStageActive, - directoryservice.DirectoryStageDeleting, - }, - Target: []string{directoryservice.DirectoryStageDeleted}, - Refresh: func() (interface{}, string, error) { - resp, err := conn.DescribeDirectories(&directoryservice.DescribeDirectoriesInput{ - DirectoryIds: []*string{aws.String(directoryID)}, - }) - if err != nil { - if isAWSErr(err, directoryservice.ErrCodeEntityDoesNotExistException, "") { - return 42, directoryservice.DirectoryStageDeleted, nil - } - return nil, "error", err - } - - if len(resp.DirectoryDescriptions) == 0 || resp.DirectoryDescriptions[0] == nil { - return 42, directoryservice.DirectoryStageDeleted, nil - } - - ds := resp.DirectoryDescriptions[0] - log.Printf("[DEBUG] Deletion of Directory Service Directory %q is in following stage: %q.", directoryID, aws.StringValue(ds.Stage)) - return ds, aws.StringValue(ds.Stage), nil - }, - Timeout: 60 * time.Minute, - } - _, err := stateConf.WaitForState() - - return err -} From e75ba1b21cc08e273c8734cb42e1f746064baff6 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 9 Jul 2021 11:46:58 -0400 Subject: [PATCH 245/398] Configuration changes for 'TestAccAwsDxGatewayAssociation_recreateProposal'. --- ...ws_dx_gateway_association_proposal_test.go | 10 ------ ...esource_aws_dx_gateway_association_test.go | 32 +++++++++++++++++-- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/aws/resource_aws_dx_gateway_association_proposal_test.go b/aws/resource_aws_dx_gateway_association_proposal_test.go index 700ae9ec673..5cc620a0520 100644 --- a/aws/resource_aws_dx_gateway_association_proposal_test.go +++ b/aws/resource_aws_dx_gateway_association_proposal_test.go @@ -351,16 +351,6 @@ func testAccCheckAwsDxGatewayAssociationProposalRecreated(old, new *directconnec } } -// func testAccCheckAwsDxGatewayAssociationProposalNotRecreated(old, new *directconnect.GatewayAssociationProposal) resource.TestCheckFunc { -// return func(s *terraform.State) error { -// if old, new := aws.StringValue(old.ProposalId), aws.StringValue(new.ProposalId); old != new { -// return fmt.Errorf("Direct Connect Gateway Association Proposal (%s) recreated (%s)", old, new) -// } - -// return nil -// } -// } - func testAccCheckAwsDxGatewayAssociationProposalAccepted(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] diff --git a/aws/resource_aws_dx_gateway_association_test.go b/aws/resource_aws_dx_gateway_association_test.go index 6578f18265e..9a72d222fea 100644 --- a/aws/resource_aws_dx_gateway_association_test.go +++ b/aws/resource_aws_dx_gateway_association_test.go @@ -463,7 +463,6 @@ func TestAccAwsDxGatewayAssociation_allowedPrefixesVpnGatewayCrossAccount(t *tes func TestAccAwsDxGatewayAssociation_recreateProposal(t *testing.T) { var providers []*schema.Provider resourceName := "aws_dx_gateway_association.test" - resourceNameProposal := "aws_dx_gateway_association_proposal.test" rName := acctest.RandomWithPrefix("tf-acc-test") rBgpAsn := acctest.RandIntRange(64512, 65534) var ga1, ga2 directconnect.GatewayAssociation @@ -482,13 +481,12 @@ func TestAccAwsDxGatewayAssociation_recreateProposal(t *testing.T) { ), }, { - Config: testAccDxGatewayAssociationConfig_basicVpnGatewayCrossAccount(rName, rBgpAsn), + Config: testAccDxGatewayAssociationConfig_basicVpnGatewayCrossAccountUpdatedProposal(rName, rBgpAsn), Check: resource.ComposeTestCheckFunc( testAccCheckAwsDxGatewayAssociationExists(resourceName, &ga2, &gap2), testAccCheckAwsDxGatewayAssociationNotRecreated(&ga1, &ga2), testAccCheckAwsDxGatewayAssociationProposalRecreated(&gap1, &gap2), ), - Taint: []string{resourceNameProposal}, }, }, }) @@ -701,6 +699,34 @@ resource "aws_dx_gateway_association" "test" { `) } +func testAccDxGatewayAssociationConfig_basicVpnGatewayCrossAccountUpdatedProposal(rName string, rBgpAsn int) string { + return composeConfig( + testAccDxGatewayAssociationConfigBase_vpnGatewayCrossAccount(rName, rBgpAsn), + ` +# Creator +resource "aws_dx_gateway_association_proposal" "test" { + dx_gateway_id = aws_dx_gateway.test.id + dx_gateway_owner_account_id = aws_dx_gateway.test.owner_account_id + associated_gateway_id = aws_vpn_gateway_attachment.test.vpn_gateway_id +} + +resource "aws_dx_gateway_association_proposal" "test2" { + dx_gateway_id = aws_dx_gateway.test.id + dx_gateway_owner_account_id = aws_dx_gateway.test.owner_account_id + associated_gateway_id = aws_vpn_gateway_attachment.test.vpn_gateway_id + } + +# Accepter +resource "aws_dx_gateway_association" "test" { + provider = "awsalternate" + + proposal_id = aws_dx_gateway_association_proposal.test2.id + dx_gateway_id = aws_dx_gateway.test.id + associated_gateway_owner_account_id = data.aws_caller_identity.creator.account_id +} +`) +} + func testAccDxGatewayAssociationConfig_basicTransitGatewaySingleAccount(rName string, rBgpAsn int) string { return fmt.Sprintf(` resource "aws_dx_gateway" "test" { From 27618a9b5b573e9bf85635ec6ba7f724bdf0796f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 9 Jul 2021 12:07:08 -0400 Subject: [PATCH 246/398] Add CHANGELOG entry. --- .changelog/20031.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .changelog/20031.txt diff --git a/.changelog/20031.txt b/.changelog/20031.txt new file mode 100644 index 00000000000..833dab21350 --- /dev/null +++ b/.changelog/20031.txt @@ -0,0 +1,11 @@ +```release-note:bug +resource/aws_cognito_user_pool_client: Retry on `ConcurrentModificationException` +``` + +```release-note:bug +resource/aws_cognito_user_pool_client: Allow the `default_redirect_uri` argument value to be an empty string +``` + +```release-note:enhancement +resource/aws_cognito_user_pool_client: Add the `enable_token_revocation` argument to support targeted sign out +``` \ No newline at end of file From 1983631376fc8eb37a8a627bd225b70b44df3c7f Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 9 Jul 2021 12:23:58 -0400 Subject: [PATCH 247/398] r/db_instance: Clean up arguments --- aws/resource_aws_db_instance.go | 450 ++++++++++++++------------------ 1 file changed, 196 insertions(+), 254 deletions(-) diff --git a/aws/resource_aws_db_instance.go b/aws/resource_aws_db_instance.go index a9c2ed0be76..79458ce4c7a 100644 --- a/aws/resource_aws_db_instance.go +++ b/aws/resource_aws_db_instance.go @@ -44,36 +44,137 @@ func resourceAwsDbInstance() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "name": { + "address": { Type: schema.TypeString, - Optional: true, Computed: true, - ForceNew: true, }, + "allocated_storage": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + mas := d.Get("max_allocated_storage").(int) + + newInt, err := strconv.Atoi(new) + if err != nil { + return false + } + + oldInt, err := strconv.Atoi(old) + + if err != nil { + return false + } + + // Allocated is higher than the configuration + // and autoscaling is enabled + if oldInt > newInt && mas > newInt { + return true + } + + return false + }, + }, + "allow_major_version_upgrade": { + Type: schema.TypeBool, + Optional: true, + }, + // apply_immediately is used to determine when the update modifications + // take place. + // See http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.DBInstance.Modifying.html + "apply_immediately": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + }, "arn": { Type: schema.TypeString, Computed: true, }, - - "username": { + "auto_minor_version_upgrade": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "availability_zone": { Type: schema.TypeString, Optional: true, Computed: true, ForceNew: true, }, - - "password": { - Type: schema.TypeString, - Optional: true, - Sensitive: true, + "backup_retention_period": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + "backup_window": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateOnceADayWindowFormat, + }, + "ca_cert_identifier": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "character_set_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "copy_tags_to_snapshot": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "db_subnet_group_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "delete_automated_backups": { + Type: schema.TypeBool, + Optional: true, + Default: true, }, - "deletion_protection": { Type: schema.TypeBool, Optional: true, }, - + "domain": { + Type: schema.TypeString, + Optional: true, + }, + "domain_iam_role_name": { + Type: schema.TypeString, + Optional: true, + }, + "enabled_cloudwatch_logs_exports": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + "agent", + "alert", + "audit", + "error", + "general", + "listener", + "slowquery", + "trace", + "postgresql", + "upgrade", + }, false), + }, + }, + "endpoint": { + Type: schema.TypeString, + Computed: true, + }, "engine": { Type: schema.TypeString, Optional: true, @@ -84,68 +185,30 @@ func resourceAwsDbInstance() *schema.Resource { return strings.ToLower(value) }, }, - "engine_version": { Type: schema.TypeString, Optional: true, Computed: true, DiffSuppressFunc: suppressAwsDbEngineVersionDiffs, }, - - "ca_cert_identifier": { + "final_snapshot_identifier": { Type: schema.TypeString, Optional: true, - Computed: true, + ValidateFunc: validation.All( + validation.StringMatch(regexp.MustCompile(`^[A-Za-z]`), "must begin with alphabetic character"), + validation.StringMatch(regexp.MustCompile(`^[0-9A-Za-z-]+$`), "must only contain alphanumeric characters and hyphens"), + validation.StringDoesNotMatch(regexp.MustCompile(`--`), "cannot contain two consecutive hyphens"), + validation.StringDoesNotMatch(regexp.MustCompile(`-$`), "cannot end in a hyphen"), + ), }, - - "character_set_name": { + "hosted_zone_id": { Type: schema.TypeString, - Optional: true, Computed: true, - ForceNew: true, }, - - "storage_encrypted": { + "iam_database_authentication_enabled": { Type: schema.TypeBool, Optional: true, - ForceNew: true, - }, - - "allocated_storage": { - Type: schema.TypeInt, - Optional: true, - Computed: true, - DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - mas := d.Get("max_allocated_storage").(int) - - newInt, err := strconv.Atoi(new) - - if err != nil { - return false - } - - oldInt, err := strconv.Atoi(old) - - if err != nil { - return false - } - - // Allocated is higher than the configuration - // and autoscaling is enabled - if oldInt > newInt && mas > newInt { - return true - } - - return false - }, - }, - - "storage_type": { - Type: schema.TypeString, - Optional: true, - Computed: true, }, - "identifier": { Type: schema.TypeString, Optional: true, @@ -161,48 +224,30 @@ func resourceAwsDbInstance() *schema.Resource { ForceNew: true, ValidateFunc: validateRdsIdentifierPrefix, }, - "instance_class": { Type: schema.TypeString, Required: true, }, - - "availability_zone": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - - "backup_retention_period": { + "iops": { Type: schema.TypeInt, Optional: true, - Computed: true, }, - - "backup_window": { + "kms_key_id": { Type: schema.TypeString, Optional: true, Computed: true, - ValidateFunc: validateOnceADayWindowFormat, - }, - - "iops": { - Type: schema.TypeInt, - Optional: true, + ForceNew: true, + ValidateFunc: validateArn, }, - "latest_restorable_time": { Type: schema.TypeString, Computed: true, }, - "license_model": { Type: schema.TypeString, Optional: true, Computed: true, }, - "maintenance_window": { Type: schema.TypeString, Optional: true, @@ -216,7 +261,6 @@ func resourceAwsDbInstance() *schema.Resource { }, ValidateFunc: validateOnceAWeekWindowFormat, }, - "max_allocated_storage": { Type: schema.TypeInt, Optional: true, @@ -227,51 +271,81 @@ func resourceAwsDbInstance() *schema.Resource { return false }, }, - + "monitoring_interval": { + Type: schema.TypeInt, + Optional: true, + Default: 0, + }, + "monitoring_role_arn": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, "multi_az": { Type: schema.TypeBool, Optional: true, Computed: true, }, - + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "option_group_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "parameter_group_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "password": { + Type: schema.TypeString, + Optional: true, + Sensitive: true, + }, + "performance_insights_enabled": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "performance_insights_kms_key_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateArn, + }, + "performance_insights_retention_period": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, "port": { Type: schema.TypeInt, Optional: true, Computed: true, }, - "publicly_accessible": { Type: schema.TypeBool, Optional: true, Default: false, }, - - "vpc_security_group_ids": { - Type: schema.TypeSet, - Optional: true, + "replicas": { + Type: schema.TypeList, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, }, - - "security_group_names": { - Type: schema.TypeSet, + "replicate_source_db": { + Type: schema.TypeString, Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, }, - - "final_snapshot_identifier": { + "resource_id": { Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.All( - validation.StringMatch(regexp.MustCompile(`^[A-Za-z]`), "must begin with alphabetic character"), - validation.StringMatch(regexp.MustCompile(`^[0-9A-Za-z-]+$`), "must only contain alphanumeric characters and hyphens"), - validation.StringDoesNotMatch(regexp.MustCompile(`--`), "cannot contain two consecutive hyphens"), - validation.StringDoesNotMatch(regexp.MustCompile(`-$`), "cannot end in a hyphen"), - ), + Computed: true, }, - "restore_to_point_in_time": { Type: schema.TypeList, Optional: true, @@ -309,7 +383,6 @@ func resourceAwsDbInstance() *schema.Resource { }, }, }, - "s3_import": { Type: schema.TypeList, Optional: true, @@ -348,189 +421,58 @@ func resourceAwsDbInstance() *schema.Resource { }, }, }, - - "skip_final_snapshot": { - Type: schema.TypeBool, + "security_group_names": { + Type: schema.TypeSet, Optional: true, - Default: false, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, }, - - "copy_tags_to_snapshot": { + "skip_final_snapshot": { Type: schema.TypeBool, Optional: true, Default: false, }, - - "db_subnet_group_name": { + "snapshot_identifier": { Type: schema.TypeString, - Optional: true, Computed: true, - }, - - "parameter_group_name": { - Type: schema.TypeString, Optional: true, - Computed: true, - }, - - "address": { - Type: schema.TypeString, - Computed: true, - }, - - "endpoint": { - Type: schema.TypeString, - Computed: true, - }, - - "hosted_zone_id": { - Type: schema.TypeString, - Computed: true, + ForceNew: true, }, - "status": { Type: schema.TypeString, Computed: true, }, - - // apply_immediately is used to determine when the update modifications - // take place. - // See http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.DBInstance.Modifying.html - "apply_immediately": { + "storage_encrypted": { Type: schema.TypeBool, Optional: true, - Computed: true, - }, - - "replicate_source_db": { - Type: schema.TypeString, - Optional: true, - }, - - "replicas": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - - "snapshot_identifier": { - Type: schema.TypeString, - Computed: true, - Optional: true, ForceNew: true, }, - - "auto_minor_version_upgrade": { - Type: schema.TypeBool, - Optional: true, - Default: true, - }, - - "allow_major_version_upgrade": { - Type: schema.TypeBool, - Optional: true, - }, - - "monitoring_role_arn": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - - "monitoring_interval": { - Type: schema.TypeInt, - Optional: true, - Default: 0, - }, - - "option_group_name": { + "storage_type": { Type: schema.TypeString, Optional: true, Computed: true, }, - - "kms_key_id": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ValidateFunc: validateArn, - }, - + "tags": tagsSchema(), + "tags_all": tagsSchemaComputed(), "timezone": { Type: schema.TypeString, Optional: true, Computed: true, ForceNew: true, }, - - "iam_database_authentication_enabled": { - Type: schema.TypeBool, - Optional: true, - }, - - "resource_id": { + "username": { Type: schema.TypeString, + Optional: true, Computed: true, + ForceNew: true, }, - - "enabled_cloudwatch_logs_exports": { + "vpc_security_group_ids": { Type: schema.TypeSet, Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validation.StringInSlice([]string{ - "agent", - "alert", - "audit", - "error", - "general", - "listener", - "slowquery", - "trace", - "postgresql", - "upgrade", - }, false), - }, - }, - - "domain": { - Type: schema.TypeString, - Optional: true, - }, - - "domain_iam_role_name": { - Type: schema.TypeString, - Optional: true, - }, - - "performance_insights_enabled": { - Type: schema.TypeBool, - Optional: true, - Default: false, - }, - - "performance_insights_kms_key_id": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validateArn, - }, - - "performance_insights_retention_period": { - Type: schema.TypeInt, - Optional: true, Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, }, - - "delete_automated_backups": { - Type: schema.TypeBool, - Optional: true, - Default: true, - }, - - "tags": tagsSchema(), - "tags_all": tagsSchemaComputed(), }, CustomizeDiff: SetTagsDiff, From abaa2d775fba8b739b0faa6d65b8899a0ace0f41 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 9 Jul 2021 12:26:06 -0400 Subject: [PATCH 248/398] tests/r/db_instance: Clean interpolation only --- aws/resource_aws_db_instance_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/aws/resource_aws_db_instance_test.go b/aws/resource_aws_db_instance_test.go index 5d59b8a29b9..6cb4e3d8d67 100644 --- a/aws/resource_aws_db_instance_test.go +++ b/aws/resource_aws_db_instance_test.go @@ -5455,8 +5455,8 @@ resource "aws_db_instance" "source" { resource "aws_db_instance" "test" { identifier = %q - instance_class = "${aws_db_instance.source.instance_class}" - replicate_source_db = "${aws_db_instance.source.id}" + instance_class = aws_db_instance.source.instance_class + replicate_source_db = aws_db_instance.source.id skip_final_snapshot = true iops = %d storage_type = "io1" @@ -5482,8 +5482,8 @@ resource "aws_db_instance" "source" { resource "aws_db_instance" "test" { allocated_storage = %[2]d identifier = %[1]q - instance_class = "${aws_db_instance.source.instance_class}" - replicate_source_db = "${aws_db_instance.source.id}" + instance_class = aws_db_instance.source.instance_class + replicate_source_db = aws_db_instance.source.id skip_final_snapshot = true iops = %[3]d storage_type = "io1" From 7fefe9f9e157fb4262b4af90fd19a1a6d846bfc4 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 9 Jul 2021 13:47:33 -0400 Subject: [PATCH 249/398] tests/r/db_instance: Add errorchecks --- aws/resource_aws_db_instance_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aws/resource_aws_db_instance_test.go b/aws/resource_aws_db_instance_test.go index 6cb4e3d8d67..f6728e1b2f4 100644 --- a/aws/resource_aws_db_instance_test.go +++ b/aws/resource_aws_db_instance_test.go @@ -658,6 +658,7 @@ func TestAccAWSDBInstance_ReplicateSourceDb_Iops(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, rds.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckAWSDBInstanceDestroy, Steps: []resource.TestStep{ @@ -683,6 +684,7 @@ func TestAccAWSDBInstance_ReplicateSourceDb_AllocatedStorageAndIops(t *testing.T resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, rds.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckAWSDBInstanceDestroy, Steps: []resource.TestStep{ From e2fb6ed3e0b30495f1dfa5e2887218f6d91951c2 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 9 Jul 2021 13:49:37 -0400 Subject: [PATCH 250/398] Report any FailureDetails. --- aws/fsx.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/aws/fsx.go b/aws/fsx.go index 7aa5d989698..9e4cc968e8a 100644 --- a/aws/fsx.go +++ b/aws/fsx.go @@ -1,11 +1,13 @@ package aws import ( + "errors" "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/fsx" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func describeFsxFileSystem(conn *fsx.FSx, id string) (*fsx.FileSystem, error) { @@ -87,7 +89,13 @@ func waitForFsxFileSystemCreation(conn *fsx.FSx, id string, timeout time.Duratio Delay: 30 * time.Second, } - _, err := stateConf.WaitForState() + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*fsx.FileSystem); ok { + if output.FailureDetails != nil { + tfresource.SetLastError(err, errors.New(aws.StringValue(output.FailureDetails.Message))) + } + } return err } @@ -101,7 +109,13 @@ func waitForFsxFileSystemDeletion(conn *fsx.FSx, id string, timeout time.Duratio Delay: 30 * time.Second, } - _, err := stateConf.WaitForState() + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*fsx.FileSystem); ok { + if output.FailureDetails != nil { + tfresource.SetLastError(err, errors.New(aws.StringValue(output.FailureDetails.Message))) + } + } return err } From b565d310616793cbc0792d249bfa341f21ffef61 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 9 Jul 2021 13:56:21 -0400 Subject: [PATCH 251/398] r/db_instance: Add changelog --- .changelog/12548.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/12548.txt diff --git a/.changelog/12548.txt b/.changelog/12548.txt new file mode 100644 index 00000000000..83b4cd6d85a --- /dev/null +++ b/.changelog/12548.txt @@ -0,0 +1,3 @@ +```release-note:bug +resource/aws_db_instance: Ignore allocated_storage for replica at creation time +``` \ No newline at end of file From 8270c7a9d28f5b0c396022c80cf1937fe3c07e07 Mon Sep 17 00:00:00 2001 From: changelogbot Date: Fri, 9 Jul 2021 18:22:03 +0000 Subject: [PATCH 252/398] Update CHANGELOG.md for #20114 --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64ba9062ffd..1588887e975 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,19 @@ ## 3.50.0 (Unreleased) + +FEATURES: + +* **New Resource:** `aws_config_organization_conformance_pack` ([#17298](https://github.com/hashicorp/terraform-provider-aws/issues/17298)) + +ENHANCEMENTS: + +* resource/aws_cognito_user_pool_client: Add the `enable_token_revocation` argument to support targeted sign out ([#20031](https://github.com/hashicorp/terraform-provider-aws/issues/20031)) +* resource/fsx_windows_file_system: Add `aliases` argument. ([#20054](https://github.com/hashicorp/terraform-provider-aws/issues/20054)) + +BUG FIXES: + +* resource/aws_cognito_user_pool_client: Allow the `default_redirect_uri` argument value to be an empty string ([#20031](https://github.com/hashicorp/terraform-provider-aws/issues/20031)) +* resource/aws_cognito_user_pool_client: Retry on `ConcurrentModificationException` ([#20031](https://github.com/hashicorp/terraform-provider-aws/issues/20031)) + ## 3.49.0 (July 08, 2021) FEATURES: From bfcec117ee00e9183bfce0a6bcb4c8d9d65f556e Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Fri, 25 Jun 2021 12:42:46 -0700 Subject: [PATCH 253/398] Removes `terraformtest.com` domain names from API Gateway acceptance tests --- aws/data_source_aws_api_gateway_domain_name_test.go | 3 +-- aws/provider_test.go | 12 ++++++++++++ ...esource_aws_api_gateway_base_path_mapping_test.go | 9 ++++----- aws/resource_aws_api_gateway_domain_name_test.go | 12 ++++++------ 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/aws/data_source_aws_api_gateway_domain_name_test.go b/aws/data_source_aws_api_gateway_domain_name_test.go index a415f2becc0..a2bab5406a7 100644 --- a/aws/data_source_aws_api_gateway_domain_name_test.go +++ b/aws/data_source_aws_api_gateway_domain_name_test.go @@ -5,14 +5,13 @@ import ( "testing" "github.com/aws/aws-sdk-go/service/apigateway" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) func TestAccDataSourceAwsApiGatewayDomainName_basic(t *testing.T) { resourceName := "aws_api_gateway_domain_name.test" dataSourceName := "data.aws_api_gateway_domain_name.test" - rName := fmt.Sprintf("tf-acc-%s.terraformtest.com", acctest.RandString(8)) + rName := testAccRandomSubdomain() key := tlsRsaPrivateKeyPem(2048) certificate := tlsRsaX509SelfSignedCertificatePem(key, rName) diff --git a/aws/provider_test.go b/aws/provider_test.go index de8c5142252..c0a96b2a353 100644 --- a/aws/provider_test.go +++ b/aws/provider_test.go @@ -19,6 +19,7 @@ import ( "github.com/aws/aws-sdk-go/service/iam" "github.com/aws/aws-sdk-go/service/organizations" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" @@ -2283,3 +2284,14 @@ func composeConfig(config ...string) string { return str.String() } + +const defaultRootLevelDomain = "example.com" +const defaultRootLevelDomainWildcard = "*." + defaultRootLevelDomain + +// testAccRandomSubdomain creates a random three-level domain name of the form +// ".example.com" +// The second level domain name "example.com" is reserved by IANA for testing and +// documentation purposes: https://datatracker.ietf.org/doc/html/rfc2606 +func testAccRandomSubdomain() string { + return fmt.Sprintf("%s.%s", acctest.RandString(8), defaultRootLevelDomain) +} diff --git a/aws/resource_aws_api_gateway_base_path_mapping_test.go b/aws/resource_aws_api_gateway_base_path_mapping_test.go index 0462b29e55e..3adfd1eebfa 100644 --- a/aws/resource_aws_api_gateway_base_path_mapping_test.go +++ b/aws/resource_aws_api_gateway_base_path_mapping_test.go @@ -6,7 +6,6 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/apigateway" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) @@ -66,7 +65,7 @@ func TestDecodeApiGatewayBasePathMappingId(t *testing.T) { func TestAccAWSAPIGatewayBasePathMapping_basic(t *testing.T) { var conf apigateway.BasePathMapping - name := fmt.Sprintf("tf-acc-%s.terraformtest.com", acctest.RandString(8)) + name := testAccRandomSubdomain() key := tlsRsaPrivateKeyPem(2048) certificate := tlsRsaX509SelfSignedCertificatePem(key, name) @@ -96,7 +95,7 @@ func TestAccAWSAPIGatewayBasePathMapping_basic(t *testing.T) { func TestAccAWSAPIGatewayBasePathMapping_BasePath_Empty(t *testing.T) { var conf apigateway.BasePathMapping - name := fmt.Sprintf("tf-acc-%s.terraformtest.com", acctest.RandString(8)) + name := testAccRandomSubdomain() key := tlsRsaPrivateKeyPem(2048) certificate := tlsRsaX509SelfSignedCertificatePem(key, name) @@ -125,7 +124,7 @@ func TestAccAWSAPIGatewayBasePathMapping_BasePath_Empty(t *testing.T) { func TestAccAWSAPIGatewayBasePathMapping_updates(t *testing.T) { var confFirst, conf apigateway.BasePathMapping resourceName := "aws_api_gateway_base_path_mapping.test" - name := fmt.Sprintf("tf-acc-%s.terraformtest.com", acctest.RandString(8)) + name := testAccRandomSubdomain() key := tlsRsaPrivateKeyPem(2048) certificate := tlsRsaX509SelfSignedCertificatePem(key, name) @@ -175,7 +174,7 @@ func TestAccAWSAPIGatewayBasePathMapping_updates(t *testing.T) { func TestAccAWSAPIGatewayBasePathMapping_disappears(t *testing.T) { var conf apigateway.BasePathMapping - name := fmt.Sprintf("tf-acc-%s.terraformtest.com", acctest.RandString(8)) + name := testAccRandomSubdomain() resourceName := "aws_api_gateway_base_path_mapping.test" key := tlsRsaPrivateKeyPem(2048) diff --git a/aws/resource_aws_api_gateway_domain_name_test.go b/aws/resource_aws_api_gateway_domain_name_test.go index b0e7e33dcd4..523329aeda7 100644 --- a/aws/resource_aws_api_gateway_domain_name_test.go +++ b/aws/resource_aws_api_gateway_domain_name_test.go @@ -115,7 +115,7 @@ func TestAccAWSAPIGatewayDomainName_CertificateName(t *testing.T) { func TestAccAWSAPIGatewayDomainName_RegionalCertificateArn(t *testing.T) { var domainName apigateway.DomainName resourceName := "aws_api_gateway_domain_name.test" - rName := fmt.Sprintf("tf-acc-%s.terraformtest.com", acctest.RandString(8)) + rName := testAccRandomSubdomain() key := tlsRsaPrivateKeyPem(2048) certificate := tlsRsaX509SelfSignedCertificatePem(key, rName) @@ -157,12 +157,12 @@ func TestAccAWSAPIGatewayDomainName_RegionalCertificateName(t *testing.T) { var domainName apigateway.DomainName resourceName := "aws_api_gateway_domain_name.test" - rName := fmt.Sprintf("tf-acc-%s.terraformtest.com", acctest.RandString(8)) + rName := testAccRandomSubdomain() caKey := tlsRsaPrivateKeyPem(2048) caCertificate := tlsRsaX509SelfSignedCaCertificatePem(caKey) key := tlsRsaPrivateKeyPem(2048) - certificate := tlsRsaX509LocallySignedCertificatePem(caKey, caCertificate, key, "*.terraformtest.com") + certificate := tlsRsaX509LocallySignedCertificatePem(caKey, caCertificate, key, defaultRootLevelDomainWildcard) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -192,7 +192,7 @@ func TestAccAWSAPIGatewayDomainName_RegionalCertificateName(t *testing.T) { func TestAccAWSAPIGatewayDomainName_SecurityPolicy(t *testing.T) { var domainName apigateway.DomainName resourceName := "aws_api_gateway_domain_name.test" - rName := fmt.Sprintf("tf-acc-%s.terraformtest.com", acctest.RandString(8)) + rName := testAccRandomSubdomain() key := tlsRsaPrivateKeyPem(2048) certificate := tlsRsaX509SelfSignedCertificatePem(key, rName) @@ -222,7 +222,7 @@ func TestAccAWSAPIGatewayDomainName_SecurityPolicy(t *testing.T) { func TestAccAWSAPIGatewayDomainName_Tags(t *testing.T) { var domainName apigateway.DomainName resourceName := "aws_api_gateway_domain_name.test" - rName := fmt.Sprintf("tf-acc-%s.terraformtest.com", acctest.RandString(8)) + rName := testAccRandomSubdomain() key := tlsRsaPrivateKeyPem(2048) certificate := tlsRsaX509SelfSignedCertificatePem(key, rName) @@ -270,7 +270,7 @@ func TestAccAWSAPIGatewayDomainName_Tags(t *testing.T) { func TestAccAWSAPIGatewayDomainName_disappears(t *testing.T) { var domainName apigateway.DomainName resourceName := "aws_api_gateway_domain_name.test" - rName := fmt.Sprintf("tf-acc-%s.terraformtest.com", acctest.RandString(8)) + rName := testAccRandomSubdomain() key := tlsRsaPrivateKeyPem(2048) certificate := tlsRsaX509SelfSignedCertificatePem(key, rName) From a9dcf6d752a70026211c72d9afc3b745828d7a91 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Fri, 25 Jun 2021 15:23:01 -0700 Subject: [PATCH 254/398] Removes domain names from Glue connection acceptance tests --- aws/provider_test.go | 9 ++ aws/resource_aws_glue_connection_test.go | 138 +++++++++++++---------- 2 files changed, 85 insertions(+), 62 deletions(-) diff --git a/aws/provider_test.go b/aws/provider_test.go index c0a96b2a353..dd41f9e39fe 100644 --- a/aws/provider_test.go +++ b/aws/provider_test.go @@ -2287,6 +2287,7 @@ func composeConfig(config ...string) string { const defaultRootLevelDomain = "example.com" const defaultRootLevelDomainWildcard = "*." + defaultRootLevelDomain +const testingTopLevelDomain = "test" // testAccRandomSubdomain creates a random three-level domain name of the form // ".example.com" @@ -2295,3 +2296,11 @@ const defaultRootLevelDomainWildcard = "*." + defaultRootLevelDomain func testAccRandomSubdomain() string { return fmt.Sprintf("%s.%s", acctest.RandString(8), defaultRootLevelDomain) } + +// testAccRandomDomainName creates a random second level domain name in the form +// ".test" +// The top level domain ".test" is reserved by IANA for testing purposes: +// https://datatracker.ietf.org/doc/html/rfc6761 +func testAccRandomDomainName() string { + return fmt.Sprintf("%s.%s", acctest.RandString(8), testingTopLevelDomain) +} diff --git a/aws/resource_aws_glue_connection_test.go b/aws/resource_aws_glue_connection_test.go index 8c25778feac..e099b273cbe 100644 --- a/aws/resource_aws_glue_connection_test.go +++ b/aws/resource_aws_glue_connection_test.go @@ -60,9 +60,11 @@ func testSweepGlueConnections(region string) error { func TestAccAWSGlueConnection_basic(t *testing.T) { var connection glue.Connection - rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(5)) + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_glue_connection.test" + jdbcConnectionUrl := fmt.Sprintf("jdbc:mysql://%s/testdatabase", testAccRandomDomainName()) + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, glue.EndpointsID), @@ -70,12 +72,12 @@ func TestAccAWSGlueConnection_basic(t *testing.T) { CheckDestroy: testAccCheckAWSGlueConnectionDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSGlueConnectionConfig_Required(rName), + Config: testAccAWSGlueConnectionConfig_Required(rName, jdbcConnectionUrl), Check: resource.ComposeTestCheckFunc( testAccCheckAWSGlueConnectionExists(resourceName, &connection), testAccCheckResourceAttrRegionalARN(resourceName, "arn", "glue", fmt.Sprintf("connection/%s", rName)), resource.TestCheckResourceAttr(resourceName, "connection_properties.%", "3"), - resource.TestCheckResourceAttr(resourceName, "connection_properties.JDBC_CONNECTION_URL", "jdbc:mysql://terraformacctesting.com/testdatabase"), + resource.TestCheckResourceAttr(resourceName, "connection_properties.JDBC_CONNECTION_URL", jdbcConnectionUrl), resource.TestCheckResourceAttr(resourceName, "connection_properties.PASSWORD", "testpassword"), resource.TestCheckResourceAttr(resourceName, "connection_properties.USERNAME", "testusername"), resource.TestCheckResourceAttr(resourceName, "match_criteria.#", "0"), @@ -94,9 +96,11 @@ func TestAccAWSGlueConnection_basic(t *testing.T) { func TestAccAWSGlueConnection_MongoDB(t *testing.T) { var connection glue.Connection - rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(5)) + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_glue_connection.test" + connectionUrl := fmt.Sprintf("mongodb://%s:27017/testdatabase", testAccRandomDomainName()) + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, glue.EndpointsID), @@ -104,11 +108,11 @@ func TestAccAWSGlueConnection_MongoDB(t *testing.T) { CheckDestroy: testAccCheckAWSGlueConnectionDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSGlueConnectionConfig_MongoDB(rName), + Config: testAccAWSGlueConnectionConfig_MongoDB(rName, connectionUrl), Check: resource.ComposeTestCheckFunc( testAccCheckAWSGlueConnectionExists(resourceName, &connection), resource.TestCheckResourceAttr(resourceName, "connection_properties.%", "3"), - resource.TestCheckResourceAttr(resourceName, "connection_properties.CONNECTION_URL", "mongodb://testdb.com:27017/databasename"), + resource.TestCheckResourceAttr(resourceName, "connection_properties.CONNECTION_URL", connectionUrl), resource.TestCheckResourceAttr(resourceName, "connection_properties.USERNAME", "testusername"), resource.TestCheckResourceAttr(resourceName, "connection_properties.PASSWORD", "testpassword"), resource.TestCheckResourceAttr(resourceName, "connection_type", "MONGODB"), @@ -128,9 +132,11 @@ func TestAccAWSGlueConnection_MongoDB(t *testing.T) { func TestAccAWSGlueConnection_Kafka(t *testing.T) { var connection glue.Connection - rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(5)) + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_glue_connection.test" + bootstrapServers := fmt.Sprintf("%s:9094,%s:9094", testAccRandomDomainName(), testAccRandomDomainName()) + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, glue.EndpointsID), @@ -138,11 +144,11 @@ func TestAccAWSGlueConnection_Kafka(t *testing.T) { CheckDestroy: testAccCheckAWSGlueConnectionDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSGlueConnectionConfig_Kafka(rName), + Config: testAccAWSGlueConnectionConfig_Kafka(rName, bootstrapServers), Check: resource.ComposeTestCheckFunc( testAccCheckAWSGlueConnectionExists(resourceName, &connection), resource.TestCheckResourceAttr(resourceName, "connection_properties.%", "1"), - resource.TestCheckResourceAttr(resourceName, "connection_properties.KAFKA_BOOTSTRAP_SERVERS", "a.terraformtest.com:9094,b.terraformtest.com:9094"), + resource.TestCheckResourceAttr(resourceName, "connection_properties.KAFKA_BOOTSTRAP_SERVERS", bootstrapServers), resource.TestCheckResourceAttr(resourceName, "connection_type", "KAFKA"), resource.TestCheckResourceAttr(resourceName, "match_criteria.#", "0"), resource.TestCheckResourceAttr(resourceName, "physical_connection_requirements.#", "0"), @@ -160,7 +166,7 @@ func TestAccAWSGlueConnection_Kafka(t *testing.T) { func TestAccAWSGlueConnection_Network(t *testing.T) { var connection glue.Connection - rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(5)) + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_glue_connection.test" resource.ParallelTest(t, resource.TestCase{ @@ -194,9 +200,11 @@ func TestAccAWSGlueConnection_Network(t *testing.T) { func TestAccAWSGlueConnection_Description(t *testing.T) { var connection glue.Connection - rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(5)) + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_glue_connection.test" + jdbcConnectionUrl := fmt.Sprintf("jdbc:mysql://%s/testdatabase", testAccRandomDomainName()) + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, glue.EndpointsID), @@ -204,14 +212,14 @@ func TestAccAWSGlueConnection_Description(t *testing.T) { CheckDestroy: testAccCheckAWSGlueConnectionDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSGlueConnectionConfig_Description(rName, "First Description"), + Config: testAccAWSGlueConnectionConfig_Description(rName, jdbcConnectionUrl, "First Description"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSGlueConnectionExists(resourceName, &connection), resource.TestCheckResourceAttr(resourceName, "description", "First Description"), ), }, { - Config: testAccAWSGlueConnectionConfig_Description(rName, "Second Description"), + Config: testAccAWSGlueConnectionConfig_Description(rName, jdbcConnectionUrl, "Second Description"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSGlueConnectionExists(resourceName, &connection), resource.TestCheckResourceAttr(resourceName, "description", "Second Description"), @@ -229,9 +237,11 @@ func TestAccAWSGlueConnection_Description(t *testing.T) { func TestAccAWSGlueConnection_MatchCriteria(t *testing.T) { var connection glue.Connection - rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(5)) + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_glue_connection.test" + jdbcConnectionUrl := fmt.Sprintf("jdbc:mysql://%s/testdatabase", testAccRandomDomainName()) + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, glue.EndpointsID), @@ -239,7 +249,7 @@ func TestAccAWSGlueConnection_MatchCriteria(t *testing.T) { CheckDestroy: testAccCheckAWSGlueConnectionDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSGlueConnectionConfig_MatchCriteria_First(rName), + Config: testAccAWSGlueConnectionConfig_MatchCriteria_First(rName, jdbcConnectionUrl), Check: resource.ComposeTestCheckFunc( testAccCheckAWSGlueConnectionExists(resourceName, &connection), resource.TestCheckResourceAttr(resourceName, "match_criteria.#", "4"), @@ -250,7 +260,7 @@ func TestAccAWSGlueConnection_MatchCriteria(t *testing.T) { ), }, { - Config: testAccAWSGlueConnectionConfig_MatchCriteria_Second(rName), + Config: testAccAWSGlueConnectionConfig_MatchCriteria_Second(rName, jdbcConnectionUrl), Check: resource.ComposeTestCheckFunc( testAccCheckAWSGlueConnectionExists(resourceName, &connection), resource.TestCheckResourceAttr(resourceName, "match_criteria.#", "1"), @@ -258,7 +268,7 @@ func TestAccAWSGlueConnection_MatchCriteria(t *testing.T) { ), }, { - Config: testAccAWSGlueConnectionConfig_MatchCriteria_Third(rName), + Config: testAccAWSGlueConnectionConfig_MatchCriteria_Third(rName, jdbcConnectionUrl), Check: resource.ComposeTestCheckFunc( testAccCheckAWSGlueConnectionExists(resourceName, &connection), resource.TestCheckResourceAttr(resourceName, "match_criteria.#", "3"), @@ -279,7 +289,7 @@ func TestAccAWSGlueConnection_MatchCriteria(t *testing.T) { func TestAccAWSGlueConnection_PhysicalConnectionRequirements(t *testing.T) { var connection glue.Connection - rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(5)) + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_glue_connection.test" resource.ParallelTest(t, resource.TestCase{ @@ -316,9 +326,11 @@ func TestAccAWSGlueConnection_PhysicalConnectionRequirements(t *testing.T) { func TestAccAWSGlueConnection_disappears(t *testing.T) { var connection glue.Connection - rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(5)) + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_glue_connection.test" + jdbcConnectionUrl := fmt.Sprintf("jdbc:mysql://%s/testdatabase", testAccRandomDomainName()) + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, glue.EndpointsID), @@ -326,7 +338,7 @@ func TestAccAWSGlueConnection_disappears(t *testing.T) { CheckDestroy: testAccCheckAWSGlueConnectionDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSGlueConnectionConfig_Required(rName), + Config: testAccAWSGlueConnectionConfig_Required(rName, jdbcConnectionUrl), Check: resource.ComposeTestCheckFunc( testAccCheckAWSGlueConnectionExists(resourceName, &connection), testAccCheckResourceDisappears(testAccProvider, resourceAwsGlueConnection(), resourceName), @@ -410,64 +422,68 @@ func testAccCheckAWSGlueConnectionDestroy(s *terraform.State) error { return nil } -func testAccAWSGlueConnectionConfig_Description(rName, description string) string { +func testAccAWSGlueConnectionConfig_Description(rName, jdbcConnectionUrl, description string) string { return fmt.Sprintf(` resource "aws_glue_connection" "test" { + name = %[1]q + description = %[2]q + connection_properties = { - JDBC_CONNECTION_URL = "jdbc:mysql://terraformacctesting.com/testdatabase" + JDBC_CONNECTION_URL = %[3]q PASSWORD = "testpassword" USERNAME = "testusername" } - description = "%[1]s" - name = "%[2]s" } -`, description, rName) +`, rName, description, jdbcConnectionUrl) } -func testAccAWSGlueConnectionConfig_MatchCriteria_First(rName string) string { +func testAccAWSGlueConnectionConfig_MatchCriteria_First(rName, jdbcConnectionUrl string) string { return fmt.Sprintf(` resource "aws_glue_connection" "test" { + name = %[1]q + + match_criteria = ["criteria1", "criteria2", "criteria3", "criteria4"] + connection_properties = { - JDBC_CONNECTION_URL = "jdbc:mysql://terraformacctesting.com/testdatabase" + JDBC_CONNECTION_URL = %[2]q PASSWORD = "testpassword" USERNAME = "testusername" } - - match_criteria = ["criteria1", "criteria2", "criteria3", "criteria4"] - name = "%s" } -`, rName) +`, rName, jdbcConnectionUrl) } -func testAccAWSGlueConnectionConfig_MatchCriteria_Second(rName string) string { +func testAccAWSGlueConnectionConfig_MatchCriteria_Second(rName, jdbcConnectionUrl string) string { return fmt.Sprintf(` resource "aws_glue_connection" "test" { + name = %[1]q + + match_criteria = ["criteria1"] + connection_properties = { - JDBC_CONNECTION_URL = "jdbc:mysql://terraformacctesting.com/testdatabase" + JDBC_CONNECTION_URL = %[2]q PASSWORD = "testpassword" USERNAME = "testusername" } - - match_criteria = ["criteria1"] - name = "%s" } -`, rName) +`, rName, jdbcConnectionUrl) } -func testAccAWSGlueConnectionConfig_MatchCriteria_Third(rName string) string { +func testAccAWSGlueConnectionConfig_MatchCriteria_Third(rName, jdbcConnectionUrl string) string { return fmt.Sprintf(` resource "aws_glue_connection" "test" { + name = "%s" + + match_criteria = ["criteria2", "criteria3", "criteria4"] + connection_properties = { - JDBC_CONNECTION_URL = "jdbc:mysql://terraformacctesting.com/testdatabase" + JDBC_CONNECTION_URL = %[2]q PASSWORD = "testpassword" USERNAME = "testusername" } - - match_criteria = ["criteria2", "criteria3", "criteria4"] - name = "%s" } -`, rName) +`, rName, jdbcConnectionUrl) } func testAccAWSGlueConnectionConfig_PhysicalConnectionRequirements(rName string) string { @@ -555,48 +571,46 @@ resource "aws_glue_connection" "test" { `, rName) } -func testAccAWSGlueConnectionConfig_Required(rName string) string { +func testAccAWSGlueConnectionConfig_Required(rName, jdbcConnectionUrl string) string { return fmt.Sprintf(` resource "aws_glue_connection" "test" { + name = %[1]q + connection_properties = { - JDBC_CONNECTION_URL = "jdbc:mysql://terraformacctesting.com/testdatabase" + JDBC_CONNECTION_URL = %[2]q PASSWORD = "testpassword" USERNAME = "testusername" } - - name = "%s" } -`, rName) +`, rName, jdbcConnectionUrl) } -func testAccAWSGlueConnectionConfig_MongoDB(rName string) string { +func testAccAWSGlueConnectionConfig_MongoDB(rName, connectionUrl string) string { return fmt.Sprintf(` resource "aws_glue_connection" "test" { + name = %[1]q + + connection_type = "MONGODB" connection_properties = { - CONNECTION_URL = "mongodb://testdb.com:27017/databasename" + CONNECTION_URL = %[2]q PASSWORD = "testpassword" USERNAME = "testusername" } - - connection_type = "MONGODB" - - name = "%s" } -`, rName) +`, rName, connectionUrl) } -func testAccAWSGlueConnectionConfig_Kafka(rName string) string { +func testAccAWSGlueConnectionConfig_Kafka(rName, bootstrapServers string) string { return fmt.Sprintf(` resource "aws_glue_connection" "test" { - connection_properties = { - KAFKA_BOOTSTRAP_SERVERS = "a.terraformtest.com:9094,b.terraformtest.com:9094" - } + name = %[1]q connection_type = "KAFKA" - - name = "%s" + connection_properties = { + KAFKA_BOOTSTRAP_SERVERS = %[2]q + } } -`, rName) +`, rName, bootstrapServers) } func testAccAWSGlueConnectionConfig_Network(rName string) string { From cf99d59c52355ba77cba93d53fcd10b903499bb7 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Mon, 28 Jun 2021 13:02:46 -0700 Subject: [PATCH 255/398] Removes hard-coded domain names from Route 53 resources --- aws/provider_test.go | 16 +++--- ...source_aws_api_gateway_domain_name_test.go | 6 ++- ...esource_aws_route53_delegation_set_test.go | 20 +++---- ...rce_aws_route53_hosted_zone_dnssec_test.go | 31 ++++++----- ...source_aws_route53_key_signing_key_test.go | 30 ++++++----- aws/resource_aws_route53_zone_test.go | 54 +++++++++---------- 6 files changed, 83 insertions(+), 74 deletions(-) diff --git a/aws/provider_test.go b/aws/provider_test.go index dd41f9e39fe..a9dcaccf772 100644 --- a/aws/provider_test.go +++ b/aws/provider_test.go @@ -2285,16 +2285,14 @@ func composeConfig(config ...string) string { return str.String() } -const defaultRootLevelDomain = "example.com" -const defaultRootLevelDomainWildcard = "*." + defaultRootLevelDomain const testingTopLevelDomain = "test" -// testAccRandomSubdomain creates a random three-level domain name of the form -// ".example.com" -// The second level domain name "example.com" is reserved by IANA for testing and -// documentation purposes: https://datatracker.ietf.org/doc/html/rfc2606 +// testAccRandomSubdomain creates a random three-level domain name in the form +// "..test" +// The top level domain ".test" is reserved by IANA for testing purposes: +// https://datatracker.ietf.org/doc/html/rfc6761 func testAccRandomSubdomain() string { - return fmt.Sprintf("%s.%s", acctest.RandString(8), defaultRootLevelDomain) + return testAccSubdomainWithRandomSecondLevel(acctest.RandString(8)) } // testAccRandomDomainName creates a random second level domain name in the form @@ -2304,3 +2302,7 @@ func testAccRandomSubdomain() string { func testAccRandomDomainName() string { return fmt.Sprintf("%s.%s", acctest.RandString(8), testingTopLevelDomain) } + +func testAccSubdomainWithRandomSecondLevel(thing string) string { + return fmt.Sprintf("%s.%s", thing, testAccRandomDomainName()) +} diff --git a/aws/resource_aws_api_gateway_domain_name_test.go b/aws/resource_aws_api_gateway_domain_name_test.go index 523329aeda7..3e74b4925f2 100644 --- a/aws/resource_aws_api_gateway_domain_name_test.go +++ b/aws/resource_aws_api_gateway_domain_name_test.go @@ -157,12 +157,14 @@ func TestAccAWSAPIGatewayDomainName_RegionalCertificateName(t *testing.T) { var domainName apigateway.DomainName resourceName := "aws_api_gateway_domain_name.test" - rName := testAccRandomSubdomain() + domain := testAccRandomDomainName() + domainWildcard := fmt.Sprintf("*.%s", domain) + rName := fmt.Sprintf("%s.%s", acctest.RandString(8), domain) caKey := tlsRsaPrivateKeyPem(2048) caCertificate := tlsRsaX509SelfSignedCaCertificatePem(caKey) key := tlsRsaPrivateKeyPem(2048) - certificate := tlsRsaX509LocallySignedCertificatePem(caKey, caCertificate, key, defaultRootLevelDomainWildcard) + certificate := tlsRsaX509LocallySignedCertificatePem(caKey, caCertificate, key, domainWildcard) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, diff --git a/aws/resource_aws_route53_delegation_set_test.go b/aws/resource_aws_route53_delegation_set_test.go index 542f1a8f637..f29310b795c 100644 --- a/aws/resource_aws_route53_delegation_set_test.go +++ b/aws/resource_aws_route53_delegation_set_test.go @@ -13,8 +13,7 @@ import ( ) func TestAccAWSRoute53DelegationSet_basic(t *testing.T) { - rString := acctest.RandString(8) - refName := fmt.Sprintf("tf_acc_%s", rString) + refName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_route53_delegation_set.test" resource.ParallelTest(t, resource.TestCase{ @@ -42,11 +41,12 @@ func TestAccAWSRoute53DelegationSet_basic(t *testing.T) { func TestAccAWSRoute53DelegationSet_withZones(t *testing.T) { var zone route53.GetHostedZoneOutput - rString := acctest.RandString(8) - refName := fmt.Sprintf("tf_acc_%s", rString) + refName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_route53_delegation_set.test" - zoneName1 := fmt.Sprintf("%s-primary.terraformtest.com", rString) - zoneName2 := fmt.Sprintf("%s-secondary.terraformtest.com", rString) + + domain := testAccRandomDomainName() + zoneName1 := fmt.Sprintf("primary.%s", domain) + zoneName2 := fmt.Sprintf("secondary.%s", domain) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -156,7 +156,7 @@ func testAccCheckRoute53NameServersMatch(delegationSetName, zoneName string) res func testAccRoute53DelegationSetConfig(refName string) string { return fmt.Sprintf(` resource "aws_route53_delegation_set" "test" { - reference_name = "%s" + reference_name = %[1]q } `, refName) } @@ -164,16 +164,16 @@ resource "aws_route53_delegation_set" "test" { func testAccRoute53DelegationSetWithZonesConfig(refName, zoneName1, zoneName2 string) string { return fmt.Sprintf(` resource "aws_route53_delegation_set" "test" { - reference_name = "%s" + reference_name = %[1]q } resource "aws_route53_zone" "primary" { - name = "%s" + name = %[2]q delegation_set_id = aws_route53_delegation_set.test.id } resource "aws_route53_zone" "secondary" { - name = "%s" + name = %[3]q delegation_set_id = aws_route53_delegation_set.test.id } `, refName, zoneName1, zoneName2) diff --git a/aws/resource_aws_route53_hosted_zone_dnssec_test.go b/aws/resource_aws_route53_hosted_zone_dnssec_test.go index 98864244204..a381e3ff39e 100644 --- a/aws/resource_aws_route53_hosted_zone_dnssec_test.go +++ b/aws/resource_aws_route53_hosted_zone_dnssec_test.go @@ -19,6 +19,8 @@ func TestAccAwsRoute53HostedZoneDnssec_basic(t *testing.T) { resourceName := "aws_route53_hosted_zone_dnssec.test" rName := acctest.RandomWithPrefix("tf-acc-test") + domainName := testAccRandomDomainName() + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckRoute53KeySigningKey(t) }, ErrorCheck: testAccErrorCheck(t, route53.EndpointsID), @@ -26,7 +28,7 @@ func TestAccAwsRoute53HostedZoneDnssec_basic(t *testing.T) { CheckDestroy: testAccCheckAwsRoute53HostedZoneDnssecDestroy, Steps: []resource.TestStep{ { - Config: testAccAwsRoute53HostedZoneDnssecConfig(rName), + Config: testAccAwsRoute53HostedZoneDnssecConfig(rName, domainName), Check: resource.ComposeAggregateTestCheckFunc( testAccAwsRoute53HostedZoneDnssecExists(resourceName), resource.TestCheckResourceAttrPair(resourceName, "hosted_zone_id", route53ZoneResourceName, "id"), @@ -46,6 +48,8 @@ func TestAccAwsRoute53HostedZoneDnssec_disappears(t *testing.T) { resourceName := "aws_route53_hosted_zone_dnssec.test" rName := acctest.RandomWithPrefix("tf-acc-test") + domainName := testAccRandomDomainName() + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckRoute53KeySigningKey(t) }, ErrorCheck: testAccErrorCheck(t, route53.EndpointsID), @@ -53,7 +57,7 @@ func TestAccAwsRoute53HostedZoneDnssec_disappears(t *testing.T) { CheckDestroy: testAccCheckAwsRoute53HostedZoneDnssecDestroy, Steps: []resource.TestStep{ { - Config: testAccAwsRoute53HostedZoneDnssecConfig(rName), + Config: testAccAwsRoute53HostedZoneDnssecConfig(rName, domainName), Check: resource.ComposeAggregateTestCheckFunc( testAccAwsRoute53HostedZoneDnssecExists(resourceName), testAccCheckResourceDisappears(testAccProvider, resourceAwsRoute53HostedZoneDnssec(), resourceName), @@ -68,6 +72,8 @@ func TestAccAwsRoute53HostedZoneDnssec_SigningStatus(t *testing.T) { resourceName := "aws_route53_hosted_zone_dnssec.test" rName := acctest.RandomWithPrefix("tf-acc-test") + domainName := testAccRandomDomainName() + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckRoute53KeySigningKey(t) }, ErrorCheck: testAccErrorCheck(t, route53.EndpointsID), @@ -75,7 +81,7 @@ func TestAccAwsRoute53HostedZoneDnssec_SigningStatus(t *testing.T) { CheckDestroy: testAccCheckAwsRoute53HostedZoneDnssecDestroy, Steps: []resource.TestStep{ { - Config: testAccAwsRoute53HostedZoneDnssecConfig_SigningStatus(rName, tfroute53.ServeSignatureNotSigning), + Config: testAccAwsRoute53HostedZoneDnssecConfig_SigningStatus(rName, domainName, tfroute53.ServeSignatureNotSigning), Check: resource.ComposeAggregateTestCheckFunc( testAccAwsRoute53HostedZoneDnssecExists(resourceName), resource.TestCheckResourceAttr(resourceName, "signing_status", tfroute53.ServeSignatureNotSigning), @@ -87,14 +93,14 @@ func TestAccAwsRoute53HostedZoneDnssec_SigningStatus(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccAwsRoute53HostedZoneDnssecConfig_SigningStatus(rName, tfroute53.ServeSignatureSigning), + Config: testAccAwsRoute53HostedZoneDnssecConfig_SigningStatus(rName, domainName, tfroute53.ServeSignatureSigning), Check: resource.ComposeAggregateTestCheckFunc( testAccAwsRoute53HostedZoneDnssecExists(resourceName), resource.TestCheckResourceAttr(resourceName, "signing_status", tfroute53.ServeSignatureSigning), ), }, { - Config: testAccAwsRoute53HostedZoneDnssecConfig_SigningStatus(rName, tfroute53.ServeSignatureNotSigning), + Config: testAccAwsRoute53HostedZoneDnssecConfig_SigningStatus(rName, domainName, tfroute53.ServeSignatureNotSigning), Check: resource.ComposeAggregateTestCheckFunc( testAccAwsRoute53HostedZoneDnssecExists(resourceName), resource.TestCheckResourceAttr(resourceName, "signing_status", tfroute53.ServeSignatureNotSigning), @@ -162,7 +168,7 @@ func testAccAwsRoute53HostedZoneDnssecExists(resourceName string) resource.TestC } } -func testAccAwsRoute53HostedZoneDnssecConfig_Base(rName string) string { +func testAccAwsRoute53HostedZoneDnssecConfig_Base(rName, domainName string) string { return composeConfig( testAccRoute53KeySigningKeyRegionProviderConfig(), fmt.Sprintf(` @@ -199,7 +205,7 @@ resource "aws_kms_key" "test" { } resource "aws_route53_zone" "test" { - name = "%[1]s.terraformtest.com" + name = %[2]q } resource "aws_route53_key_signing_key" "test" { @@ -207,22 +213,21 @@ resource "aws_route53_key_signing_key" "test" { key_management_service_arn = aws_kms_key.test.arn name = %[1]q } -`, rName)) +`, rName, domainName)) } -func testAccAwsRoute53HostedZoneDnssecConfig(rName string) string { +func testAccAwsRoute53HostedZoneDnssecConfig(rName, domainName string) string { return composeConfig( - testAccAwsRoute53HostedZoneDnssecConfig_Base(rName), - ` + testAccAwsRoute53HostedZoneDnssecConfig_Base(rName, domainName), ` resource "aws_route53_hosted_zone_dnssec" "test" { hosted_zone_id = aws_route53_key_signing_key.test.hosted_zone_id } `) } -func testAccAwsRoute53HostedZoneDnssecConfig_SigningStatus(rName string, signingStatus string) string { +func testAccAwsRoute53HostedZoneDnssecConfig_SigningStatus(rName, domainName, signingStatus string) string { return composeConfig( - testAccAwsRoute53HostedZoneDnssecConfig_Base(rName), + testAccAwsRoute53HostedZoneDnssecConfig_Base(rName, domainName), fmt.Sprintf(` resource "aws_route53_hosted_zone_dnssec" "test" { hosted_zone_id = aws_route53_key_signing_key.test.hosted_zone_id diff --git a/aws/resource_aws_route53_key_signing_key_test.go b/aws/resource_aws_route53_key_signing_key_test.go index 4da3e733178..0d1e122047b 100644 --- a/aws/resource_aws_route53_key_signing_key_test.go +++ b/aws/resource_aws_route53_key_signing_key_test.go @@ -113,6 +113,8 @@ func TestAccAwsRoute53KeySigningKey_basic(t *testing.T) { resourceName := "aws_route53_key_signing_key.test" rName := acctest.RandomWithPrefix("tf-acc-test") + domainName := testAccRandomDomainName() + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckRoute53KeySigningKey(t) }, ErrorCheck: testAccErrorCheck(t, route53.EndpointsID), @@ -120,7 +122,7 @@ func TestAccAwsRoute53KeySigningKey_basic(t *testing.T) { CheckDestroy: testAccCheckAwsRoute53KeySigningKeyDestroy, Steps: []resource.TestStep{ { - Config: testAccAwsRoute53KeySigningKeyConfig_Name(rName), + Config: testAccAwsRoute53KeySigningKeyConfig_Name(rName, domainName), Check: resource.ComposeAggregateTestCheckFunc( testAccAwsRoute53KeySigningKeyExists(resourceName), resource.TestCheckResourceAttr(resourceName, "digest_algorithm_mnemonic", "SHA-256"), @@ -152,6 +154,8 @@ func TestAccAwsRoute53KeySigningKey_disappears(t *testing.T) { resourceName := "aws_route53_key_signing_key.test" rName := acctest.RandomWithPrefix("tf-acc-test") + domainName := testAccRandomDomainName() + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckRoute53KeySigningKey(t) }, ErrorCheck: testAccErrorCheck(t, route53.EndpointsID), @@ -159,7 +163,7 @@ func TestAccAwsRoute53KeySigningKey_disappears(t *testing.T) { CheckDestroy: testAccCheckAwsRoute53KeySigningKeyDestroy, Steps: []resource.TestStep{ { - Config: testAccAwsRoute53KeySigningKeyConfig_Name(rName), + Config: testAccAwsRoute53KeySigningKeyConfig_Name(rName, domainName), Check: resource.ComposeAggregateTestCheckFunc( testAccAwsRoute53KeySigningKeyExists(resourceName), testAccCheckResourceDisappears(testAccProvider, resourceAwsRoute53KeySigningKey(), resourceName), @@ -174,6 +178,8 @@ func TestAccAwsRoute53KeySigningKey_Status(t *testing.T) { resourceName := "aws_route53_key_signing_key.test" rName := acctest.RandomWithPrefix("tf-acc-test") + domainName := testAccRandomDomainName() + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckRoute53KeySigningKey(t) }, ErrorCheck: testAccErrorCheck(t, route53.EndpointsID), @@ -181,7 +187,7 @@ func TestAccAwsRoute53KeySigningKey_Status(t *testing.T) { CheckDestroy: testAccCheckAwsRoute53KeySigningKeyDestroy, Steps: []resource.TestStep{ { - Config: testAccAwsRoute53KeySigningKeyConfig_Status(rName, tfroute53.KeySigningKeyStatusInactive), + Config: testAccAwsRoute53KeySigningKeyConfig_Status(rName, domainName, tfroute53.KeySigningKeyStatusInactive), Check: resource.ComposeAggregateTestCheckFunc( testAccAwsRoute53KeySigningKeyExists(resourceName), resource.TestCheckResourceAttr(resourceName, "status", tfroute53.KeySigningKeyStatusInactive), @@ -193,14 +199,14 @@ func TestAccAwsRoute53KeySigningKey_Status(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccAwsRoute53KeySigningKeyConfig_Status(rName, tfroute53.KeySigningKeyStatusActive), + Config: testAccAwsRoute53KeySigningKeyConfig_Status(rName, domainName, tfroute53.KeySigningKeyStatusActive), Check: resource.ComposeAggregateTestCheckFunc( testAccAwsRoute53KeySigningKeyExists(resourceName), resource.TestCheckResourceAttr(resourceName, "status", tfroute53.KeySigningKeyStatusActive), ), }, { - Config: testAccAwsRoute53KeySigningKeyConfig_Status(rName, tfroute53.KeySigningKeyStatusInactive), + Config: testAccAwsRoute53KeySigningKeyConfig_Status(rName, domainName, tfroute53.KeySigningKeyStatusInactive), Check: resource.ComposeAggregateTestCheckFunc( testAccAwsRoute53KeySigningKeyExists(resourceName), resource.TestCheckResourceAttr(resourceName, "status", tfroute53.KeySigningKeyStatusInactive), @@ -268,7 +274,7 @@ func testAccAwsRoute53KeySigningKeyExists(resourceName string) resource.TestChec } } -func testAccAwsRoute53KeySigningKeyConfig_Base(rName string) string { +func testAccAwsRoute53KeySigningKeyConfig_Base(rName, domainName string) string { return composeConfig( testAccRoute53KeySigningKeyRegionProviderConfig(), fmt.Sprintf(` @@ -305,14 +311,14 @@ resource "aws_kms_key" "test" { } resource "aws_route53_zone" "test" { - name = "%[1]s.terraformtest.com" + name = %[2]q } -`, rName)) +`, rName, domainName)) } -func testAccAwsRoute53KeySigningKeyConfig_Name(rName string) string { +func testAccAwsRoute53KeySigningKeyConfig_Name(rName, domainName string) string { return composeConfig( - testAccAwsRoute53KeySigningKeyConfig_Base(rName), + testAccAwsRoute53KeySigningKeyConfig_Base(rName, domainName), fmt.Sprintf(` resource "aws_route53_key_signing_key" "test" { hosted_zone_id = aws_route53_zone.test.id @@ -322,9 +328,9 @@ resource "aws_route53_key_signing_key" "test" { `, rName)) } -func testAccAwsRoute53KeySigningKeyConfig_Status(rName string, status string) string { +func testAccAwsRoute53KeySigningKeyConfig_Status(rName, domainName, status string) string { return composeConfig( - testAccAwsRoute53KeySigningKeyConfig_Base(rName), + testAccAwsRoute53KeySigningKeyConfig_Base(rName, domainName), fmt.Sprintf(` resource "aws_route53_key_signing_key" "test" { hosted_zone_id = aws_route53_zone.test.id diff --git a/aws/resource_aws_route53_zone_test.go b/aws/resource_aws_route53_zone_test.go index 8e39e13afb7..f19b8275a85 100644 --- a/aws/resource_aws_route53_zone_test.go +++ b/aws/resource_aws_route53_zone_test.go @@ -98,7 +98,6 @@ func hostedZonesToPreserve() []string { "tfacc.hashicorptest.com", "aws.tfacc.hashicorptest.com", "hashicorp.com", - "terraformtest.com", "terraform-provider-aws-acctest-acm.com", } } @@ -167,9 +166,8 @@ func testSweepRoute53Zones(region string) error { func TestAccAWSRoute53Zone_basic(t *testing.T) { var zone route53.GetHostedZoneOutput - rString := acctest.RandString(8) resourceName := "aws_route53_zone.test" - zoneName := fmt.Sprintf("%s.terraformtest.com", rString) + zoneName := testAccRandomDomainName() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -200,9 +198,8 @@ func TestAccAWSRoute53Zone_basic(t *testing.T) { func TestAccAWSRoute53Zone_disappears(t *testing.T) { var zone route53.GetHostedZoneOutput - rString := acctest.RandString(8) resourceName := "aws_route53_zone.test" - zoneName := fmt.Sprintf("%s.terraformtest.com", rString) + zoneName := testAccRandomDomainName() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -225,6 +222,8 @@ func TestAccAWSRoute53Zone_disappears(t *testing.T) { func TestAccAWSRoute53Zone_multiple(t *testing.T) { var zone0, zone1, zone2, zone3, zone4 route53.GetHostedZoneOutput + domainName := testAccRandomDomainName() + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, route53.EndpointsID), @@ -232,18 +231,18 @@ func TestAccAWSRoute53Zone_multiple(t *testing.T) { CheckDestroy: testAccCheckRoute53ZoneDestroy, Steps: []resource.TestStep{ { - Config: testAccRoute53ZoneConfigMultiple(), + Config: testAccRoute53ZoneConfigMultiple(domainName), Check: resource.ComposeTestCheckFunc( testAccCheckRoute53ZoneExists("aws_route53_zone.test.0", &zone0), - testAccCheckDomainName(&zone0, "subdomain0.terraformtest.com."), + testAccCheckDomainName(&zone0, fmt.Sprintf("subdomain0.%s.", domainName)), testAccCheckRoute53ZoneExists("aws_route53_zone.test.1", &zone1), - testAccCheckDomainName(&zone1, "subdomain1.terraformtest.com."), + testAccCheckDomainName(&zone1, fmt.Sprintf("subdomain1.%s.", domainName)), testAccCheckRoute53ZoneExists("aws_route53_zone.test.2", &zone2), - testAccCheckDomainName(&zone2, "subdomain2.terraformtest.com."), + testAccCheckDomainName(&zone2, fmt.Sprintf("subdomain2.%s.", domainName)), testAccCheckRoute53ZoneExists("aws_route53_zone.test.3", &zone3), - testAccCheckDomainName(&zone3, "subdomain3.terraformtest.com."), + testAccCheckDomainName(&zone3, fmt.Sprintf("subdomain3.%s.", domainName)), testAccCheckRoute53ZoneExists("aws_route53_zone.test.4", &zone4), - testAccCheckDomainName(&zone4, "subdomain4.terraformtest.com."), + testAccCheckDomainName(&zone4, fmt.Sprintf("subdomain4.%s.", domainName)), ), }, }, @@ -253,9 +252,8 @@ func TestAccAWSRoute53Zone_multiple(t *testing.T) { func TestAccAWSRoute53Zone_Comment(t *testing.T) { var zone route53.GetHostedZoneOutput - rString := acctest.RandString(8) resourceName := "aws_route53_zone.test" - zoneName := fmt.Sprintf("%s.terraformtest.com", rString) + zoneName := testAccRandomDomainName() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -290,10 +288,9 @@ func TestAccAWSRoute53Zone_Comment(t *testing.T) { func TestAccAWSRoute53Zone_DelegationSetID(t *testing.T) { var zone route53.GetHostedZoneOutput - rString := acctest.RandString(8) delegationSetResourceName := "aws_route53_delegation_set.test" resourceName := "aws_route53_zone.test" - zoneName := fmt.Sprintf("%s.terraformtest.com", rString) + zoneName := testAccRandomDomainName() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -321,9 +318,8 @@ func TestAccAWSRoute53Zone_DelegationSetID(t *testing.T) { func TestAccAWSRoute53Zone_ForceDestroy(t *testing.T) { var zone route53.GetHostedZoneOutput - rString := acctest.RandString(8) resourceName := "aws_route53_zone.test" - zoneName := fmt.Sprintf("%s.terraformtest.com", rString) + zoneName := testAccRandomDomainName() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -347,9 +343,8 @@ func TestAccAWSRoute53Zone_ForceDestroy(t *testing.T) { func TestAccAWSRoute53Zone_ForceDestroy_TrailingPeriod(t *testing.T) { var zone route53.GetHostedZoneOutput - rString := acctest.RandString(8) resourceName := "aws_route53_zone.test" - zoneName := fmt.Sprintf("%s.terraformtest.com", rString) + zoneName := testAccRandomDomainName() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -373,9 +368,8 @@ func TestAccAWSRoute53Zone_ForceDestroy_TrailingPeriod(t *testing.T) { func TestAccAWSRoute53Zone_Tags(t *testing.T) { var zone route53.GetHostedZoneOutput - rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_route53_zone.test" - zoneName := fmt.Sprintf("%s.terraformtest.com", rName) + zoneName := testAccRandomDomainName() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -424,7 +418,7 @@ func TestAccAWSRoute53Zone_VPC_Single(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_route53_zone.test" vpcResourceName := "aws_vpc.test1" - zoneName := fmt.Sprintf("%s.terraformtest.com", rName) + zoneName := testAccRandomDomainName() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -457,7 +451,7 @@ func TestAccAWSRoute53Zone_VPC_Multiple(t *testing.T) { resourceName := "aws_route53_zone.test" vpcResourceName1 := "aws_vpc.test1" vpcResourceName2 := "aws_vpc.test2" - zoneName := fmt.Sprintf("%s.terraformtest.com", rName) + zoneName := testAccRandomDomainName() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -491,7 +485,7 @@ func TestAccAWSRoute53Zone_VPC_Updates(t *testing.T) { resourceName := "aws_route53_zone.test" vpcResourceName1 := "aws_vpc.test1" vpcResourceName2 := "aws_vpc.test2" - zoneName := fmt.Sprintf("%s.terraformtest.com", rName) + zoneName := testAccRandomDomainName() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -696,14 +690,14 @@ resource "aws_route53_zone" "test" { `, zoneName) } -func testAccRoute53ZoneConfigMultiple() string { - return ` +func testAccRoute53ZoneConfigMultiple(domainName string) string { + return fmt.Sprintf(` resource "aws_route53_zone" "test" { count = 5 - name = "subdomain${count.index}.terraformtest.com" + name = "subdomain${count.index}.%[1]s" } -` +`, domainName) } func testAccRoute53ZoneConfigComment(zoneName, comment string) string { @@ -775,12 +769,12 @@ resource "aws_vpc" "test1" { cidr_block = "10.1.0.0/16" tags = { - Name = %q + Name = %[1]q } } resource "aws_route53_zone" "test" { - name = "%s." + name = "%[2]s." vpc { vpc_id = aws_vpc.test1.id From c7922a300f243dc402281754ab7afd5bbfb3fbd7 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Mon, 28 Jun 2021 15:19:09 -0700 Subject: [PATCH 256/398] Removes more domain names from API Gateway acceptance tests --- aws/resource_aws_api_gateway_rest_api_test.go | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/aws/resource_aws_api_gateway_rest_api_test.go b/aws/resource_aws_api_gateway_rest_api_test.go index 7c788fbaafa..fba3ed46a5c 100644 --- a/aws/resource_aws_api_gateway_rest_api_test.go +++ b/aws/resource_aws_api_gateway_rest_api_test.go @@ -1377,7 +1377,7 @@ resource "aws_api_gateway_rest_api" "test" { statusCode = 200 } } - uri = "https://aws.amazon.com/" + uri = "https://api.example.com/" } } } @@ -1418,7 +1418,7 @@ resource "aws_api_gateway_rest_api" "test" { statusCode = 200 } } - uri = "https://aws.amazon.com/" + uri = "https://api.example.com/" } } } @@ -1620,7 +1620,7 @@ resource "aws_api_gateway_rest_api" "test" { statusCode = 200 } } - uri = "https://aws.amazon.com/" + uri = "https://api.example.com/" } } } @@ -1702,7 +1702,7 @@ resource "aws_api_gateway_rest_api" "test" { statusCode = 200 } } - uri = "https://aws.amazon.com/" + uri = "https://api.example.com/" } } } @@ -1829,7 +1829,7 @@ resource "aws_api_gateway_rest_api" "test" { statusCode = 200 } } - uri = "https://aws.amazon.com/" + uri = "https://api.example.com/" } } } @@ -1868,7 +1868,7 @@ resource "aws_api_gateway_rest_api" "test" { statusCode = 200 } } - uri = "https://aws.amazon.com/" + uri = "https://api.example.com/" } } } @@ -1917,7 +1917,7 @@ resource "aws_api_gateway_rest_api" "test" { statusCode = 200 } } - uri = "https://aws.amazon.com/" + uri = "https://api.example.com/" } } } @@ -1956,7 +1956,7 @@ resource "aws_api_gateway_rest_api" "test" { statusCode = 200 } } - uri = "https://aws.amazon.com/" + uri = "https://api.example.com/" } } } @@ -1995,7 +1995,7 @@ resource "aws_api_gateway_rest_api" "test" { statusCode = 200 } } - uri = "https://aws.amazon.com/" + uri = "https://api.example.com/" } } } @@ -2044,7 +2044,7 @@ resource "aws_api_gateway_rest_api" "test" { statusCode = 200 } } - uri = "https://aws.amazon.com/" + uri = "https://api.example.com/" } } } @@ -2083,7 +2083,7 @@ resource "aws_api_gateway_rest_api" "test" { statusCode = 200 } } - uri = "https://aws.amazon.com/" + uri = "https://api.example.com/" } } } @@ -2131,7 +2131,7 @@ resource "aws_api_gateway_rest_api" "test" { statusCode = 200 } } - uri = "https://aws.amazon.com/" + uri = "https://api.example.com/" } } } @@ -2170,7 +2170,7 @@ resource "aws_api_gateway_rest_api" "test" { statusCode = 200 } } - uri = "https://aws.amazon.com/" + uri = "https://api.example.com/" } } } @@ -2217,7 +2217,7 @@ resource "aws_api_gateway_rest_api" "test" { statusCode = 200 } } - uri = "https://aws.amazon.com/" + uri = "https://api.example.com/" } } } @@ -2256,7 +2256,7 @@ resource "aws_api_gateway_rest_api" "test" { statusCode = 200 } } - uri = "https://aws.amazon.com/" + uri = "https://api.example.com/" } } } @@ -2298,7 +2298,7 @@ resource "aws_api_gateway_rest_api" "test" { statusCode = 200 } } - uri = "https://aws.amazon.com/" + uri = "https://api.example.com/" } } } @@ -2355,7 +2355,7 @@ resource "aws_api_gateway_rest_api" "test" { statusCode = 200 } } - uri = "https://aws.amazon.com/" + uri = "https://api.example.com/" } } } From 0ad26f26d174e4e78e45d3f2b14d1c5f79c66cc5 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Mon, 28 Jun 2021 15:55:10 -0700 Subject: [PATCH 257/398] Removes more domain names from Glue acceptance tests --- aws/data_source_aws_glue_connection_test.go | 15 +- aws/resource_aws_glue_crawler_test.go | 216 +++++++++++--------- 2 files changed, 123 insertions(+), 108 deletions(-) diff --git a/aws/data_source_aws_glue_connection_test.go b/aws/data_source_aws_glue_connection_test.go index b9d99ad7bad..a62255c814c 100644 --- a/aws/data_source_aws_glue_connection_test.go +++ b/aws/data_source_aws_glue_connection_test.go @@ -13,7 +13,9 @@ import ( func TestAccDataSourceAwsGlueConnection_basic(t *testing.T) { resourceName := "aws_glue_connection.test" datasourceName := "data.aws_glue_connection.test" - rName := fmt.Sprintf("tf-testacc-glue-connection-%s", acctest.RandString(13)) + rName := acctest.RandomWithPrefix("tf-acc-test") + + jdbcConnectionUrl := fmt.Sprintf("jdbc:mysql://%s/testdatabase", testAccRandomDomainName()) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -21,7 +23,7 @@ func TestAccDataSourceAwsGlueConnection_basic(t *testing.T) { Providers: testAccProviders, Steps: []resource.TestStep{ { - Config: testAccDataSourceAwsGlueConnectionConfig(rName), + Config: testAccDataSourceAwsGlueConnectionConfig(rName, jdbcConnectionUrl), Check: resource.ComposeTestCheckFunc( testAccDataSourceAwsGlueConnectionCheck(datasourceName), resource.TestCheckResourceAttrPair(datasourceName, "catalog_id", resourceName, "catalog_id"), @@ -49,20 +51,21 @@ func testAccDataSourceAwsGlueConnectionCheck(name string) resource.TestCheckFunc } } -func testAccDataSourceAwsGlueConnectionConfig(rName string) string { +func testAccDataSourceAwsGlueConnectionConfig(rName, jdbcConnectionUrl string) string { return fmt.Sprintf(` resource "aws_glue_connection" "test" { + name = %[1]q + connection_properties = { - JDBC_CONNECTION_URL = "jdbc:mysql://terraformacctesting.com/testdatabase" + JDBC_CONNECTION_URL = %[2]q PASSWORD = "testpassword" USERNAME = "testusername" } - name = "%s" } data "aws_glue_connection" "test" { id = aws_glue_connection.test.id } -`, rName) +`, rName, jdbcConnectionUrl) } diff --git a/aws/resource_aws_glue_crawler_test.go b/aws/resource_aws_glue_crawler_test.go index bc59b615e49..2233738d4eb 100644 --- a/aws/resource_aws_glue_crawler_test.go +++ b/aws/resource_aws_glue_crawler_test.go @@ -220,6 +220,8 @@ func TestAccAWSGlueCrawler_JdbcTarget(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_glue_crawler.test" + jdbcConnectionUrl := fmt.Sprintf("jdbc:mysql://%s/testdatabase", testAccRandomDomainName()) + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, glue.EndpointsID), @@ -227,7 +229,7 @@ func TestAccAWSGlueCrawler_JdbcTarget(t *testing.T) { CheckDestroy: testAccCheckAWSGlueCrawlerDestroy, Steps: []resource.TestStep{ { - Config: testAccGlueCrawlerConfig_JdbcTarget(rName, "database-name/%"), + Config: testAccGlueCrawlerConfig_JdbcTarget(rName, jdbcConnectionUrl, "database-name/%"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSGlueCrawlerExists(resourceName, &crawler), testAccCheckResourceAttrRegionalARN(resourceName, "arn", "glue", fmt.Sprintf("crawler/%s", rName)), @@ -252,7 +254,7 @@ func TestAccAWSGlueCrawler_JdbcTarget(t *testing.T) { ), }, { - Config: testAccGlueCrawlerConfig_JdbcTarget(rName, "database-name/table-name"), + Config: testAccGlueCrawlerConfig_JdbcTarget(rName, jdbcConnectionUrl, "database-name/table-name"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSGlueCrawlerExists(resourceName, &crawler), testAccCheckResourceAttrRegionalARN(resourceName, "arn", "glue", fmt.Sprintf("crawler/%s", rName)), @@ -290,6 +292,8 @@ func TestAccAWSGlueCrawler_JdbcTarget_Exclusions(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_glue_crawler.test" + jdbcConnectionUrl := fmt.Sprintf("jdbc:mysql://%s/testdatabase", testAccRandomDomainName()) + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, glue.EndpointsID), @@ -297,7 +301,7 @@ func TestAccAWSGlueCrawler_JdbcTarget_Exclusions(t *testing.T) { CheckDestroy: testAccCheckAWSGlueCrawlerDestroy, Steps: []resource.TestStep{ { - Config: testAccGlueCrawlerConfig_JdbcTarget_Exclusions2(rName, "exclusion1", "exclusion2"), + Config: testAccGlueCrawlerConfig_JdbcTarget_Exclusions2(rName, jdbcConnectionUrl, "exclusion1", "exclusion2"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSGlueCrawlerExists(resourceName, &crawler), testAccCheckResourceAttrRegionalARN(resourceName, "arn", "glue", fmt.Sprintf("crawler/%s", rName)), @@ -308,7 +312,7 @@ func TestAccAWSGlueCrawler_JdbcTarget_Exclusions(t *testing.T) { ), }, { - Config: testAccGlueCrawlerConfig_JdbcTarget_Exclusions1(rName, "exclusion1"), + Config: testAccGlueCrawlerConfig_JdbcTarget_Exclusions1(rName, jdbcConnectionUrl, "exclusion1"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSGlueCrawlerExists(resourceName, &crawler), testAccCheckResourceAttrRegionalARN(resourceName, "arn", "glue", fmt.Sprintf("crawler/%s", rName)), @@ -331,6 +335,8 @@ func TestAccAWSGlueCrawler_JdbcTarget_Multiple(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_glue_crawler.test" + jdbcConnectionUrl := fmt.Sprintf("jdbc:mysql://%s/testdatabase", testAccRandomDomainName()) + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, glue.EndpointsID), @@ -338,7 +344,7 @@ func TestAccAWSGlueCrawler_JdbcTarget_Multiple(t *testing.T) { CheckDestroy: testAccCheckAWSGlueCrawlerDestroy, Steps: []resource.TestStep{ { - Config: testAccGlueCrawlerConfig_JdbcTarget_Multiple(rName, "database-name/table1", "database-name/table2"), + Config: testAccGlueCrawlerConfig_JdbcTarget_Multiple(rName, jdbcConnectionUrl, "database-name/table1", "database-name/table2"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSGlueCrawlerExists(resourceName, &crawler), testAccCheckResourceAttrRegionalARN(resourceName, "arn", "glue", fmt.Sprintf("crawler/%s", rName)), @@ -352,7 +358,7 @@ func TestAccAWSGlueCrawler_JdbcTarget_Multiple(t *testing.T) { ), }, { - Config: testAccGlueCrawlerConfig_JdbcTarget(rName, "database-name/table1"), + Config: testAccGlueCrawlerConfig_JdbcTarget(rName, jdbcConnectionUrl, "database-name/table1"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSGlueCrawlerExists(resourceName, &crawler), testAccCheckResourceAttrRegionalARN(resourceName, "arn", "glue", fmt.Sprintf("crawler/%s", rName)), @@ -363,7 +369,7 @@ func TestAccAWSGlueCrawler_JdbcTarget_Multiple(t *testing.T) { ), }, { - Config: testAccGlueCrawlerConfig_JdbcTarget_Multiple(rName, "database-name/table1", "database-name/table2"), + Config: testAccGlueCrawlerConfig_JdbcTarget_Multiple(rName, jdbcConnectionUrl, "database-name/table1", "database-name/table2"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSGlueCrawlerExists(resourceName, &crawler), resource.TestCheckResourceAttr(resourceName, "jdbc_target.#", "2"), @@ -390,6 +396,8 @@ func TestAccAWSGlueCrawler_mongoDBTarget(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_glue_crawler.test" + connectionUrl := fmt.Sprintf("mongodb://%s:27017/testdatabase", testAccRandomDomainName()) + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, glue.EndpointsID), @@ -397,7 +405,7 @@ func TestAccAWSGlueCrawler_mongoDBTarget(t *testing.T) { CheckDestroy: testAccCheckAWSGlueCrawlerDestroy, Steps: []resource.TestStep{ { - Config: testAccGlueCrawlerConfigMongoDBTarget(rName, "database-name/%"), + Config: testAccGlueCrawlerConfigMongoDBTarget(rName, connectionUrl, "database-name/%"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSGlueCrawlerExists(resourceName, &crawler), resource.TestCheckResourceAttr(resourceName, "mongodb_target.#", "1"), @@ -412,7 +420,7 @@ func TestAccAWSGlueCrawler_mongoDBTarget(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccGlueCrawlerConfigMongoDBTarget(rName, "database-name/table-name"), + Config: testAccGlueCrawlerConfigMongoDBTarget(rName, connectionUrl, "database-name/table-name"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSGlueCrawlerExists(resourceName, &crawler), resource.TestCheckResourceAttr(resourceName, "mongodb_target.#", "1"), @@ -430,6 +438,8 @@ func TestAccAWSGlueCrawler_mongoDBTarget_scan_all(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_glue_crawler.test" + connectionUrl := fmt.Sprintf("mongodb://%s:27017/testdatabase", testAccRandomDomainName()) + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, glue.EndpointsID), @@ -437,7 +447,7 @@ func TestAccAWSGlueCrawler_mongoDBTarget_scan_all(t *testing.T) { CheckDestroy: testAccCheckAWSGlueCrawlerDestroy, Steps: []resource.TestStep{ { - Config: testAccGlueCrawlerConfigMongoDBTargetScanAll(rName, false), + Config: testAccGlueCrawlerConfigMongoDBTargetScanAll(rName, connectionUrl, false), Check: resource.ComposeTestCheckFunc( testAccCheckAWSGlueCrawlerExists(resourceName, &crawler), resource.TestCheckResourceAttr(resourceName, "mongodb_target.#", "1"), @@ -452,7 +462,7 @@ func TestAccAWSGlueCrawler_mongoDBTarget_scan_all(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccGlueCrawlerConfigMongoDBTargetScanAll(rName, true), + Config: testAccGlueCrawlerConfigMongoDBTargetScanAll(rName, connectionUrl, true), Check: resource.ComposeTestCheckFunc( testAccCheckAWSGlueCrawlerExists(resourceName, &crawler), resource.TestCheckResourceAttr(resourceName, "mongodb_target.#", "1"), @@ -462,7 +472,7 @@ func TestAccAWSGlueCrawler_mongoDBTarget_scan_all(t *testing.T) { ), }, { - Config: testAccGlueCrawlerConfigMongoDBTargetScanAll(rName, false), + Config: testAccGlueCrawlerConfigMongoDBTargetScanAll(rName, connectionUrl, false), Check: resource.ComposeTestCheckFunc( testAccCheckAWSGlueCrawlerExists(resourceName, &crawler), resource.TestCheckResourceAttr(resourceName, "mongodb_target.#", "1"), @@ -480,6 +490,8 @@ func TestAccAWSGlueCrawler_mongoDBTarget_multiple(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_glue_crawler.test" + connectionUrl := fmt.Sprintf("mongodb://%s:27017/testdatabase", testAccRandomDomainName()) + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, glue.EndpointsID), @@ -487,7 +499,7 @@ func TestAccAWSGlueCrawler_mongoDBTarget_multiple(t *testing.T) { CheckDestroy: testAccCheckAWSGlueCrawlerDestroy, Steps: []resource.TestStep{ { - Config: testAccGlueCrawlerConfigMongoDBMultiple(rName, "database-name/table1", "database-name/table2"), + Config: testAccGlueCrawlerConfigMongoDBMultiple(rName, connectionUrl, "database-name/table1", "database-name/table2"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSGlueCrawlerExists(resourceName, &crawler), resource.TestCheckResourceAttr(resourceName, "mongodb_target.#", "2"), @@ -505,7 +517,7 @@ func TestAccAWSGlueCrawler_mongoDBTarget_multiple(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccGlueCrawlerConfigMongoDBTarget(rName, "database-name/%"), + Config: testAccGlueCrawlerConfigMongoDBTarget(rName, connectionUrl, "database-name/%"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSGlueCrawlerExists(resourceName, &crawler), resource.TestCheckResourceAttr(resourceName, "mongodb_target.#", "1"), @@ -515,7 +527,7 @@ func TestAccAWSGlueCrawler_mongoDBTarget_multiple(t *testing.T) { ), }, { - Config: testAccGlueCrawlerConfigMongoDBMultiple(rName, "database-name/table1", "database-name/table2"), + Config: testAccGlueCrawlerConfigMongoDBMultiple(rName, connectionUrl, "database-name/table1", "database-name/table2"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSGlueCrawlerExists(resourceName, &crawler), resource.TestCheckResourceAttr(resourceName, "mongodb_target.#", "2"), @@ -1697,17 +1709,17 @@ resource "aws_glue_crawler" "test" { `, rName, path, scanRate) } -func testAccGlueCrawlerConfig_JdbcTarget(rName, path string) string { +func testAccGlueCrawlerConfig_JdbcTarget(rName, jdbcConnectionUrl, path string) string { return testAccGlueCrawlerConfig_Base(rName) + fmt.Sprintf(` resource "aws_glue_catalog_database" "test" { - name = %q + name = %[1]q } resource "aws_glue_connection" "test" { - name = %q + name = %[1]q connection_properties = { - JDBC_CONNECTION_URL = "jdbc:mysql://terraformacctesting.com/testdatabase" + JDBC_CONNECTION_URL = %[1]q PASSWORD = "testpassword" USERNAME = "testusername" } @@ -1717,28 +1729,28 @@ resource "aws_glue_crawler" "test" { depends_on = [aws_iam_role_policy_attachment.test-AWSGlueServiceRole] database_name = aws_glue_catalog_database.test.name - name = %q + name = %[1]q role = aws_iam_role.test.name jdbc_target { connection_name = aws_glue_connection.test.name - path = %q + path = %[3]q } } -`, rName, rName, rName, path) +`, rName, jdbcConnectionUrl, path) } -func testAccGlueCrawlerConfig_JdbcTarget_Exclusions1(rName, exclusion1 string) string { +func testAccGlueCrawlerConfig_JdbcTarget_Exclusions1(rName, jdbcConnectionUrl, exclusion1 string) string { return testAccGlueCrawlerConfig_Base(rName) + fmt.Sprintf(` resource "aws_glue_catalog_database" "test" { - name = %q + name = %[1]q } resource "aws_glue_connection" "test" { - name = %q + name = %[1]q connection_properties = { - JDBC_CONNECTION_URL = "jdbc:mysql://terraformacctesting.com/testdatabase" + JDBC_CONNECTION_URL = %[2]q PASSWORD = "testpassword" USERNAME = "testusername" } @@ -1748,29 +1760,29 @@ resource "aws_glue_crawler" "test" { depends_on = [aws_iam_role_policy_attachment.test-AWSGlueServiceRole] database_name = aws_glue_catalog_database.test.name - name = %q + name = %[1]q role = aws_iam_role.test.name jdbc_target { connection_name = aws_glue_connection.test.name - exclusions = [%q] + exclusions = [%[3]q] path = "database-name/table1" } } -`, rName, rName, rName, exclusion1) +`, rName, jdbcConnectionUrl, exclusion1) } -func testAccGlueCrawlerConfig_JdbcTarget_Exclusions2(rName, exclusion1, exclusion2 string) string { +func testAccGlueCrawlerConfig_JdbcTarget_Exclusions2(rName, jdbcConnectionUrl, exclusion1, exclusion2 string) string { return testAccGlueCrawlerConfig_Base(rName) + fmt.Sprintf(` resource "aws_glue_catalog_database" "test" { - name = %q + name = %[1]q } resource "aws_glue_connection" "test" { - name = %q + name = %[1]q connection_properties = { - JDBC_CONNECTION_URL = "jdbc:mysql://terraformacctesting.com/testdatabase" + JDBC_CONNECTION_URL = %[2]q PASSWORD = "testpassword" USERNAME = "testusername" } @@ -1780,29 +1792,29 @@ resource "aws_glue_crawler" "test" { depends_on = [aws_iam_role_policy_attachment.test-AWSGlueServiceRole] database_name = aws_glue_catalog_database.test.name - name = %q + name = %[1]q role = aws_iam_role.test.name jdbc_target { connection_name = aws_glue_connection.test.name - exclusions = [%q, %q] + exclusions = [%[3]q, %[4]q] path = "database-name/table1" } } -`, rName, rName, rName, exclusion1, exclusion2) +`, rName, jdbcConnectionUrl, exclusion1, exclusion2) } -func testAccGlueCrawlerConfig_JdbcTarget_Multiple(rName, path1, path2 string) string { +func testAccGlueCrawlerConfig_JdbcTarget_Multiple(rName, jdbcConnectionUrl, path1, path2 string) string { return testAccGlueCrawlerConfig_Base(rName) + fmt.Sprintf(` resource "aws_glue_catalog_database" "test" { - name = %q + name = %[1]q } resource "aws_glue_connection" "test" { - name = %q + name = %[1]q connection_properties = { - JDBC_CONNECTION_URL = "jdbc:mysql://terraformacctesting.com/testdatabase" + JDBC_CONNECTION_URL = %[2]q PASSWORD = "testpassword" USERNAME = "testusername" } @@ -1812,40 +1824,40 @@ resource "aws_glue_crawler" "test" { depends_on = [aws_iam_role_policy_attachment.test-AWSGlueServiceRole] database_name = aws_glue_catalog_database.test.name - name = %q + name = %[1]q role = aws_iam_role.test.name jdbc_target { connection_name = aws_glue_connection.test.name - path = %q + path = %[3]q } jdbc_target { connection_name = aws_glue_connection.test.name - path = %q + path = %[4]q } } -`, rName, rName, rName, path1, path2) +`, rName, jdbcConnectionUrl, path1, path2) } func testAccGlueCrawlerConfig_Role_ARN_NoPath(rName string) string { return testAccGlueCrawlerConfig_Base(rName) + fmt.Sprintf(` resource "aws_glue_catalog_database" "test" { - name = %q + name = %[1]q } resource "aws_glue_crawler" "test" { depends_on = [aws_iam_role_policy_attachment.test-AWSGlueServiceRole] database_name = aws_glue_catalog_database.test.name - name = %q + name = %[1]q role = aws_iam_role.test.arn s3_target { path = "s3://bucket-name" } } -`, rName, rName) +`, rName) } func testAccGlueCrawlerConfig_Role_ARN_Path(rName string) string { @@ -1853,7 +1865,7 @@ func testAccGlueCrawlerConfig_Role_ARN_Path(rName string) string { data "aws_partition" "current" {} resource "aws_iam_role" "test" { - name = %q + name = %[1]q path = "/path/" assume_role_policy = < Date: Tue, 29 Jun 2021 12:34:31 -0700 Subject: [PATCH 258/398] Removes more hard-coded domain names from Route 53 acceptance tests --- aws/data_source_aws_route53_zone_test.go | 42 ++-- aws/provider_test.go | 36 +++- aws/resource_aws_route53_health_check_test.go | 22 +- aws/resource_aws_route53_query_log_test.go | 15 +- ...esource_aws_route53_record_migrate_test.go | 8 +- aws/resource_aws_route53_record_test.go | 190 +++++++++--------- ...te53_resolver_firewall_domain_list_test.go | 11 +- ...ource_aws_route53_zone_association_test.go | 62 +++--- 8 files changed, 214 insertions(+), 172 deletions(-) diff --git a/aws/data_source_aws_route53_zone_test.go b/aws/data_source_aws_route53_zone_test.go index dcdcc606313..9f130db6f35 100644 --- a/aws/data_source_aws_route53_zone_test.go +++ b/aws/data_source_aws_route53_zone_test.go @@ -10,10 +10,11 @@ import ( ) func TestAccAWSRoute53ZoneDataSource_id(t *testing.T) { - rInt := acctest.RandInt() resourceName := "aws_route53_zone.test" dataSourceName := "data.aws_route53_zone.test" + fqdn := testAccRandomFQDomainName() + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, route53.EndpointsID), @@ -21,7 +22,7 @@ func TestAccAWSRoute53ZoneDataSource_id(t *testing.T) { CheckDestroy: testAccCheckRoute53ZoneDestroy, Steps: []resource.TestStep{ { - Config: testAccDataSourceAwsRoute53ZoneConfigId(rInt), + Config: testAccDataSourceAwsRoute53ZoneConfigId(fqdn), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrPair(resourceName, "id", dataSourceName, "id"), resource.TestCheckResourceAttrPair(resourceName, "name", dataSourceName, "name"), @@ -34,10 +35,11 @@ func TestAccAWSRoute53ZoneDataSource_id(t *testing.T) { } func TestAccAWSRoute53ZoneDataSource_name(t *testing.T) { - rInt := acctest.RandInt() resourceName := "aws_route53_zone.test" dataSourceName := "data.aws_route53_zone.test" + fqdn := testAccRandomFQDomainName() + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, route53.EndpointsID), @@ -45,7 +47,7 @@ func TestAccAWSRoute53ZoneDataSource_name(t *testing.T) { CheckDestroy: testAccCheckRoute53ZoneDestroy, Steps: []resource.TestStep{ { - Config: testAccDataSourceAwsRoute53ZoneConfigName(rInt), + Config: testAccDataSourceAwsRoute53ZoneConfigName(fqdn), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrPair(resourceName, "id", dataSourceName, "id"), resource.TestCheckResourceAttrPair(resourceName, "name", dataSourceName, "name"), @@ -62,6 +64,8 @@ func TestAccAWSRoute53ZoneDataSource_tags(t *testing.T) { resourceName := "aws_route53_zone.test" dataSourceName := "data.aws_route53_zone.test" + fqdn := testAccRandomFQDomainName() + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, route53.EndpointsID), @@ -69,7 +73,7 @@ func TestAccAWSRoute53ZoneDataSource_tags(t *testing.T) { CheckDestroy: testAccCheckRoute53ZoneDestroy, Steps: []resource.TestStep{ { - Config: testAccDataSourceAwsRoute53ZoneConfigTagsPrivate(rInt), + Config: testAccDataSourceAwsRoute53ZoneConfigTagsPrivate(fqdn, rInt), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrPair(resourceName, "id", dataSourceName, "id"), resource.TestCheckResourceAttrPair(resourceName, "name", dataSourceName, "name"), @@ -128,50 +132,46 @@ func TestAccAWSRoute53ZoneDataSource_serviceDiscovery(t *testing.T) { }) } -func testAccDataSourceAwsRoute53ZoneConfigId(rInt int) string { +func testAccDataSourceAwsRoute53ZoneConfigId(fqdn string) string { return fmt.Sprintf(` resource "aws_route53_zone" "test" { - name = "terraformtestacchz-%[1]d.com." + name = %[1]q } data "aws_route53_zone" "test" { zone_id = aws_route53_zone.test.zone_id } -`, rInt) +`, fqdn) } -func testAccDataSourceAwsRoute53ZoneConfigName(rInt int) string { +func testAccDataSourceAwsRoute53ZoneConfigName(fqdn string) string { return fmt.Sprintf(` resource "aws_route53_zone" "test" { - name = "terraformtestacchz-%[1]d.com." + name = %[1]q } data "aws_route53_zone" "test" { name = aws_route53_zone.test.name } -`, rInt) +`, fqdn) } -func testAccDataSourceAwsRoute53ZoneConfigTagsPrivate(rInt int) string { +func testAccDataSourceAwsRoute53ZoneConfigTagsPrivate(fqdn string, rInt int) string { return fmt.Sprintf(` resource "aws_vpc" "test" { cidr_block = "10.0.0.0/16" - - tags = { - Name = "terraform-testacc-r53-zone-data-source-%[1]d" - } } resource "aws_route53_zone" "test" { - name = "terraformtestacchz-%[1]d.com." + name = %[1]q vpc { vpc_id = aws_vpc.test.id } tags = { - Environment = "tf-acc-test-%[1]d" - Name = "tf-acc-test-%[1]d" + Environment = "tf-acc-test-%[2]d" + Name = "tf-acc-test-%[2]d" } } @@ -181,10 +181,10 @@ data "aws_route53_zone" "test" { vpc_id = aws_vpc.test.id tags = { - Environment = "tf-acc-test-%[1]d" + Environment = "tf-acc-test-%[2]d" } } -`, rInt) +`, fqdn, rInt) } func testAccDataSourceAwsRoute53ZoneConfigVpc(rInt int) string { diff --git a/aws/provider_test.go b/aws/provider_test.go index a9dcaccf772..c7ac625ea75 100644 --- a/aws/provider_test.go +++ b/aws/provider_test.go @@ -2285,24 +2285,48 @@ func composeConfig(config ...string) string { return str.String() } -const testingTopLevelDomain = "test" +type domainName string + +// The top level domain ".test" is reserved by IANA for testing purposes: +// https://datatracker.ietf.org/doc/html/rfc6761 +const domainNameTestTopLevelDomain domainName = "test" // testAccRandomSubdomain creates a random three-level domain name in the form // "..test" // The top level domain ".test" is reserved by IANA for testing purposes: // https://datatracker.ietf.org/doc/html/rfc6761 func testAccRandomSubdomain() string { - return testAccSubdomainWithRandomSecondLevel(acctest.RandString(8)) + return string(testAccRandomDomain().RandomSubdomain()) } -// testAccRandomDomainName creates a random second level domain name in the form +// testAccRandomDomainName creates a random two-level domain name in the form // ".test" // The top level domain ".test" is reserved by IANA for testing purposes: // https://datatracker.ietf.org/doc/html/rfc6761 func testAccRandomDomainName() string { - return fmt.Sprintf("%s.%s", acctest.RandString(8), testingTopLevelDomain) + return string(testAccRandomDomain()) +} + +// testAccRandomFQDomainName creates a random fully-qualified two-level domain name in the form +// ".test." +// The top level domain ".test" is reserved by IANA for testing purposes: +// https://datatracker.ietf.org/doc/html/rfc6761 +func testAccRandomFQDomainName() string { + return string(testAccRandomDomain().FQDN()) +} + +func (d domainName) Subdomain(name string) domainName { + return domainName(fmt.Sprintf("%s.%s", name, d)) +} + +func (d domainName) RandomSubdomain() domainName { + return d.Subdomain(acctest.RandString(8)) +} + +func (d domainName) FQDN() domainName { + return domainName(fmt.Sprintf("%s.", d)) } -func testAccSubdomainWithRandomSecondLevel(thing string) string { - return fmt.Sprintf("%s.%s", thing, testAccRandomDomainName()) +func testAccRandomDomain() domainName { + return domainNameTestTopLevelDomain.RandomSubdomain() } diff --git a/aws/resource_aws_route53_health_check_test.go b/aws/resource_aws_route53_health_check_test.go index 097674d5077..15a0ca31866 100644 --- a/aws/resource_aws_route53_health_check_test.go +++ b/aws/resource_aws_route53_health_check_test.go @@ -429,7 +429,7 @@ func testAccCheckRoute53HealthCheckDisappears(hCheck *route53.HealthCheck) resou const testAccRoute53HealthCheckConfig = ` resource "aws_route53_health_check" "test" { - fqdn = "dev.notexample.com" + fqdn = "dev.example.com" port = 80 type = "HTTP" resource_path = "/" @@ -443,7 +443,7 @@ resource "aws_route53_health_check" "test" { func testAccRoute53HealthCheckConfigTags1(tag1Key, tag1Value string) string { return fmt.Sprintf(` resource "aws_route53_health_check" "test" { - fqdn = "dev.notexample.com" + fqdn = "dev.example.com" port = 80 type = "HTTP" resource_path = "/" @@ -462,7 +462,7 @@ resource "aws_route53_health_check" "test" { func testAccRoute53HealthCheckConfigTags2(tag1Key, tag1Value, tagKey2, tagValue2 string) string { return fmt.Sprintf(` resource "aws_route53_health_check" "test" { - fqdn = "dev.notexample.com" + fqdn = "dev.example.com" port = 80 type = "HTTP" resource_path = "/" @@ -481,7 +481,7 @@ resource "aws_route53_health_check" "test" { const testAccRoute53HealthCheckConfigUpdate = ` resource "aws_route53_health_check" "test" { - fqdn = "dev.notexample.com" + fqdn = "dev.example.com" port = 80 type = "HTTP" resource_path = "/" @@ -539,7 +539,7 @@ resource "aws_route53_health_check" "test" { const testAccRoute53HealthCheckConfig_withChildHealthChecks = ` resource "aws_route53_health_check" "child1" { - fqdn = "child1.notexample.com" + fqdn = "child1.example.com" port = 80 type = "HTTP" resource_path = "/" @@ -602,7 +602,7 @@ resource "aws_route53_health_check" "test" { const testAccRoute53HealthCheckConfigWithSearchString = ` resource "aws_route53_health_check" "test" { - fqdn = "dev.notexample.com" + fqdn = "dev.example.com" port = 80 type = "HTTP_STR_MATCH" resource_path = "/" @@ -620,7 +620,7 @@ resource "aws_route53_health_check" "test" { const testAccRoute53HealthCheckConfigWithSearchStringUpdate = ` resource "aws_route53_health_check" "test" { - fqdn = "dev.notexample.com" + fqdn = "dev.example.com" port = 80 type = "HTTP_STR_MATCH" resource_path = "/" @@ -638,7 +638,7 @@ resource "aws_route53_health_check" "test" { const testAccRoute53HealthCheckConfigWithoutSNI = ` resource "aws_route53_health_check" "test" { - fqdn = "dev.notexample.com" + fqdn = "dev.example.com" port = 443 type = "HTTPS" resource_path = "/" @@ -655,7 +655,7 @@ resource "aws_route53_health_check" "test" { const testAccRoute53HealthCheckConfigWithSNI = ` resource "aws_route53_health_check" "test" { - fqdn = "dev.notexample.com" + fqdn = "dev.example.com" port = 443 type = "HTTPS" resource_path = "/" @@ -673,7 +673,7 @@ resource "aws_route53_health_check" "test" { const testAccRoute53HealthCheckConfigWithSNIDisabled = ` resource "aws_route53_health_check" "test" { - fqdn = "dev.notexample.com" + fqdn = "dev.example.com" port = 443 type = "HTTPS" resource_path = "/" @@ -694,7 +694,7 @@ func testAccRoute53HealthCheckConfigDisabled(disabled bool) string { resource "aws_route53_health_check" "test" { disabled = %[1]t failure_threshold = "2" - fqdn = "dev.notexample.com" + fqdn = "dev.example.com" port = 80 request_interval = "30" resource_path = "/" diff --git a/aws/resource_aws_route53_query_log_test.go b/aws/resource_aws_route53_query_log_test.go index 05e02070ded..dc6c45b622d 100644 --- a/aws/resource_aws_route53_query_log_test.go +++ b/aws/resource_aws_route53_query_log_test.go @@ -3,7 +3,6 @@ package aws import ( "fmt" "log" - "strings" "testing" "github.com/aws/aws-sdk-go/aws" @@ -72,7 +71,9 @@ func TestAccAWSRoute53QueryLog_basic(t *testing.T) { cloudwatchLogGroupResourceName := "aws_cloudwatch_log_group.test" resourceName := "aws_route53_query_log.test" route53ZoneResourceName := "aws_route53_zone.test" - rName := strings.ToLower(fmt.Sprintf("%s-%s", t.Name(), acctest.RandString(5))) + + rName := acctest.RandomWithPrefix("tf-acc-test") + domainName := testAccRandomDomainName() var queryLoggingConfig route53.QueryLoggingConfig resource.ParallelTest(t, resource.TestCase{ @@ -82,7 +83,7 @@ func TestAccAWSRoute53QueryLog_basic(t *testing.T) { CheckDestroy: testAccCheckRoute53QueryLogDestroy, Steps: []resource.TestStep{ { - Config: testAccCheckAWSRoute53QueryLogResourceConfigBasic1(rName), + Config: testAccCheckAWSRoute53QueryLogResourceConfigBasic1(rName, domainName), Check: resource.ComposeTestCheckFunc( testAccCheckRoute53QueryLogExists(resourceName, &queryLoggingConfig), resource.TestCheckResourceAttrPair(resourceName, "cloudwatch_log_group_arn", cloudwatchLogGroupResourceName, "arn"), @@ -154,7 +155,7 @@ func testAccCheckRoute53QueryLogDestroy(s *terraform.State) error { return nil } -func testAccCheckAWSRoute53QueryLogResourceConfigBasic1(rName string) string { +func testAccCheckAWSRoute53QueryLogResourceConfigBasic1(rName, domainName string) string { return composeConfig( testAccRoute53QueryLogRegionProviderConfig(), fmt.Sprintf(` @@ -182,12 +183,12 @@ data "aws_iam_policy_document" "test" { } resource "aws_cloudwatch_log_resource_policy" "test" { - policy_name = "%[1]s" + policy_name = %[1]q policy_document = data.aws_iam_policy_document.test.json } resource "aws_route53_zone" "test" { - name = "%[1]s.com" + name = %[2]q } resource "aws_route53_query_log" "test" { @@ -196,5 +197,5 @@ resource "aws_route53_query_log" "test" { cloudwatch_log_group_arn = aws_cloudwatch_log_group.test.arn zone_id = aws_route53_zone.test.zone_id } -`, rName)) +`, rName, domainName)) } diff --git a/aws/resource_aws_route53_record_migrate_test.go b/aws/resource_aws_route53_record_migrate_test.go index 69b4dfdb722..8d7176e8c25 100644 --- a/aws/resource_aws_route53_record_migrate_test.go +++ b/aws/resource_aws_route53_record_migrate_test.go @@ -26,17 +26,17 @@ func TestAWSRoute53RecordMigrateState(t *testing.T) { StateVersion: 0, ID: "some_id", Attributes: map[string]string{ - "name": "www.notdomain.com.", + "name": "www.example.com.", }, - Expected: "www.notdomain.com", + Expected: "www.example.com", }, "v0_2": { StateVersion: 0, ID: "some_id", Attributes: map[string]string{ - "name": "www.notdomain.com", + "name": "www.example.com", }, - Expected: "www.notdomain.com", + Expected: "www.example.com", }, } diff --git a/aws/resource_aws_route53_record_test.go b/aws/resource_aws_route53_record_test.go index bbaa1c6cec9..9315da491f3 100644 --- a/aws/resource_aws_route53_record_test.go +++ b/aws/resource_aws_route53_record_test.go @@ -23,11 +23,11 @@ func TestCleanRecordName(t *testing.T) { cases := []struct { Input, Output string }{ - {"www.nonexample.com", "www.nonexample.com"}, - {"\\052.nonexample.com", "*.nonexample.com"}, - {"\\100.nonexample.com", "@.nonexample.com"}, - {"\\043.nonexample.com", "#.nonexample.com"}, - {"nonexample.com", "nonexample.com"}, + {"www.example.com", "www.example.com"}, + {"\\052.example.com", "*.example.com"}, + {"\\100.example.com", "@.example.com"}, + {"\\043.example.com", "#.example.com"}, + {"example.com", "example.com"}, } for _, tc := range cases { @@ -42,16 +42,16 @@ func TestExpandRecordName(t *testing.T) { cases := []struct { Input, Output string }{ - {"www", "www.nonexample.com"}, - {"www.", "www.nonexample.com"}, - {"dev.www", "dev.www.nonexample.com"}, - {"*", "*.nonexample.com"}, - {"nonexample.com", "nonexample.com"}, - {"test.nonexample.com", "test.nonexample.com"}, - {"test.nonexample.com.", "test.nonexample.com"}, + {"www", "www.example.com"}, + {"www.", "www.example.com"}, + {"dev.www", "dev.www.example.com"}, + {"*", "*.example.com"}, + {"example.com", "example.com"}, + {"test.example.com", "test.example.com"}, + {"test.example.com.", "test.example.com"}, } - zone_name := "nonexample.com" + zone_name := "example.com" for _, tc := range cases { actual := expandRecordName(tc.Input, zone_name) if actual != tc.Output { @@ -64,11 +64,11 @@ func TestNormalizeAwsAliasName(t *testing.T) { cases := []struct { Input, Output string }{ - {"www.nonexample.com", "www.nonexample.com"}, - {"www.nonexample.com.", "www.nonexample.com"}, + {"www.example.com", "www.example.com"}, + {"www.example.com.", "www.example.com"}, {"dualstack.name-123456789.region.elb.amazonaws.com", "name-123456789.region.elb.amazonaws.com"}, {"dualstack.test-987654321.region.elb.amazonaws.com", "test-987654321.region.elb.amazonaws.com"}, - {"dualstacktest.com", "dualstacktest.com"}, + {"dualstacktest.test", "dualstacktest.test"}, {"NAME-123456789.region.elb.amazonaws.com", "name-123456789.region.elb.amazonaws.com"}, } @@ -84,11 +84,11 @@ func TestParseRecordId(t *testing.T) { cases := []struct { Input, Zone, Name, Type, Set string }{ - {"ABCDEF_test.notexample.com_A", "ABCDEF", "test.notexample.com", "A", ""}, - {"ABCDEF_test.notexample.com._A", "ABCDEF", "test.notexample.com", "A", ""}, - {"ABCDEF_test.notexample.com_A_set1", "ABCDEF", "test.notexample.com", "A", "set1"}, - {"ABCDEF__underscore.notexample.com_A", "ABCDEF", "_underscore.notexample.com", "A", ""}, - {"ABCDEF__underscore.notexample.com_A_set1", "ABCDEF", "_underscore.notexample.com", "A", "set1"}, + {"ABCDEF_test.example.com_A", "ABCDEF", "test.example.com", "A", ""}, + {"ABCDEF_test.example.com._A", "ABCDEF", "test.example.com", "A", ""}, + {"ABCDEF_test.example.com_A_set1", "ABCDEF", "test.example.com", "A", "set1"}, + {"ABCDEF__underscore.example.com_A", "ABCDEF", "_underscore.example.com", "A", ""}, + {"ABCDEF__underscore.example.com_A_set1", "ABCDEF", "_underscore.example.com", "A", "set1"}, } for _, tc := range cases { @@ -318,7 +318,7 @@ func TestAccAWSRoute53Record_spfSupport(t *testing.T) { Config: testAccRoute53RecordConfigSPF, Check: resource.ComposeTestCheckFunc( testAccCheckRoute53RecordExists(resourceName, &record1), - resource.TestCheckTypeSetElemAttr(resourceName, "records.*", "include:notexample.com"), + resource.TestCheckTypeSetElemAttr(resourceName, "records.*", "include:domain.test"), ), }, { @@ -345,7 +345,7 @@ func TestAccAWSRoute53Record_caaSupport(t *testing.T) { Config: testAccRoute53RecordConfigCAA, Check: resource.ComposeTestCheckFunc( testAccCheckRoute53RecordExists(resourceName, &record1), - resource.TestCheckTypeSetElemAttr(resourceName, "records.*", "0 issue \"exampleca.com;\""), + resource.TestCheckTypeSetElemAttr(resourceName, "records.*", "0 issue \"domainca.test;\""), ), }, { @@ -1069,7 +1069,7 @@ func testAccAWSRoute53RecordOverwriteExpectErrorCheck(t *testing.T) resource.Err t.Fatalf("Expected an error but got none") } - re := regexp.MustCompile(`Tried to create resource record set \[name='www.notexample.com.', type='A'] but it already exists`) + re := regexp.MustCompile(`Tried to create resource record set \[name='www.domain.test.', type='A'] but it already exists`) if !re.MatchString(err.Error()) { t.Fatalf("Expected an error with pattern, no match on: %s", err) } @@ -1090,7 +1090,7 @@ func testAccCheckRoute53RecordDestroy(s *terraform.State) error { name := parts[1] rType := parts[2] - en := expandRecordName(name, "notexample.com") + en := expandRecordName(name, "domain.test") lopts := &route53.ListResourceRecordSetsInput{ HostedZoneId: aws.String(cleanZoneID(zone)), @@ -1171,7 +1171,7 @@ func testAccCheckRoute53RecordExists(n string, resourceRecordSet *route53.Resour name := parts[1] rType := parts[2] - en := expandRecordName(name, "notexample.com") + en := expandRecordName(name, "domain.test") lopts := &route53.ListResourceRecordSetsInput{ HostedZoneId: aws.String(cleanZoneID(zone)), @@ -1240,12 +1240,12 @@ func testAccCheckRoute53RecordDoesNotExist(zoneResourceName string, recordName s func testAccRoute53RecordConfig_allowOverwrite(allowOverwrite bool) string { return fmt.Sprintf(` resource "aws_route53_zone" "main" { - name = "notexample.com." + name = "domain.test." } resource "aws_route53_record" "default" { zone_id = aws_route53_zone.main.zone_id - name = "www.notexample.com" + name = "www.domain.test" type = "A" ttl = "30" records = ["127.0.0.1"] @@ -1256,7 +1256,7 @@ resource "aws_route53_record" "overwriting" { allow_overwrite = %[1]t zone_id = aws_route53_zone.main.zone_id - name = "www.notexample.com" + name = "www.domain.test" type = "A" ttl = "30" records = ["127.0.0.1"] @@ -1266,12 +1266,12 @@ resource "aws_route53_record" "overwriting" { const testAccRoute53RecordConfig = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "default" { zone_id = aws_route53_zone.main.zone_id - name = "www.NOTexamplE.com" + name = "www.DOmaiN.test" type = "A" ttl = "30" records = ["127.0.0.1", "127.0.0.27"] @@ -1280,12 +1280,12 @@ resource "aws_route53_record" "default" { const testAccRoute53RecordConfig_nameWithTrailingPeriod = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "default" { zone_id = aws_route53_zone.main.zone_id - name = "www.NOTexamplE.com." + name = "www.DOmaiN.test." type = "A" ttl = "30" records = ["127.0.0.1", "127.0.0.27"] @@ -1294,7 +1294,7 @@ resource "aws_route53_record" "default" { const testAccRoute53RecordConfigMultiple = ` resource "aws_route53_zone" "test" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "test" { @@ -1310,12 +1310,12 @@ resource "aws_route53_record" "test" { const testAccRoute53RecordConfig_fqdn = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "default" { zone_id = aws_route53_zone.main.zone_id - name = "www.NOTexamplE.com" + name = "www.DOmaiN.test" type = "A" ttl = "30" records = ["127.0.0.1", "127.0.0.27"] @@ -1328,12 +1328,12 @@ resource "aws_route53_record" "default" { const testAccRoute53RecordConfig_fqdn_no_op = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "default" { zone_id = aws_route53_zone.main.zone_id - name = "www.NOTexamplE.com." + name = "www.DOmaiN.test." type = "A" ttl = "30" records = ["127.0.0.1", "127.0.0.27"] @@ -1346,7 +1346,7 @@ resource "aws_route53_record" "default" { const testAccRoute53RecordConfigSuffix = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "default" { @@ -1360,7 +1360,7 @@ resource "aws_route53_record" "default" { const testAccRoute53WildCardRecordConfig = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "default" { @@ -1373,7 +1373,7 @@ resource "aws_route53_record" "default" { resource "aws_route53_record" "wildcard" { zone_id = aws_route53_zone.main.zone_id - name = "*.notexample.com" + name = "*.domain.test" type = "A" ttl = "30" records = ["127.0.0.1"] @@ -1382,7 +1382,7 @@ resource "aws_route53_record" "wildcard" { const testAccRoute53WildCardRecordConfigUpdate = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "default" { @@ -1395,7 +1395,7 @@ resource "aws_route53_record" "default" { resource "aws_route53_record" "wildcard" { zone_id = aws_route53_zone.main.zone_id - name = "*.notexample.com" + name = "*.domain.test" type = "A" ttl = "60" records = ["127.0.0.1"] @@ -1404,7 +1404,7 @@ resource "aws_route53_record" "wildcard" { const testAccRoute53RecordConfigTXT = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "default" { @@ -1418,7 +1418,7 @@ resource "aws_route53_record" "default" { const testAccRoute53RecordConfigSPF = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "default" { @@ -1426,13 +1426,13 @@ resource "aws_route53_record" "default" { name = "test" type = "SPF" ttl = "30" - records = ["include:notexample.com"] + records = ["include:domain.test"] } ` const testAccRoute53RecordConfigCAA = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "default" { @@ -1441,13 +1441,13 @@ resource "aws_route53_record" "default" { type = "CAA" ttl = "30" - records = ["0 issue \"exampleca.com;\""] + records = ["0 issue \"domainca.test;\""] } ` const testAccRoute53RecordConfigDS = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "default" { @@ -1461,11 +1461,11 @@ resource "aws_route53_record" "default" { const testAccRoute53FailoverCNAMERecord = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_health_check" "foo" { - fqdn = "dev.notexample.com" + fqdn = "dev.domain.test" port = 80 type = "HTTP" resource_path = "/" @@ -1489,7 +1489,7 @@ resource "aws_route53_record" "www-primary" { health_check_id = aws_route53_health_check.foo.id set_identifier = "www-primary" - records = ["primary.notexample.com"] + records = ["primary.domain.test"] } resource "aws_route53_record" "www-secondary" { @@ -1503,13 +1503,13 @@ resource "aws_route53_record" "www-secondary" { } set_identifier = "www-secondary" - records = ["secondary.notexample.com"] + records = ["secondary.domain.test"] } ` const testAccRoute53WeightedCNAMERecord = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "www-dev" { @@ -1523,7 +1523,7 @@ resource "aws_route53_record" "www-dev" { } set_identifier = "dev" - records = ["dev.notexample.com"] + records = ["dev.domain.test"] } resource "aws_route53_record" "www-live" { @@ -1537,7 +1537,7 @@ resource "aws_route53_record" "www-live" { } set_identifier = "live" - records = ["dev.notexample.com"] + records = ["dev.domain.test"] } resource "aws_route53_record" "www-off" { @@ -1551,13 +1551,13 @@ resource "aws_route53_record" "www-off" { } set_identifier = "off" - records = ["dev.notexample.com"] + records = ["dev.domain.test"] } ` const testAccRoute53GeolocationCNAMERecord = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "default" { @@ -1571,7 +1571,7 @@ resource "aws_route53_record" "default" { } set_identifier = "Default" - records = ["dev.notexample.com"] + records = ["dev.domain.test"] } resource "aws_route53_record" "california" { @@ -1586,7 +1586,7 @@ resource "aws_route53_record" "california" { } set_identifier = "California" - records = ["dev.notexample.com"] + records = ["dev.domain.test"] } resource "aws_route53_record" "oceania" { @@ -1600,7 +1600,7 @@ resource "aws_route53_record" "oceania" { } set_identifier = "Oceania" - records = ["dev.notexample.com"] + records = ["dev.domain.test"] } resource "aws_route53_record" "denmark" { @@ -1614,14 +1614,14 @@ resource "aws_route53_record" "denmark" { } set_identifier = "Denmark" - records = ["dev.notexample.com"] + records = ["dev.domain.test"] } ` func testAccRoute53LatencyCNAMERecord(firstRegion, secondRegion, thirdRegion string) string { return fmt.Sprintf(` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "first_region" { @@ -1635,7 +1635,7 @@ resource "aws_route53_record" "first_region" { } set_identifier = %[1]q - records = ["dev.notexample.com"] + records = ["dev.domain.test"] } resource "aws_route53_record" "second_region" { @@ -1649,7 +1649,7 @@ resource "aws_route53_record" "second_region" { } set_identifier = %[2]q - records = ["dev.notexample.com"] + records = ["dev.domain.test"] } resource "aws_route53_record" "third_region" { @@ -1663,7 +1663,7 @@ resource "aws_route53_record" "third_region" { } set_identifier = %[3]q - records = ["dev.notexample.com"] + records = ["dev.domain.test"] } `, firstRegion, secondRegion, thirdRegion) } @@ -1679,7 +1679,7 @@ data "aws_availability_zones" "available" { } resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "alias" { @@ -1718,7 +1718,7 @@ data "aws_availability_zones" "available" { } resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "alias" { @@ -1749,7 +1749,7 @@ resource "aws_elb" "main" { func testAccRoute53RecordConfigAliasS3(rName string) string { return fmt.Sprintf(` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_s3_bucket" "website" { @@ -1779,12 +1779,12 @@ func testAccRoute53RecordConfigHealthCheckIdSetIdentifier(setIdentifier string) return fmt.Sprintf(` resource "aws_route53_zone" "test" { force_destroy = true - name = "notexample.com" + name = "domain.test" } resource "aws_route53_health_check" "test" { failure_threshold = "2" - fqdn = "test.notexample.com" + fqdn = "test.domain.test" port = 80 request_interval = "30" resource_path = "/" @@ -1811,12 +1811,12 @@ func testAccRoute53RecordConfigHealthCheckIdTypeA() string { return ` resource "aws_route53_zone" "test" { force_destroy = true - name = "notexample.com" + name = "domain.test" } resource "aws_route53_health_check" "test" { failure_threshold = "2" - fqdn = "test.notexample.com" + fqdn = "test.domain.test" port = 80 request_interval = "30" resource_path = "/" @@ -1843,12 +1843,12 @@ func testAccRoute53RecordConfigHealthCheckIdTypeCname() string { return ` resource "aws_route53_zone" "test" { force_destroy = true - name = "notexample.com" + name = "domain.test" } resource "aws_route53_health_check" "test" { failure_threshold = "2" - fqdn = "test.notexample.com" + fqdn = "test.domain.test" port = 80 request_interval = "30" resource_path = "/" @@ -1859,7 +1859,7 @@ resource "aws_route53_record" "test" { zone_id = aws_route53_zone.test.zone_id health_check_id = aws_route53_health_check.test.id name = "test" - records = ["test1.notexample.com"] + records = ["test1.domain.test"] set_identifier = "test" ttl = "5" type = "CNAME" @@ -1921,7 +1921,7 @@ resource "aws_vpc_endpoint" "test" { } resource "aws_route53_zone" "test" { - name = "notexample.com" + name = "domain.test" vpc { vpc_id = aws_vpc.test.id @@ -1973,7 +1973,7 @@ data "aws_availability_zones" "available" { } resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_elb" "live" { @@ -2039,7 +2039,7 @@ resource "aws_route53_record" "elb_weighted_alias_dev" { const testAccRoute53WeightedR53AliasRecord = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "blue_origin" { @@ -2097,7 +2097,7 @@ resource "aws_route53_record" "r53_weighted_alias_dev" { const testAccRoute53RecordTypeChangePre = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "sample" { @@ -2111,7 +2111,7 @@ resource "aws_route53_record" "sample" { const testAccRoute53RecordTypeChangePost = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "sample" { @@ -2125,7 +2125,7 @@ resource "aws_route53_record" "sample" { const testAccRoute53RecordNameChangePre = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "sample" { @@ -2139,7 +2139,7 @@ resource "aws_route53_record" "sample" { const testAccRoute53RecordNameChangePost = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "sample" { @@ -2153,7 +2153,7 @@ resource "aws_route53_record" "sample" { const testAccRoute53RecordSetIdentifierChangePre = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "basic_to_weighted" { @@ -2167,7 +2167,7 @@ resource "aws_route53_record" "basic_to_weighted" { const testAccRoute53RecordSetIdentifierChangePost = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "basic_to_weighted" { @@ -2195,7 +2195,7 @@ data "aws_availability_zones" "available" { } resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_elb" "alias_change" { @@ -2225,7 +2225,7 @@ resource "aws_route53_record" "elb_alias_change" { const testAccRoute53RecordAliasChangePost = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "elb_alias_change" { @@ -2239,7 +2239,7 @@ resource "aws_route53_record" "elb_alias_change" { const testAccRoute53RecordConfigEmptyName = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "empty" { @@ -2253,12 +2253,12 @@ resource "aws_route53_record" "empty" { const testAccRoute53RecordConfigLongTxtRecord = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "long_txt" { zone_id = aws_route53_zone.main.zone_id - name = "google.notexample.com" + name = "google.domain.test" type = "TXT" ttl = "30" records = [ @@ -2269,12 +2269,12 @@ resource "aws_route53_record" "long_txt" { const testAccRoute53RecordConfigUnderscoreInName = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "underscore" { zone_id = aws_route53_zone.main.zone_id - name = "_underscore.notexample.com" + name = "_underscore.domain.test" type = "A" ttl = "30" records = ["127.0.0.1"] @@ -2283,7 +2283,7 @@ resource "aws_route53_record" "underscore" { const testAccRoute53MultiValueAnswerARecord = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "www-server1" { @@ -2309,7 +2309,7 @@ resource "aws_route53_record" "www-server2" { const testaccRoute53RecordConfigWithWeightedRoutingPolicy = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "www-server1" { @@ -2329,7 +2329,7 @@ resource "aws_route53_record" "www-server1" { const testaccRoute53RecordConfigWithSimpleRoutingPolicy = ` resource "aws_route53_zone" "main" { - name = "notexample.com" + name = "domain.test" } resource "aws_route53_record" "www-server1" { diff --git a/aws/resource_aws_route53_resolver_firewall_domain_list_test.go b/aws/resource_aws_route53_resolver_firewall_domain_list_test.go index f0897972006..c076ea85070 100644 --- a/aws/resource_aws_route53_resolver_firewall_domain_list_test.go +++ b/aws/resource_aws_route53_resolver_firewall_domain_list_test.go @@ -99,6 +99,9 @@ func TestAccAWSRoute53ResolverFirewallDomainList_domains(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_route53_resolver_firewall_domain_list.test" + domainName1 := testAccRandomFQDomainName() + domainName2 := testAccRandomFQDomainName() + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSRoute53Resolver(t) }, ErrorCheck: testAccErrorCheck(t, route53resolver.EndpointsID), @@ -106,12 +109,12 @@ func TestAccAWSRoute53ResolverFirewallDomainList_domains(t *testing.T) { CheckDestroy: testAccCheckRoute53ResolverFirewallDomainListDestroy, Steps: []resource.TestStep{ { - Config: testAccRoute53ResolverFirewallDomainListConfigDomains(rName, "foo.com."), + Config: testAccRoute53ResolverFirewallDomainListConfigDomains(rName, domainName1), Check: resource.ComposeTestCheckFunc( testAccCheckRoute53ResolverFirewallDomainListExists(resourceName, &v), resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "domains.#", "1"), - resource.TestCheckTypeSetElemAttr(resourceName, "domains.*", "foo.com."), + resource.TestCheckTypeSetElemAttr(resourceName, "domains.*", domainName1), ), }, { @@ -120,12 +123,12 @@ func TestAccAWSRoute53ResolverFirewallDomainList_domains(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccRoute53ResolverFirewallDomainListConfigDomains(rName, "bar.com."), + Config: testAccRoute53ResolverFirewallDomainListConfigDomains(rName, domainName2), Check: resource.ComposeTestCheckFunc( testAccCheckRoute53ResolverFirewallDomainListExists(resourceName, &v), resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "domains.#", "1"), - resource.TestCheckTypeSetElemAttr(resourceName, "domains.*", "bar.com."), + resource.TestCheckTypeSetElemAttr(resourceName, "domains.*", domainName2), ), }, { diff --git a/aws/resource_aws_route53_zone_association_test.go b/aws/resource_aws_route53_zone_association_test.go index 9271b6f4b75..3152e20534d 100644 --- a/aws/resource_aws_route53_zone_association_test.go +++ b/aws/resource_aws_route53_zone_association_test.go @@ -11,7 +11,9 @@ import ( ) func TestAccAWSRoute53ZoneAssociation_basic(t *testing.T) { - resourceName := "aws_route53_zone_association.foobar" + resourceName := "aws_route53_zone_association.test" + + domainName := testAccRandomFQDomainName() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -20,7 +22,7 @@ func TestAccAWSRoute53ZoneAssociation_basic(t *testing.T) { CheckDestroy: testAccCheckRoute53ZoneAssociationDestroy, Steps: []resource.TestStep{ { - Config: testAccRoute53ZoneAssociationConfig, + Config: testAccRoute53ZoneAssociationConfig(domainName), Check: resource.ComposeTestCheckFunc( testAccCheckRoute53ZoneAssociationExists(resourceName), ), @@ -35,7 +37,9 @@ func TestAccAWSRoute53ZoneAssociation_basic(t *testing.T) { } func TestAccAWSRoute53ZoneAssociation_disappears(t *testing.T) { - resourceName := "aws_route53_zone_association.foobar" + resourceName := "aws_route53_zone_association.test" + + domainName := testAccRandomFQDomainName() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -44,7 +48,7 @@ func TestAccAWSRoute53ZoneAssociation_disappears(t *testing.T) { CheckDestroy: testAccCheckRoute53ZoneAssociationDestroy, Steps: []resource.TestStep{ { - Config: testAccRoute53ZoneAssociationConfig, + Config: testAccRoute53ZoneAssociationConfig(domainName), Check: resource.ComposeTestCheckFunc( testAccCheckRoute53ZoneAssociationExists(resourceName), testAccCheckResourceDisappears(testAccProvider, resourceAwsRoute53ZoneAssociation(), resourceName), @@ -56,9 +60,11 @@ func TestAccAWSRoute53ZoneAssociation_disappears(t *testing.T) { } func TestAccAWSRoute53ZoneAssociation_disappears_VPC(t *testing.T) { - resourceName := "aws_route53_zone_association.foobar" + resourceName := "aws_route53_zone_association.test" vpcResourceName := "aws_vpc.bar" + domainName := testAccRandomFQDomainName() + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, route53.EndpointsID), @@ -66,7 +72,7 @@ func TestAccAWSRoute53ZoneAssociation_disappears_VPC(t *testing.T) { CheckDestroy: testAccCheckRoute53ZoneAssociationDestroy, Steps: []resource.TestStep{ { - Config: testAccRoute53ZoneAssociationConfig, + Config: testAccRoute53ZoneAssociationConfig(domainName), Check: resource.ComposeTestCheckFunc( testAccCheckRoute53ZoneAssociationExists(resourceName), testAccCheckResourceDisappears(testAccProvider, resourceAwsVpc(), vpcResourceName), @@ -78,9 +84,11 @@ func TestAccAWSRoute53ZoneAssociation_disappears_VPC(t *testing.T) { } func TestAccAWSRoute53ZoneAssociation_disappears_Zone(t *testing.T) { - resourceName := "aws_route53_zone_association.foobar" + resourceName := "aws_route53_zone_association.test" route53ZoneResourceName := "aws_route53_zone.foo" + domainName := testAccRandomFQDomainName() + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, route53.EndpointsID), @@ -88,7 +96,7 @@ func TestAccAWSRoute53ZoneAssociation_disappears_Zone(t *testing.T) { CheckDestroy: testAccCheckRoute53ZoneAssociationDestroy, Steps: []resource.TestStep{ { - Config: testAccRoute53ZoneAssociationConfig, + Config: testAccRoute53ZoneAssociationConfig(domainName), Check: resource.ComposeTestCheckFunc( testAccCheckRoute53ZoneAssociationExists(resourceName), testAccCheckResourceDisappears(testAccProvider, resourceAwsRoute53Zone(), route53ZoneResourceName), @@ -106,6 +114,8 @@ func TestAccAWSRoute53ZoneAssociation_CrossAccount(t *testing.T) { // check for the instances in each region var providers []*schema.Provider + domainName := testAccRandomFQDomainName() + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) @@ -116,13 +126,13 @@ func TestAccAWSRoute53ZoneAssociation_CrossAccount(t *testing.T) { CheckDestroy: testAccCheckRoute53ZoneAssociationDestroy, Steps: []resource.TestStep{ { - Config: testAccRoute53ZoneAssociationCrossAccountConfig(), + Config: testAccRoute53ZoneAssociationCrossAccountConfig(domainName), Check: resource.ComposeTestCheckFunc( testAccCheckRoute53ZoneAssociationExists(resourceName), ), }, { - Config: testAccRoute53ZoneAssociationCrossAccountConfig(), + Config: testAccRoute53ZoneAssociationCrossAccountConfig(domainName), ResourceName: resourceName, ImportState: true, ImportStateVerify: true, @@ -138,6 +148,8 @@ func TestAccAWSRoute53ZoneAssociation_CrossRegion(t *testing.T) { // check for the instances in each region var providers []*schema.Provider + domainName := testAccRandomFQDomainName() + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) @@ -148,13 +160,13 @@ func TestAccAWSRoute53ZoneAssociation_CrossRegion(t *testing.T) { CheckDestroy: testAccCheckRoute53ZoneAssociationDestroy, Steps: []resource.TestStep{ { - Config: testAccRoute53ZoneAssociationRegionConfig(), + Config: testAccRoute53ZoneAssociationRegionConfig(domainName), Check: resource.ComposeTestCheckFunc( testAccCheckRoute53ZoneAssociationExists(resourceName), ), }, { - Config: testAccRoute53ZoneAssociationRegionConfig(), + Config: testAccRoute53ZoneAssociationRegionConfig(domainName), ResourceName: resourceName, ImportState: true, ImportStateVerify: true, @@ -226,7 +238,8 @@ func testAccCheckRoute53ZoneAssociationExists(resourceName string) resource.Test } } -const testAccRoute53ZoneAssociationConfig = ` +func testAccRoute53ZoneAssociationConfig(domainName string) string { + return fmt.Sprintf(` resource "aws_vpc" "foo" { cidr_block = "10.6.0.0/16" enable_dns_hostnames = true @@ -248,7 +261,7 @@ resource "aws_vpc" "bar" { } resource "aws_route53_zone" "foo" { - name = "foo.com" + name = %[1]q vpc { vpc_id = aws_vpc.foo.id @@ -259,16 +272,17 @@ resource "aws_route53_zone" "foo" { } } -resource "aws_route53_zone_association" "foobar" { +resource "aws_route53_zone_association" "test" { zone_id = aws_route53_zone.foo.id vpc_id = aws_vpc.bar.id } -` +`, domainName) +} -func testAccRoute53ZoneAssociationCrossAccountConfig() string { +func testAccRoute53ZoneAssociationCrossAccountConfig(domainName string) string { return composeConfig( testAccAlternateAccountProviderConfig(), - ` + fmt.Sprintf(` resource "aws_vpc" "alternate" { provider = "awsalternate" @@ -286,7 +300,7 @@ resource "aws_vpc" "test" { resource "aws_route53_zone" "test" { provider = "awsalternate" - name = "foo.com" + name = %[1]q vpc { vpc_id = aws_vpc.alternate.id @@ -308,13 +322,13 @@ resource "aws_route53_zone_association" "test" { vpc_id = aws_route53_vpc_association_authorization.test.vpc_id zone_id = aws_route53_vpc_association_authorization.test.zone_id } -`) +`, domainName)) } -func testAccRoute53ZoneAssociationRegionConfig() string { +func testAccRoute53ZoneAssociationRegionConfig(domainName string) string { return composeConfig( testAccMultipleRegionProviderConfig(2), - ` + fmt.Sprintf(` data "aws_region" "alternate" { provider = "awsalternate" } @@ -344,7 +358,7 @@ resource "aws_vpc" "alternate" { } resource "aws_route53_zone" "test" { - name = "foo.com" + name = %[1]q vpc { vpc_id = aws_vpc.test.id @@ -361,5 +375,5 @@ resource "aws_route53_zone_association" "test" { vpc_region = data.aws_region.alternate.name zone_id = aws_route53_zone.test.id } -`) +`, domainName)) } From d189f4b8e92f6bec6421bf29f061bc2383274199 Mon Sep 17 00:00:00 2001 From: Ian Dillon Date: Mon, 21 Sep 2020 09:29:34 -0400 Subject: [PATCH 259/398] r/aws_guardduty_organization_configuration: Adds datasource/s3_logs auto_enable attribute. --- ...ws_guardduty_organization_configuration.go | 90 ++++++++++++++++++- ...ardduty_organization_configuration_test.go | 71 +++++++++++++++ aws/resource_aws_guardduty_test.go | 3 +- ...y_organization_configuration.html.markdown | 16 ++++ 4 files changed, 177 insertions(+), 3 deletions(-) diff --git a/aws/resource_aws_guardduty_organization_configuration.go b/aws/resource_aws_guardduty_organization_configuration.go index 2a1857bb230..7ca8425503f 100644 --- a/aws/resource_aws_guardduty_organization_configuration.go +++ b/aws/resource_aws_guardduty_organization_configuration.go @@ -32,6 +32,30 @@ func resourceAwsGuardDutyOrganizationConfiguration() *schema.Resource { ForceNew: true, ValidateFunc: validation.NoZeroValues, }, + "datasources": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "s3_logs": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "auto_enable": { + Type: schema.TypeBool, + Required: true, + }, + }, + }, + }, + }, + }, + }, }, } } @@ -42,8 +66,9 @@ func resourceAwsGuardDutyOrganizationConfigurationUpdate(d *schema.ResourceData, detectorID := d.Get("detector_id").(string) input := &guardduty.UpdateOrganizationConfigurationInput{ - AutoEnable: aws.Bool(d.Get("auto_enable").(bool)), - DetectorId: aws.String(detectorID), + AutoEnable: aws.Bool(d.Get("auto_enable").(bool)), + DetectorId: aws.String(detectorID), + DataSources: expandOrganizationDatasourceConfig(d), } _, err := conn.UpdateOrganizationConfiguration(input) @@ -80,8 +105,69 @@ func resourceAwsGuardDutyOrganizationConfigurationRead(d *schema.ResourceData, m return fmt.Errorf("error reading GuardDuty Organization Configuration (%s): empty response", d.Id()) } + if err := d.Set("datasources", flattenOrganizationDatasourceConfig(output.DataSources)); err != nil { + return fmt.Errorf("error setting datasources: %s", err) + } + d.Set("detector_id", d.Id()) d.Set("auto_enable", output.AutoEnable) return nil } + +func flattenOrganizationDatasourceConfig(dsConfig *guardduty.OrganizationDataSourceConfigurationsResult) []interface{} { + if dsConfig == nil { + return []interface{}{} + } + + values := map[string]interface{}{} + + if v := dsConfig.S3Logs; v != nil { + values["s3_logs"] = flattenOrganizationS3LogsConfig(v) + } + + return []interface{}{values} +} + +func flattenOrganizationS3LogsConfig(s3LogsConfig *guardduty.OrganizationS3LogsConfigurationResult) []interface{} { + values := map[string]interface{}{} + + if s3LogsConfig == nil { + values["auto_enable"] = false + } else { + values["auto_enable"] = aws.BoolValue(s3LogsConfig.AutoEnable) + } + + return []interface{}{values} +} + +func expandOrganizationDatasourceConfig(d *schema.ResourceData) *guardduty.OrganizationDataSourceConfigurations { + dsConfig := &guardduty.OrganizationDataSourceConfigurations{} + + if v, ok := d.GetOk("datasources"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + configList := v.([]interface{}) + data := configList[0].(map[string]interface{}) + + if v, ok := data["s3_logs"]; ok { + dsConfig.S3Logs = expandOrganizationS3LogsConfig(v.([]interface{})) + } + } + + return dsConfig +} + +func expandOrganizationS3LogsConfig(configList []interface{}) *guardduty.OrganizationS3LogsConfiguration { + if len(configList) == 0 || configList[0] == nil { + return nil + } + + data := configList[0].(map[string]interface{}) + + autoEnable := data["auto_enable"].(bool) + + s3LogsConfig := &guardduty.OrganizationS3LogsConfiguration{ + AutoEnable: aws.Bool(autoEnable), + } + + return s3LogsConfig +} diff --git a/aws/resource_aws_guardduty_organization_configuration_test.go b/aws/resource_aws_guardduty_organization_configuration_test.go index 5d31055cf7b..007759e8bab 100644 --- a/aws/resource_aws_guardduty_organization_configuration_test.go +++ b/aws/resource_aws_guardduty_organization_configuration_test.go @@ -46,6 +46,43 @@ func testAccAwsGuardDutyOrganizationConfiguration_basic(t *testing.T) { }) } +func testAccAwsGuardDutyOrganizationConfiguration_s3logs(t *testing.T) { + detectorResourceName := "aws_guardduty_detector.test" + resourceName := "aws_guardduty_organization_configuration.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccOrganizationsAccountPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsGuardDutyDetectorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccGuardDutyOrganizationConfigurationConfigS3Logs(true), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "auto_enable", "true"), + resource.TestCheckResourceAttrPair(resourceName, "detector_id", detectorResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "datasources.0.s3_logs.0.auto_enable", "true"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccGuardDutyOrganizationConfigurationConfigS3Logs(false), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "auto_enable", "true"), + resource.TestCheckResourceAttrPair(resourceName, "detector_id", detectorResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "datasources.0.s3_logs.0.auto_enable", "false"), + ), + }, + }, + }) +} + func testAccGuardDutyOrganizationConfigurationConfigAutoEnable(autoEnable bool) string { return fmt.Sprintf(` data "aws_caller_identity" "current" {} @@ -73,3 +110,37 @@ resource "aws_guardduty_organization_configuration" "test" { } `, autoEnable) } + +func testAccGuardDutyOrganizationConfigurationConfigS3Logs(autoEnable bool) string { + return fmt.Sprintf(` +data "aws_caller_identity" "current" {} + +data "aws_partition" "current" {} + +resource "aws_organizations_organization" "test" { + aws_service_access_principals = ["guardduty.${data.aws_partition.current.dns_suffix}"] + feature_set = "ALL" +} + +resource "aws_guardduty_detector" "test" {} + +resource "aws_guardduty_organization_admin_account" "test" { + depends_on = [aws_organizations_organization.test] + + admin_account_id = data.aws_caller_identity.current.account_id +} + +resource "aws_guardduty_organization_configuration" "test" { + depends_on = [aws_guardduty_organization_admin_account.test] + + auto_enable = true + detector_id = aws_guardduty_detector.test.id + + datasources { + s3_logs { + auto_enable = %[1]t + } + } +} +`, autoEnable) +} diff --git a/aws/resource_aws_guardduty_test.go b/aws/resource_aws_guardduty_test.go index 596655cb7aa..fe042b6bea7 100644 --- a/aws/resource_aws_guardduty_test.go +++ b/aws/resource_aws_guardduty_test.go @@ -30,7 +30,8 @@ func TestAccAWSGuardDuty_serial(t *testing.T) { "basic": testAccAwsGuardDutyOrganizationAdminAccount_basic, }, "OrganizationConfiguration": { - "basic": testAccAwsGuardDutyOrganizationConfiguration_basic, + "basic": testAccAwsGuardDutyOrganizationConfiguration_basic, + "s3Logs": testAccAwsGuardDutyOrganizationConfiguration_s3logs, }, "ThreatIntelSet": { "basic": testAccAwsGuardDutyThreatintelset_basic, diff --git a/website/docs/r/guardduty_organization_configuration.html.markdown b/website/docs/r/guardduty_organization_configuration.html.markdown index b972f04cddb..6a44a467b31 100644 --- a/website/docs/r/guardduty_organization_configuration.html.markdown +++ b/website/docs/r/guardduty_organization_configuration.html.markdown @@ -22,6 +22,12 @@ resource "aws_guardduty_detector" "example" { resource "aws_guardduty_organization_configuration" "example" { auto_enable = true detector_id = aws_guardduty_detector.example.id + + datasources { + s3_logs { + auto_enable = true + } + } } ``` @@ -31,6 +37,16 @@ The following arguments are supported: * `auto_enable` - (Required) When this setting is enabled, all new accounts that are created in, or added to, the organization are added as a member accounts of the organization’s GuardDuty delegated administrator and GuardDuty is enabled in that AWS Region. * `detector_id` - (Required) The detector ID of the GuardDuty account. +* `datasources` - (Optional) Configuration for the collected datasources. + +`datasources` supports the following: + +* `s3_logs` - (Optional) Configuration for the builds to store logs to S3. + +`s3_logs` supports the following: + +* `auto_enable` - (Optional) Set to `true` if you want S3 data event logs to be automatically enabled for new members of the organization. Default: `false` + ## Attributes Reference From 95fd5a8bfe0823c4fed7704f5df0dc4b33f39a4e Mon Sep 17 00:00:00 2001 From: Ian Dillon Date: Mon, 21 Sep 2020 09:50:52 -0400 Subject: [PATCH 260/398] Correct tabs -> spaces in aws_guardduty_organization_configuration tests. --- ...esource_aws_guardduty_organization_configuration_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aws/resource_aws_guardduty_organization_configuration_test.go b/aws/resource_aws_guardduty_organization_configuration_test.go index 007759e8bab..378a041336e 100644 --- a/aws/resource_aws_guardduty_organization_configuration_test.go +++ b/aws/resource_aws_guardduty_organization_configuration_test.go @@ -137,9 +137,9 @@ resource "aws_guardduty_organization_configuration" "test" { detector_id = aws_guardduty_detector.test.id datasources { - s3_logs { - auto_enable = %[1]t - } + s3_logs { + auto_enable = %[1]t + } } } `, autoEnable) From d6a6983ee94e31f25a5c459be168d6e67b8514b5 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 9 Jul 2021 15:11:36 -0400 Subject: [PATCH 261/398] r/aws_guardduty_organization_configuration: More idiomatic expand/flatten functions. --- ...ws_guardduty_organization_configuration.go | 95 ++++++++++--------- 1 file changed, 52 insertions(+), 43 deletions(-) diff --git a/aws/resource_aws_guardduty_organization_configuration.go b/aws/resource_aws_guardduty_organization_configuration.go index 7ca8425503f..d9d8120eb11 100644 --- a/aws/resource_aws_guardduty_organization_configuration.go +++ b/aws/resource_aws_guardduty_organization_configuration.go @@ -26,12 +26,7 @@ func resourceAwsGuardDutyOrganizationConfiguration() *schema.Resource { Type: schema.TypeBool, Required: true, }, - "detector_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.NoZeroValues, - }, + "datasources": { Type: schema.TypeList, Optional: true, @@ -56,6 +51,13 @@ func resourceAwsGuardDutyOrganizationConfiguration() *schema.Resource { }, }, }, + + "detector_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.NoZeroValues, + }, }, } } @@ -66,9 +68,12 @@ func resourceAwsGuardDutyOrganizationConfigurationUpdate(d *schema.ResourceData, detectorID := d.Get("detector_id").(string) input := &guardduty.UpdateOrganizationConfigurationInput{ - AutoEnable: aws.Bool(d.Get("auto_enable").(bool)), - DetectorId: aws.String(detectorID), - DataSources: expandOrganizationDatasourceConfig(d), + AutoEnable: aws.Bool(d.Get("auto_enable").(bool)), + DetectorId: aws.String(detectorID), + } + + if v, ok := d.GetOk("datasources"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.DataSources = expandGuardDutyOrganizationDataSourceConfigurations(v.([]interface{})[0].(map[string]interface{})) } _, err := conn.UpdateOrganizationConfiguration(input) @@ -105,69 +110,73 @@ func resourceAwsGuardDutyOrganizationConfigurationRead(d *schema.ResourceData, m return fmt.Errorf("error reading GuardDuty Organization Configuration (%s): empty response", d.Id()) } - if err := d.Set("datasources", flattenOrganizationDatasourceConfig(output.DataSources)); err != nil { - return fmt.Errorf("error setting datasources: %s", err) + d.Set("auto_enable", output.AutoEnable) + + if output.DataSources != nil { + if err := d.Set("datasources", []interface{}{flattenGuardDutyOrganizationDataSourceConfigurationsResult(output.DataSources)}); err != nil { + return fmt.Errorf("error setting datasources: %w", err) + } + } else { + d.Set("datasources", nil) } d.Set("detector_id", d.Id()) - d.Set("auto_enable", output.AutoEnable) return nil } -func flattenOrganizationDatasourceConfig(dsConfig *guardduty.OrganizationDataSourceConfigurationsResult) []interface{} { - if dsConfig == nil { - return []interface{}{} +func expandGuardDutyOrganizationDataSourceConfigurations(tfMap map[string]interface{}) *guardduty.OrganizationDataSourceConfigurations { + if tfMap == nil { + return nil } - values := map[string]interface{}{} + apiObject := &guardduty.OrganizationDataSourceConfigurations{} - if v := dsConfig.S3Logs; v != nil { - values["s3_logs"] = flattenOrganizationS3LogsConfig(v) + if v, ok := tfMap["s3_logs"].([]interface{}); ok && len(v) > 0 { + apiObject.S3Logs = expandGuardDutyOrganizationS3LogsConfiguration(v[0].(map[string]interface{})) } - return []interface{}{values} + return apiObject } -func flattenOrganizationS3LogsConfig(s3LogsConfig *guardduty.OrganizationS3LogsConfigurationResult) []interface{} { - values := map[string]interface{}{} +func expandGuardDutyOrganizationS3LogsConfiguration(tfMap map[string]interface{}) *guardduty.OrganizationS3LogsConfiguration { + if tfMap == nil { + return nil + } + + apiObject := &guardduty.OrganizationS3LogsConfiguration{} - if s3LogsConfig == nil { - values["auto_enable"] = false - } else { - values["auto_enable"] = aws.BoolValue(s3LogsConfig.AutoEnable) + if v, ok := tfMap["auto_enable"].(bool); ok { + apiObject.AutoEnable = aws.Bool(v) } - return []interface{}{values} + return apiObject } -func expandOrganizationDatasourceConfig(d *schema.ResourceData) *guardduty.OrganizationDataSourceConfigurations { - dsConfig := &guardduty.OrganizationDataSourceConfigurations{} +func flattenGuardDutyOrganizationDataSourceConfigurationsResult(apiObject *guardduty.OrganizationDataSourceConfigurationsResult) map[string]interface{} { + if apiObject == nil { + return nil + } - if v, ok := d.GetOk("datasources"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - configList := v.([]interface{}) - data := configList[0].(map[string]interface{}) + tfMap := map[string]interface{}{} - if v, ok := data["s3_logs"]; ok { - dsConfig.S3Logs = expandOrganizationS3LogsConfig(v.([]interface{})) - } + if v := apiObject.S3Logs; v != nil { + tfMap["s3_logs"] = []interface{}{flattenGuardDutyOrganizationS3LogsConfigurationResult(v)} } - return dsConfig + return tfMap } -func expandOrganizationS3LogsConfig(configList []interface{}) *guardduty.OrganizationS3LogsConfiguration { - if len(configList) == 0 || configList[0] == nil { +func flattenGuardDutyOrganizationS3LogsConfigurationResult(apiObject *guardduty.OrganizationS3LogsConfigurationResult) map[string]interface{} { + if apiObject == nil { return nil } - data := configList[0].(map[string]interface{}) - - autoEnable := data["auto_enable"].(bool) + tfMap := map[string]interface{}{} - s3LogsConfig := &guardduty.OrganizationS3LogsConfiguration{ - AutoEnable: aws.Bool(autoEnable), + if v := apiObject.AutoEnable; v != nil { + tfMap["auto_enable"] = aws.BoolValue(v) } - return s3LogsConfig + return tfMap } From d3c3e3877d9b499002e009d0fcfc1764766d4569 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 9 Jul 2021 15:12:19 -0400 Subject: [PATCH 262/398] Add CHANGELOG entry. --- .changelog/15241.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/15241.txt diff --git a/.changelog/15241.txt b/.changelog/15241.txt new file mode 100644 index 00000000000..906ce6da03f --- /dev/null +++ b/.changelog/15241.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_guardduty_organization_configuration: Add `datasources` argument +``` \ No newline at end of file From 8f969a18805a0cad5096863e23465ead683ab5d6 Mon Sep 17 00:00:00 2001 From: Mark Krant Date: Thu, 13 May 2021 23:30:59 -0700 Subject: [PATCH 263/398] Add delete marker replication to s3 v2 config Noted by issue 16250. This adds the option to Enable or Disable delete marker replication when working with v2 S3 replication config. Note that the v1 replication config does not support the ability to toggle delete marker replication; it is always on by default. --- .changelog/19323.txt | 3 + aws/resource_aws_s3_bucket.go | 52 +++++++++++++++- aws/resource_aws_s3_bucket_test.go | 85 +++++++++++++++++++++++++- website/docs/r/s3_bucket.html.markdown | 9 +++ 4 files changed, 145 insertions(+), 4 deletions(-) create mode 100644 .changelog/19323.txt diff --git a/.changelog/19323.txt b/.changelog/19323.txt new file mode 100644 index 00000000000..5dac0534726 --- /dev/null +++ b/.changelog/19323.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_s3_bucket: Add the delete_marker_replication option to s3 bucket replication_configuration v2 rules +``` diff --git a/aws/resource_aws_s3_bucket.go b/aws/resource_aws_s3_bucket.go index 47c868260dc..839a64a46f0 100644 --- a/aws/resource_aws_s3_bucket.go +++ b/aws/resource_aws_s3_bucket.go @@ -524,6 +524,21 @@ func resourceAwsS3Bucket() *schema.Resource { }, }, }, + "delete_marker_replication": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "status": { + Type: schema.TypeString, + Optional: true, + Default: s3.DeleteMarkerReplicationStatusDisabled, + }, + }, + }, + }, }, }, }, @@ -2109,14 +2124,19 @@ func resourceAwsS3BucketReplicationConfigurationUpdate(s3conn *s3.S3, d *schema. } else { rcRule.Filter.Prefix = aws.String(filter["prefix"].(string)) } - rcRule.DeleteMarkerReplication = &s3.DeleteMarkerReplication{ - Status: aws.String(s3.DeleteMarkerReplicationStatusDisabled), - } + } else { // XML schema V1. rcRule.Prefix = aws.String(rr["prefix"].(string)) } + if d, ok := rr["delete_marker_replication"].([]interface{}); ok && len(d) > 0 && d[0] != nil { + dmr := d[0].(map[string]interface{}) + rcRule.DeleteMarkerReplication = &s3.DeleteMarkerReplication{ + Status: aws.String(dmr["status"].(string)), + } + } + rules = append(rules, rcRule) } @@ -2409,6 +2429,14 @@ func flattenAwsS3BucketReplicationConfiguration(r *s3.ReplicationConfiguration) t["filter"] = []interface{}{m} } + if dmr := v.DeleteMarkerReplication; dmr != nil { + m := map[string]interface{}{} + if dmr.Status != nil { + m["status"] = aws.StringValue(v.DeleteMarkerReplication.Status) + } + t["delete_marker_replication"] = []interface{}{m} + } + rules = append(rules, t) } m["rules"] = schema.NewSet(rulesHash, rules) @@ -2574,6 +2602,9 @@ func rulesHash(v interface{}) int { if v, ok := m["priority"]; ok { buf.WriteString(fmt.Sprintf("%d-", v.(int))) } + if v, ok := m["delete_marker_replication"].([]interface{}); ok && len(v) > 0 && v[0] != nil { + buf.WriteString(fmt.Sprintf("%d-", deleteMarkerReplicationHash(v[0]))) + } if v, ok := m["filter"].([]interface{}); ok && len(v) > 0 && v[0] != nil { buf.WriteString(fmt.Sprintf("%d-", replicationRuleFilterHash(v[0]))) } @@ -2597,6 +2628,21 @@ func replicationRuleFilterHash(v interface{}) int { return hashcode.String(buf.String()) } +func deleteMarkerReplicationHash(v interface{}) int { + var buf bytes.Buffer + m, ok := v.(map[string]interface{}) + + if !ok { + return 0 + } + + if v, ok := m["status"]; ok { + buf.WriteString(fmt.Sprintf("%s-", v.(string))) + } + + return hashcode.String(buf.String()) +} + func destinationHash(v interface{}) int { var buf bytes.Buffer m, ok := v.(map[string]interface{}) diff --git a/aws/resource_aws_s3_bucket_test.go b/aws/resource_aws_s3_bucket_test.go index a7e1a196f38..cf31272e75f 100644 --- a/aws/resource_aws_s3_bucket_test.go +++ b/aws/resource_aws_s3_bucket_test.go @@ -2100,7 +2100,7 @@ func TestAccAWSS3Bucket_ReplicationSchemaV2(t *testing.T) { CheckDestroy: testAccCheckWithProviders(testAccCheckAWSS3BucketDestroyWithProvider, &providers), Steps: []resource.TestStep{ { - Config: testAccAWSS3BucketConfigReplicationWithV2ConfigurationNoTags(rInt), + Config: testAccAWSS3BucketConfigReplicationWithV2ConfigurationDeleteMarkerReplicationDisabled(rInt), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketExistsWithProvider(resourceName, testAccAwsRegionProviderFunc(region, &providers)), resource.TestCheckResourceAttr(resourceName, "replication_configuration.#", "1"), @@ -2129,6 +2129,36 @@ func TestAccAWSS3Bucket_ReplicationSchemaV2(t *testing.T) { ), ), }, + { + Config: testAccAWSS3BucketConfigReplicationWithV2ConfigurationNoTags(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3BucketExistsWithProvider(resourceName, testAccAwsRegionProviderFunc(region, &providers)), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "replication_configuration.0.role", iamRoleResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rules.#", "1"), + testAccCheckAWSS3BucketExistsWithProvider("aws_s3_bucket.destination", testAccAwsRegionProviderFunc(alternateRegion, &providers)), + testAccCheckAWSS3BucketReplicationRules( + resourceName, + []*s3.ReplicationRule{ + { + ID: aws.String("foobar"), + Destination: &s3.Destination{ + Bucket: aws.String(fmt.Sprintf("arn:%s:s3:::tf-test-bucket-destination-%d", partition, rInt)), + StorageClass: aws.String(s3.ObjectStorageClassStandard), + }, + Status: aws.String(s3.ReplicationRuleStatusEnabled), + Filter: &s3.ReplicationRuleFilter{ + Prefix: aws.String("foo"), + }, + Priority: aws.Int64(0), + DeleteMarkerReplication: &s3.DeleteMarkerReplication{ + Status: aws.String(s3.DeleteMarkerReplicationStatusEnabled), + }, + }, + }, + ), + ), + }, { Config: testAccAWSS3BucketConfigReplicationWithV2ConfigurationNoTags(rInt), ResourceName: resourceName, @@ -4544,6 +4574,10 @@ resource "aws_s3_bucket" "bucket" { prefix = "testprefix" } + delete_marker_replication { + status = "Enabled" + } + destination { bucket = aws_s3_bucket.destination.arn storage_class = "STANDARD" @@ -4562,6 +4596,39 @@ resource "aws_s3_bucket" "destination" { `, rName, rNameDestination)) } +func testAccAWSS3BucketConfigReplicationWithV2ConfigurationDeleteMarkerReplicationDisabled(randInt int) string { + return testAccAWSS3BucketConfigReplicationBasic(randInt) + fmt.Sprintf(` +resource "aws_s3_bucket" "bucket" { + bucket = "tf-test-bucket-%[1]d" + acl = "private" + + versioning { + enabled = true + } + + replication_configuration { + role = aws_iam_role.role.arn + + rules { + id = "foobar" + status = "Enabled" + + filter { + prefix = "foo" + } + + delete_marker_replication {} + + destination { + bucket = aws_s3_bucket.destination.arn + storage_class = "STANDARD" + } + } + } +} +`, randInt) +} + func testAccAWSS3BucketConfigReplicationWithV2ConfigurationNoTags(randInt int) string { return testAccAWSS3BucketConfigReplicationBasic(randInt) + fmt.Sprintf(` resource "aws_s3_bucket" "bucket" { @@ -4583,6 +4650,10 @@ resource "aws_s3_bucket" "bucket" { prefix = "foo" } + delete_marker_replication { + status = "Enabled" + } + destination { bucket = aws_s3_bucket.destination.arn storage_class = "STANDARD" @@ -4618,6 +4689,10 @@ resource "aws_s3_bucket" "bucket" { } } + delete_marker_replication { + status = "Disabled" + } + destination { bucket = aws_s3_bucket.destination.arn storage_class = "STANDARD" @@ -4656,6 +4731,10 @@ resource "aws_s3_bucket" "bucket" { } } + delete_marker_replication { + status = "Disabled" + } + destination { bucket = aws_s3_bucket.destination.arn storage_class = "STANDARD" @@ -4691,6 +4770,10 @@ resource "aws_s3_bucket" "bucket" { } } + delete_marker_replication { + status = "Disabled" + } + destination { bucket = aws_s3_bucket.destination.arn storage_class = "STANDARD" diff --git a/website/docs/r/s3_bucket.html.markdown b/website/docs/r/s3_bucket.html.markdown index 3f15a936085..cf54e1f364c 100644 --- a/website/docs/r/s3_bucket.html.markdown +++ b/website/docs/r/s3_bucket.html.markdown @@ -436,6 +436,7 @@ The `rules` object supports the following: * `prefix` - (Optional) Object keyname prefix identifying one or more objects to which the rule applies. Must be less than or equal to 1024 characters in length. * `status` - (Required) The status of the rule. Either `Enabled` or `Disabled`. The rule is ignored if status is not Enabled. * `filter` - (Optional) Filter that identifies subset of objects to which the replication rule applies (documented below). +* `delete_marker_replication` - (Optional) Specifies whether delete markers are replicated (documented below). ~> **NOTE on `prefix` and `filter`:** Amazon S3's latest version of the replication configuration is V2, which includes the `filter` attribute for replication rules. With the `filter` attribute, you can specify object filters based on the object key prefix, tags, or both to scope the objects that the rule applies to. @@ -444,6 +445,10 @@ Replication configuration V1 supports filtering based on only the `prefix` attri * For a specific rule, `prefix` conflicts with `filter` * If any rule has `filter` specified then they all must * `priority` is optional (with a default value of `0`) but must be unique between multiple rules +* If a rule has `filter` then it **must** have a `delete_marker_replication` object +* If a rule has `prefix` then it **must not** have a `delete_marker_replication` object + +~> **NOTE:** Delete markers are always replicated when using `prefix` in a rule and is not configurable. The default behavior when using `filter` can be achieved by providing an empty configuration block `delete_marker_replication {}`. ~> **NOTE:** Replication to multiple destination buckets requires that `priority` is specified in the `rules` object. If the corresponding rule requires no filter, an empty configuration block `filter {}` must be specified. @@ -471,6 +476,10 @@ The `filter` object supports the following: * `tags` - (Optional) A map of tags that identifies subset of objects to which the rule applies. The rule applies only to objects having all the tags in its tagset. +The `delete_marker_replication` object supports the following: + +* `status` - (Optional) The delete marker replication status. Either `Enabled` or `Disabled`. Default `Disabled`. If `tags` is set in the `filter` object, then `status` must be set to `Disabled`. + The `server_side_encryption_configuration` object supports the following: * `rule` - (required) A single object for server-side encryption by default configuration. (documented below) From 40ed3a3322f5bfb1632d8e9cda2f0e82146d10a3 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 9 Jul 2021 16:38:14 -0400 Subject: [PATCH 264/398] Fix awsproviderlint 'XAT001: missing ErrorCheck'. --- aws/resource_aws_guardduty_organization_configuration_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/aws/resource_aws_guardduty_organization_configuration_test.go b/aws/resource_aws_guardduty_organization_configuration_test.go index 378a041336e..8bd7ec2bd31 100644 --- a/aws/resource_aws_guardduty_organization_configuration_test.go +++ b/aws/resource_aws_guardduty_organization_configuration_test.go @@ -55,6 +55,7 @@ func testAccAwsGuardDutyOrganizationConfiguration_s3logs(t *testing.T) { testAccPreCheck(t) testAccOrganizationsAccountPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, guardduty.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckAwsGuardDutyDetectorDestroy, Steps: []resource.TestStep{ From 1d2717fd1fe2855a4d790fb87bdca181efc6dfac Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Tue, 29 Jun 2021 16:58:48 -0700 Subject: [PATCH 265/398] Removes hardcoded "terraformtesting.com" domain name from acceptance tests --- ...e_aws_acmpca_certificate_authority_test.go | 29 +- aws/provider_test.go | 4 + aws/resource_aws_acm_certificate_test.go | 16 +- ...e_aws_acmpca_certificate_authority_test.go | 155 ++++--- aws/resource_aws_cognito_user_pool_test.go | 2 +- aws/resource_aws_db_instance_test.go | 436 +++++------------- aws/resource_aws_msk_cluster_test.go | 10 +- aws/resource_aws_ses_domain_dkim_test.go | 7 +- aws/resource_aws_ses_domain_identity_test.go | 13 +- ...s_ses_domain_identity_verification_test.go | 8 +- aws/resource_aws_ses_domain_mail_from_test.go | 28 +- aws/resource_aws_ses_email_identity_test.go | 13 +- ...ws_ses_identity_notification_topic_test.go | 6 +- aws/resource_aws_ses_identity_policy_test.go | 13 +- ...esource_aws_storagegateway_gateway_test.go | 52 ++- ..._aws_storagegateway_smb_file_share_test.go | 78 ++-- aws/test-fixtures/public-ssh-key.pub | 2 +- 17 files changed, 356 insertions(+), 516 deletions(-) diff --git a/aws/data_source_aws_acmpca_certificate_authority_test.go b/aws/data_source_aws_acmpca_certificate_authority_test.go index 8cf446889fc..63fbba10558 100644 --- a/aws/data_source_aws_acmpca_certificate_authority_test.go +++ b/aws/data_source_aws_acmpca_certificate_authority_test.go @@ -1,6 +1,7 @@ package aws import ( + "fmt" "regexp" "testing" @@ -12,6 +13,8 @@ func TestAccDataSourceAwsAcmpcaCertificateAuthority_basic(t *testing.T) { resourceName := "aws_acmpca_certificate_authority.test" datasourceName := "data.aws_acmpca_certificate_authority.test" + domainName := testAccRandomDomainName() + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, acmpca.EndpointsID), @@ -22,7 +25,7 @@ func TestAccDataSourceAwsAcmpcaCertificateAuthority_basic(t *testing.T) { ExpectError: regexp.MustCompile(`(AccessDeniedException|ResourceNotFoundException)`), }, { - Config: testAccDataSourceAwsAcmpcaCertificateAuthorityConfig_ARN, + Config: testAccDataSourceAwsAcmpcaCertificateAuthorityConfig_ARN(domainName), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrPair(datasourceName, "arn", resourceName, "arn"), resource.TestCheckResourceAttrPair(datasourceName, "certificate", resourceName, "certificate"), @@ -47,6 +50,8 @@ func TestAccDataSourceAwsAcmpcaCertificateAuthority_S3ObjectAcl(t *testing.T) { resourceName := "aws_acmpca_certificate_authority.test" datasourceName := "data.aws_acmpca_certificate_authority.test" + domainName := testAccRandomDomainName() + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, acmpca.EndpointsID), @@ -57,7 +62,7 @@ func TestAccDataSourceAwsAcmpcaCertificateAuthority_S3ObjectAcl(t *testing.T) { ExpectError: regexp.MustCompile(`(AccessDeniedException|ResourceNotFoundException)`), }, { - Config: testAccDataSourceAwsAcmpcaCertificateAuthorityConfigS3ObjectAcl_ARN, + Config: testAccDataSourceAwsAcmpcaCertificateAuthorityConfigS3ObjectAcl_ARN(domainName), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrPair(datasourceName, "arn", resourceName, "arn"), resource.TestCheckResourceAttrPair(datasourceName, "certificate", resourceName, "certificate"), @@ -82,7 +87,8 @@ func TestAccDataSourceAwsAcmpcaCertificateAuthority_S3ObjectAcl(t *testing.T) { }) } -const testAccDataSourceAwsAcmpcaCertificateAuthorityConfig_ARN = ` +func testAccDataSourceAwsAcmpcaCertificateAuthorityConfig_ARN(domainName string) string { + return fmt.Sprintf(` resource "aws_acmpca_certificate_authority" "wrong" { permanent_deletion_time_in_days = 7 @@ -91,7 +97,7 @@ resource "aws_acmpca_certificate_authority" "wrong" { signing_algorithm = "SHA512WITHRSA" subject { - common_name = "terraformtesting.com" + common_name = %[1]q } } } @@ -104,7 +110,7 @@ resource "aws_acmpca_certificate_authority" "test" { signing_algorithm = "SHA512WITHRSA" subject { - common_name = "terraformtesting.com" + common_name = %[1]q } } } @@ -112,9 +118,11 @@ resource "aws_acmpca_certificate_authority" "test" { data "aws_acmpca_certificate_authority" "test" { arn = aws_acmpca_certificate_authority.test.arn } -` +`, domainName) +} -const testAccDataSourceAwsAcmpcaCertificateAuthorityConfigS3ObjectAcl_ARN = ` +func testAccDataSourceAwsAcmpcaCertificateAuthorityConfigS3ObjectAcl_ARN(domainName string) string { + return fmt.Sprintf(` resource "aws_acmpca_certificate_authority" "wrong" { permanent_deletion_time_in_days = 7 @@ -123,7 +131,7 @@ resource "aws_acmpca_certificate_authority" "wrong" { signing_algorithm = "SHA512WITHRSA" subject { - common_name = "terraformtesting.com" + common_name = %[1]q } } } @@ -136,7 +144,7 @@ resource "aws_acmpca_certificate_authority" "test" { signing_algorithm = "SHA512WITHRSA" subject { - common_name = "terraformtesting.com" + common_name = %[1]q } } } @@ -144,7 +152,8 @@ resource "aws_acmpca_certificate_authority" "test" { data "aws_acmpca_certificate_authority" "test" { arn = aws_acmpca_certificate_authority.test.arn } -` +`, domainName) +} //lintignore:AWSAT003,AWSAT005 const testAccDataSourceAwsAcmpcaCertificateAuthorityConfig_NonExistent = ` diff --git a/aws/provider_test.go b/aws/provider_test.go index c7ac625ea75..f96c68c60fe 100644 --- a/aws/provider_test.go +++ b/aws/provider_test.go @@ -2327,6 +2327,10 @@ func (d domainName) FQDN() domainName { return domainName(fmt.Sprintf("%s.", d)) } +func (d domainName) String() string { + return string(d) +} + func testAccRandomDomain() domainName { return domainNameTestTopLevelDomain.RandomSubdomain() } diff --git a/aws/resource_aws_acm_certificate_test.go b/aws/resource_aws_acm_certificate_test.go index bed7258619a..d8858dfdc91 100644 --- a/aws/resource_aws_acm_certificate_test.go +++ b/aws/resource_aws_acm_certificate_test.go @@ -222,7 +222,9 @@ func TestAccAWSAcmCertificate_root(t *testing.T) { func TestAccAWSAcmCertificate_privateCert(t *testing.T) { certificateAuthorityResourceName := "aws_acmpca_certificate_authority.test" resourceName := "aws_acm_certificate.cert" - rName := acctest.RandomWithPrefix("tf-acc-test") + + commonName := testAccRandomDomain() + certificateDomainName := commonName.RandomSubdomain().String() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -231,10 +233,10 @@ func TestAccAWSAcmCertificate_privateCert(t *testing.T) { CheckDestroy: testAccCheckAcmCertificateDestroy, Steps: []resource.TestStep{ { - Config: testAccAcmCertificateConfig_privateCert(rName), + Config: testAccAcmCertificateConfig_privateCert(commonName.String(), certificateDomainName), Check: resource.ComposeTestCheckFunc( testAccMatchResourceAttrRegionalARN(resourceName, "arn", "acm", regexp.MustCompile("certificate/.+$")), - resource.TestCheckResourceAttr(resourceName, "domain_name", fmt.Sprintf("%s.terraformtesting.com", rName)), + resource.TestCheckResourceAttr(resourceName, "domain_name", certificateDomainName), resource.TestCheckResourceAttr(resourceName, "domain_validation_options.#", "0"), resource.TestCheckResourceAttr(resourceName, "status", acm.CertificateStatusFailed), // FailureReason: PCA_INVALID_STATE (PCA State: PENDING_CERTIFICATE) resource.TestCheckResourceAttr(resourceName, "subject_alternative_names.#", "0"), @@ -751,7 +753,7 @@ resource "aws_acm_certificate" "cert" { } -func testAccAcmCertificateConfig_privateCert(rName string) string { +func testAccAcmCertificateConfig_privateCert(commonName, certificateDomainName string) string { return fmt.Sprintf(` resource "aws_acmpca_certificate_authority" "test" { permanent_deletion_time_in_days = 7 @@ -762,16 +764,16 @@ resource "aws_acmpca_certificate_authority" "test" { signing_algorithm = "SHA512WITHRSA" subject { - common_name = "terraformtesting.com" + common_name = %[1]q } } } resource "aws_acm_certificate" "cert" { - domain_name = "%s.terraformtesting.com" + domain_name = %[2]q certificate_authority_arn = aws_acmpca_certificate_authority.test.arn } -`, rName) +`, commonName, certificateDomainName) } func testAccAcmCertificateConfig_subjectAlternativeNames(domainName, subjectAlternativeNames, validationMethod string) string { diff --git a/aws/resource_aws_acmpca_certificate_authority_test.go b/aws/resource_aws_acmpca_certificate_authority_test.go index 274be156278..3b001fc1795 100644 --- a/aws/resource_aws_acmpca_certificate_authority_test.go +++ b/aws/resource_aws_acmpca_certificate_authority_test.go @@ -86,6 +86,8 @@ func TestAccAwsAcmpcaCertificateAuthority_basic(t *testing.T) { var certificateAuthority acmpca.CertificateAuthority resourceName := "aws_acmpca_certificate_authority.test" + commonName := testAccRandomDomainName() + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, acmpca.EndpointsID), @@ -93,7 +95,7 @@ func TestAccAwsAcmpcaCertificateAuthority_basic(t *testing.T) { CheckDestroy: testAccCheckAwsAcmpcaCertificateAuthorityDestroy, Steps: []resource.TestStep{ { - Config: testAccAwsAcmpcaCertificateAuthorityConfig_Required, + Config: testAccAwsAcmpcaCertificateAuthorityConfig_Required(commonName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAcmpcaCertificateAuthorityExists(resourceName, &certificateAuthority), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "acm-pca", regexp.MustCompile(`certificate-authority/.+`)), @@ -101,7 +103,7 @@ func TestAccAwsAcmpcaCertificateAuthority_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "certificate_authority_configuration.0.key_algorithm", "RSA_4096"), resource.TestCheckResourceAttr(resourceName, "certificate_authority_configuration.0.signing_algorithm", "SHA512WITHRSA"), resource.TestCheckResourceAttr(resourceName, "certificate_authority_configuration.0.subject.#", "1"), - resource.TestCheckResourceAttr(resourceName, "certificate_authority_configuration.0.subject.0.common_name", "terraformtesting.com"), + resource.TestCheckResourceAttr(resourceName, "certificate_authority_configuration.0.subject.0.common_name", commonName), resource.TestCheckResourceAttr(resourceName, "certificate", ""), resource.TestCheckResourceAttr(resourceName, "certificate_chain", ""), resource.TestCheckResourceAttrSet(resourceName, "certificate_signing_request"), @@ -131,6 +133,8 @@ func TestAccAwsAcmpcaCertificateAuthority_disappears(t *testing.T) { var certificateAuthority acmpca.CertificateAuthority resourceName := "aws_acmpca_certificate_authority.test" + commonName := testAccRandomDomainName() + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, acmpca.EndpointsID), @@ -138,7 +142,7 @@ func TestAccAwsAcmpcaCertificateAuthority_disappears(t *testing.T) { CheckDestroy: testAccCheckAwsAcmpcaCertificateAuthorityDestroy, Steps: []resource.TestStep{ { - Config: testAccAwsAcmpcaCertificateAuthorityConfig_Required, + Config: testAccAwsAcmpcaCertificateAuthorityConfig_Required(commonName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAcmpcaCertificateAuthorityExists(resourceName, &certificateAuthority), testAccCheckResourceDisappears(testAccProvider, resourceAwsAcmpcaCertificateAuthority(), resourceName), @@ -230,6 +234,11 @@ func TestAccAwsAcmpcaCertificateAuthority_RevocationConfiguration_CrlConfigurati rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_acmpca_certificate_authority.test" + domain := testAccRandomDomain() + commonName := domain.String() + customCName := domain.Subdomain("crl").String() + customCName2 := domain.Subdomain("crl2").String() + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, acmpca.EndpointsID), @@ -238,12 +247,12 @@ func TestAccAwsAcmpcaCertificateAuthority_RevocationConfiguration_CrlConfigurati Steps: []resource.TestStep{ // Test creating revocation configuration on resource creation { - Config: testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_CustomCname(rName, "crl.terraformtesting.com"), + Config: testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_CustomCname(rName, commonName, customCName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAcmpcaCertificateAuthorityExists(resourceName, &certificateAuthority), resource.TestCheckResourceAttr(resourceName, "revocation_configuration.#", "1"), resource.TestCheckResourceAttr(resourceName, "revocation_configuration.0.crl_configuration.#", "1"), - resource.TestCheckResourceAttr(resourceName, "revocation_configuration.0.crl_configuration.0.custom_cname", "crl.terraformtesting.com"), + resource.TestCheckResourceAttr(resourceName, "revocation_configuration.0.crl_configuration.0.custom_cname", customCName), resource.TestCheckResourceAttr(resourceName, "revocation_configuration.0.crl_configuration.0.enabled", "true"), resource.TestCheckResourceAttr(resourceName, "revocation_configuration.0.crl_configuration.0.expiration_in_days", "1"), resource.TestCheckResourceAttr(resourceName, "revocation_configuration.0.crl_configuration.0.s3_bucket_name", rName), @@ -260,12 +269,12 @@ func TestAccAwsAcmpcaCertificateAuthority_RevocationConfiguration_CrlConfigurati }, // Test updating revocation configuration { - Config: testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_CustomCname(rName, "crl2.terraformtesting.com"), + Config: testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_CustomCname(rName, commonName, customCName2), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAcmpcaCertificateAuthorityExists(resourceName, &certificateAuthority), resource.TestCheckResourceAttr(resourceName, "revocation_configuration.#", "1"), resource.TestCheckResourceAttr(resourceName, "revocation_configuration.0.crl_configuration.#", "1"), - resource.TestCheckResourceAttr(resourceName, "revocation_configuration.0.crl_configuration.0.custom_cname", "crl2.terraformtesting.com"), + resource.TestCheckResourceAttr(resourceName, "revocation_configuration.0.crl_configuration.0.custom_cname", customCName2), resource.TestCheckResourceAttr(resourceName, "revocation_configuration.0.crl_configuration.0.enabled", "true"), resource.TestCheckResourceAttr(resourceName, "revocation_configuration.0.crl_configuration.0.expiration_in_days", "1"), resource.TestCheckResourceAttr(resourceName, "revocation_configuration.0.crl_configuration.0.s3_bucket_name", rName), @@ -273,7 +282,7 @@ func TestAccAwsAcmpcaCertificateAuthority_RevocationConfiguration_CrlConfigurati }, // Test removing custom cname on resource update { - Config: testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_Enabled(rName, true), + Config: testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_Enabled(rName, commonName, true), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAcmpcaCertificateAuthorityExists(resourceName, &certificateAuthority), resource.TestCheckResourceAttr(resourceName, "revocation_configuration.#", "1"), @@ -286,12 +295,12 @@ func TestAccAwsAcmpcaCertificateAuthority_RevocationConfiguration_CrlConfigurati }, // Test adding custom cname on resource update { - Config: testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_CustomCname(rName, "crl.terraformtesting.com"), + Config: testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_CustomCname(rName, commonName, customCName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAcmpcaCertificateAuthorityExists(resourceName, &certificateAuthority), resource.TestCheckResourceAttr(resourceName, "revocation_configuration.#", "1"), resource.TestCheckResourceAttr(resourceName, "revocation_configuration.0.crl_configuration.#", "1"), - resource.TestCheckResourceAttr(resourceName, "revocation_configuration.0.crl_configuration.0.custom_cname", "crl.terraformtesting.com"), + resource.TestCheckResourceAttr(resourceName, "revocation_configuration.0.crl_configuration.0.custom_cname", customCName), resource.TestCheckResourceAttr(resourceName, "revocation_configuration.0.crl_configuration.0.enabled", "true"), resource.TestCheckResourceAttr(resourceName, "revocation_configuration.0.crl_configuration.0.expiration_in_days", "1"), resource.TestCheckResourceAttr(resourceName, "revocation_configuration.0.crl_configuration.0.s3_bucket_name", rName), @@ -299,7 +308,7 @@ func TestAccAwsAcmpcaCertificateAuthority_RevocationConfiguration_CrlConfigurati }, // Test removing revocation configuration on resource update { - Config: testAccAwsAcmpcaCertificateAuthorityConfig_Required, + Config: testAccAwsAcmpcaCertificateAuthorityConfig_Required(commonName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAcmpcaCertificateAuthorityExists(resourceName, &certificateAuthority), resource.TestCheckResourceAttr(resourceName, "revocation_configuration.#", "1"), @@ -316,6 +325,8 @@ func TestAccAwsAcmpcaCertificateAuthority_RevocationConfiguration_CrlConfigurati rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_acmpca_certificate_authority.test" + commonName := testAccRandomDomainName() + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, acmpca.EndpointsID), @@ -324,7 +335,7 @@ func TestAccAwsAcmpcaCertificateAuthority_RevocationConfiguration_CrlConfigurati Steps: []resource.TestStep{ // Test creating revocation configuration on resource creation { - Config: testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_Enabled(rName, true), + Config: testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_Enabled(rName, commonName, true), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAcmpcaCertificateAuthorityExists(resourceName, &certificateAuthority), resource.TestCheckResourceAttr(resourceName, "revocation_configuration.#", "1"), @@ -346,7 +357,7 @@ func TestAccAwsAcmpcaCertificateAuthority_RevocationConfiguration_CrlConfigurati }, // Test disabling revocation configuration { - Config: testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_Enabled(rName, false), + Config: testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_Enabled(rName, commonName, false), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAcmpcaCertificateAuthorityExists(resourceName, &certificateAuthority), resource.TestCheckResourceAttr(resourceName, "revocation_configuration.#", "1"), @@ -356,7 +367,7 @@ func TestAccAwsAcmpcaCertificateAuthority_RevocationConfiguration_CrlConfigurati }, // Test enabling revocation configuration { - Config: testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_Enabled(rName, true), + Config: testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_Enabled(rName, commonName, true), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAcmpcaCertificateAuthorityExists(resourceName, &certificateAuthority), resource.TestCheckResourceAttr(resourceName, "revocation_configuration.#", "1"), @@ -369,7 +380,7 @@ func TestAccAwsAcmpcaCertificateAuthority_RevocationConfiguration_CrlConfigurati }, // Test removing revocation configuration on resource update { - Config: testAccAwsAcmpcaCertificateAuthorityConfig_Required, + Config: testAccAwsAcmpcaCertificateAuthorityConfig_Required(commonName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAcmpcaCertificateAuthorityExists(resourceName, &certificateAuthority), resource.TestCheckResourceAttr(resourceName, "revocation_configuration.#", "1"), @@ -386,6 +397,8 @@ func TestAccAwsAcmpcaCertificateAuthority_RevocationConfiguration_CrlConfigurati rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_acmpca_certificate_authority.test" + commonName := testAccRandomDomainName() + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, acmpca.EndpointsID), @@ -394,7 +407,7 @@ func TestAccAwsAcmpcaCertificateAuthority_RevocationConfiguration_CrlConfigurati Steps: []resource.TestStep{ // Test creating revocation configuration on resource creation { - Config: testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_ExpirationInDays(rName, 1), + Config: testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_ExpirationInDays(rName, commonName, 1), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAcmpcaCertificateAuthorityExists(resourceName, &certificateAuthority), resource.TestCheckResourceAttr(resourceName, "revocation_configuration.#", "1"), @@ -417,7 +430,7 @@ func TestAccAwsAcmpcaCertificateAuthority_RevocationConfiguration_CrlConfigurati }, // Test updating revocation configuration { - Config: testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_ExpirationInDays(rName, 2), + Config: testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_ExpirationInDays(rName, commonName, 2), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAcmpcaCertificateAuthorityExists(resourceName, &certificateAuthority), resource.TestCheckResourceAttr(resourceName, "revocation_configuration.#", "1"), @@ -429,7 +442,7 @@ func TestAccAwsAcmpcaCertificateAuthority_RevocationConfiguration_CrlConfigurati }, // Test removing revocation configuration on resource update { - Config: testAccAwsAcmpcaCertificateAuthorityConfig_Required, + Config: testAccAwsAcmpcaCertificateAuthorityConfig_Required(commonName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAcmpcaCertificateAuthorityExists(resourceName, &certificateAuthority), resource.TestCheckResourceAttr(resourceName, "revocation_configuration.#", "1"), @@ -446,6 +459,8 @@ func TestAccAwsAcmpcaCertificateAuthority_RevocationConfiguration_CrlConfigurati rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_acmpca_certificate_authority.test" + commonName := testAccRandomDomainName() + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, acmpca.EndpointsID), @@ -454,7 +469,7 @@ func TestAccAwsAcmpcaCertificateAuthority_RevocationConfiguration_CrlConfigurati Steps: []resource.TestStep{ // Test creating revocation configuration on resource creation { - Config: testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_s3ObjectAcl(rName, "BUCKET_OWNER_FULL_CONTROL"), + Config: testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_s3ObjectAcl(rName, commonName, "BUCKET_OWNER_FULL_CONTROL"), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAcmpcaCertificateAuthorityExists(resourceName, &certificateAuthority), resource.TestCheckResourceAttr(resourceName, "revocation_configuration.#", "1"), @@ -476,7 +491,7 @@ func TestAccAwsAcmpcaCertificateAuthority_RevocationConfiguration_CrlConfigurati }, // Test updating revocation configuration { - Config: testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_s3ObjectAcl(rName, "PUBLIC_READ"), + Config: testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_s3ObjectAcl(rName, commonName, "PUBLIC_READ"), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAcmpcaCertificateAuthorityExists(resourceName, &certificateAuthority), resource.TestCheckResourceAttr(resourceName, "revocation_configuration.#", "1"), @@ -495,6 +510,8 @@ func TestAccAwsAcmpcaCertificateAuthority_Tags(t *testing.T) { var certificateAuthority acmpca.CertificateAuthority resourceName := "aws_acmpca_certificate_authority.test" + commonName := testAccRandomDomainName() + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, acmpca.EndpointsID), @@ -502,7 +519,7 @@ func TestAccAwsAcmpcaCertificateAuthority_Tags(t *testing.T) { CheckDestroy: testAccCheckAwsAcmpcaCertificateAuthorityDestroy, Steps: []resource.TestStep{ { - Config: testAccAwsAcmpcaCertificateAuthorityConfig_Tags_Single, + Config: testAccAwsAcmpcaCertificateAuthorityConfig_Tags_Single(commonName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAcmpcaCertificateAuthorityExists(resourceName, &certificateAuthority), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), @@ -510,7 +527,7 @@ func TestAccAwsAcmpcaCertificateAuthority_Tags(t *testing.T) { ), }, { - Config: testAccAwsAcmpcaCertificateAuthorityConfig_Tags_SingleUpdated, + Config: testAccAwsAcmpcaCertificateAuthorityConfig_Tags_SingleUpdated(commonName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAcmpcaCertificateAuthorityExists(resourceName, &certificateAuthority), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), @@ -518,7 +535,7 @@ func TestAccAwsAcmpcaCertificateAuthority_Tags(t *testing.T) { ), }, { - Config: testAccAwsAcmpcaCertificateAuthorityConfig_Tags_Multiple, + Config: testAccAwsAcmpcaCertificateAuthorityConfig_Tags_Multiple(commonName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAcmpcaCertificateAuthorityExists(resourceName, &certificateAuthority), resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), @@ -527,7 +544,7 @@ func TestAccAwsAcmpcaCertificateAuthority_Tags(t *testing.T) { ), }, { - Config: testAccAwsAcmpcaCertificateAuthorityConfig_Tags_Single, + Config: testAccAwsAcmpcaCertificateAuthorityConfig_Tags_Single(commonName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAcmpcaCertificateAuthorityExists(resourceName, &certificateAuthority), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), @@ -751,23 +768,25 @@ data "aws_partition" "current" {} `, rName) } -const testAccAwsAcmpcaCertificateAuthorityConfig_Required = ` +func testAccAwsAcmpcaCertificateAuthorityConfig_Required(commonName string) string { + return fmt.Sprintf(` resource "aws_acmpca_certificate_authority" "test" { certificate_authority_configuration { key_algorithm = "RSA_4096" signing_algorithm = "SHA512WITHRSA" subject { - common_name = "terraformtesting.com" + common_name = %[1]q } } } -` - -func testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_CustomCname(rName, customCname string) string { - return fmt.Sprintf(` -%s +`, commonName) +} +func testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_CustomCname(rName, commonName, customCname string) string { + return composeConfig( + testAccAwsAcmpcaCertificateAuthorityConfig_S3Bucket(rName), + fmt.Sprintf(` resource "aws_acmpca_certificate_authority" "test" { permanent_deletion_time_in_days = 7 @@ -776,13 +795,13 @@ resource "aws_acmpca_certificate_authority" "test" { signing_algorithm = "SHA512WITHRSA" subject { - common_name = "terraformtesting.com" + common_name = %[1]q } } revocation_configuration { crl_configuration { - custom_cname = "%s" + custom_cname = %[2]q enabled = true expiration_in_days = 1 s3_bucket_name = aws_s3_bucket.test.id @@ -791,13 +810,13 @@ resource "aws_acmpca_certificate_authority" "test" { depends_on = [aws_s3_bucket_policy.test] } -`, testAccAwsAcmpcaCertificateAuthorityConfig_S3Bucket(rName), customCname) +`, commonName, customCname)) } -func testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_Enabled(rName string, enabled bool) string { - return fmt.Sprintf(` -%s - +func testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_Enabled(rName, commonName string, enabled bool) string { + return composeConfig( + testAccAwsAcmpcaCertificateAuthorityConfig_S3Bucket(rName), + fmt.Sprintf(` resource "aws_acmpca_certificate_authority" "test" { permanent_deletion_time_in_days = 7 @@ -806,25 +825,25 @@ resource "aws_acmpca_certificate_authority" "test" { signing_algorithm = "SHA512WITHRSA" subject { - common_name = "terraformtesting.com" + common_name = %[1]q } } revocation_configuration { crl_configuration { - enabled = %t + enabled = %[2]t expiration_in_days = 1 s3_bucket_name = aws_s3_bucket.test.id } } } -`, testAccAwsAcmpcaCertificateAuthorityConfig_S3Bucket(rName), enabled) +`, commonName, enabled)) } -func testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_ExpirationInDays(rName string, expirationInDays int) string { - return fmt.Sprintf(` -%s - +func testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_ExpirationInDays(rName, commonName string, expirationInDays int) string { + return composeConfig( + testAccAwsAcmpcaCertificateAuthorityConfig_S3Bucket(rName), + fmt.Sprintf(` resource "aws_acmpca_certificate_authority" "test" { permanent_deletion_time_in_days = 7 @@ -833,25 +852,25 @@ resource "aws_acmpca_certificate_authority" "test" { signing_algorithm = "SHA512WITHRSA" subject { - common_name = "terraformtesting.com" + common_name = %[1]q } } revocation_configuration { crl_configuration { enabled = true - expiration_in_days = %d + expiration_in_days = %[2]d s3_bucket_name = aws_s3_bucket.test.id } } } -`, testAccAwsAcmpcaCertificateAuthorityConfig_S3Bucket(rName), expirationInDays) +`, commonName, expirationInDays)) } -func testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_s3ObjectAcl(rName, s3ObjectAcl string) string { - return fmt.Sprintf(` -%s - +func testAccAwsAcmpcaCertificateAuthorityConfig_RevocationConfiguration_CrlConfiguration_s3ObjectAcl(rName, commonName, s3ObjectAcl string) string { + return composeConfig( + testAccAwsAcmpcaCertificateAuthorityConfig_S3Bucket(rName), + fmt.Sprintf(` resource "aws_acmpca_certificate_authority" "test" { permanent_deletion_time_in_days = 7 @@ -860,7 +879,7 @@ resource "aws_acmpca_certificate_authority" "test" { signing_algorithm = "SHA512WITHRSA" subject { - common_name = "terraformtesting.com" + common_name = %[1]q } } @@ -869,19 +888,19 @@ resource "aws_acmpca_certificate_authority" "test" { enabled = true expiration_in_days = 1 s3_bucket_name = aws_s3_bucket.test.id - s3_object_acl = "%s" + s3_object_acl = %[2]q } } depends_on = [aws_s3_bucket_policy.test] } -`, testAccAwsAcmpcaCertificateAuthorityConfig_S3Bucket(rName), s3ObjectAcl) +`, commonName, s3ObjectAcl)) } func testAccAwsAcmpcaCertificateAuthorityConfig_S3Bucket(rName string) string { return fmt.Sprintf(` resource "aws_s3_bucket" "test" { - bucket = "%s" + bucket = %[1]q force_destroy = true } @@ -913,7 +932,8 @@ resource "aws_s3_bucket_policy" "test" { `, rName) } -const testAccAwsAcmpcaCertificateAuthorityConfig_Tags_Single = ` +func testAccAwsAcmpcaCertificateAuthorityConfig_Tags_Single(commonName string) string { + return fmt.Sprintf(` resource "aws_acmpca_certificate_authority" "test" { permanent_deletion_time_in_days = 7 @@ -922,7 +942,7 @@ resource "aws_acmpca_certificate_authority" "test" { signing_algorithm = "SHA512WITHRSA" subject { - common_name = "terraformtesting.com" + common_name = %[1]q } } @@ -930,9 +950,11 @@ resource "aws_acmpca_certificate_authority" "test" { tag1 = "tag1value" } } -` +`, commonName) +} -const testAccAwsAcmpcaCertificateAuthorityConfig_Tags_SingleUpdated = ` +func testAccAwsAcmpcaCertificateAuthorityConfig_Tags_SingleUpdated(commonName string) string { + return fmt.Sprintf(` resource "aws_acmpca_certificate_authority" "test" { permanent_deletion_time_in_days = 7 @@ -941,7 +963,7 @@ resource "aws_acmpca_certificate_authority" "test" { signing_algorithm = "SHA512WITHRSA" subject { - common_name = "terraformtesting.com" + common_name = %[1]q } } @@ -949,9 +971,11 @@ resource "aws_acmpca_certificate_authority" "test" { tag1 = "tag1value-updated" } } -` +`, commonName) +} -const testAccAwsAcmpcaCertificateAuthorityConfig_Tags_Multiple = ` +func testAccAwsAcmpcaCertificateAuthorityConfig_Tags_Multiple(commonName string) string { + return fmt.Sprintf(` resource "aws_acmpca_certificate_authority" "test" { permanent_deletion_time_in_days = 7 @@ -960,7 +984,7 @@ resource "aws_acmpca_certificate_authority" "test" { signing_algorithm = "SHA512WITHRSA" subject { - common_name = "terraformtesting.com" + common_name = %[1]q } } @@ -969,4 +993,5 @@ resource "aws_acmpca_certificate_authority" "test" { tag2 = "tag2value" } } -` +`, commonName) +} diff --git a/aws/resource_aws_cognito_user_pool_test.go b/aws/resource_aws_cognito_user_pool_test.go index 126e59e7479..c872796f5db 100644 --- a/aws/resource_aws_cognito_user_pool_test.go +++ b/aws/resource_aws_cognito_user_pool_test.go @@ -790,7 +790,7 @@ func TestAccAWSCognitoUserPool_withEmailConfiguration(t *testing.T) { func TestAccAWSCognitoUserPool_withEmailConfigurationSource(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") - replyTo := fmt.Sprintf("tf-acc-reply-%s@terraformtesting.com", rName) + replyTo := "no-reply@hashicorp.com" resourceName := "aws_cognito_user_pool.test" resourceName2 := "aws_ses_configuration_set.test" diff --git a/aws/resource_aws_db_instance_test.go b/aws/resource_aws_db_instance_test.go index 22130346aeb..b3dd3290ef2 100644 --- a/aws/resource_aws_db_instance_test.go +++ b/aws/resource_aws_db_instance_test.go @@ -2217,7 +2217,11 @@ func TestAccAWSDBInstance_MSSQL_TZ(t *testing.T) { func TestAccAWSDBInstance_MSSQL_Domain(t *testing.T) { var vBefore, vAfter rds.DBInstance - rInt := acctest.RandInt() + rName := acctest.RandomWithPrefix("tf-acc-test") + + domain := testAccRandomDomain() + directory1 := domain.RandomSubdomain().String() + directory2 := domain.RandomSubdomain().String() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -2226,25 +2230,21 @@ func TestAccAWSDBInstance_MSSQL_Domain(t *testing.T) { CheckDestroy: testAccCheckAWSDBInstanceDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSDBInstanceConfig_MSSQLDomain(rInt), + Config: testAccAWSDBInstanceConfig_MSSQLDomain(rName, directory1, directory2), Check: resource.ComposeTestCheckFunc( testAccCheckAWSDBInstanceExists("aws_db_instance.mssql", &vBefore), - testAccCheckAWSDBInstanceDomainAttributes("terraformtesting.com", &vBefore), - resource.TestCheckResourceAttrSet( - "aws_db_instance.mssql", "domain"), - resource.TestCheckResourceAttrSet( - "aws_db_instance.mssql", "domain_iam_role_name"), + testAccCheckAWSDBInstanceDomainAttributes(directory1, &vBefore), + resource.TestCheckResourceAttrSet("aws_db_instance.mssql", "domain"), + resource.TestCheckResourceAttrSet("aws_db_instance.mssql", "domain_iam_role_name"), ), }, { - Config: testAccAWSDBInstanceConfig_MSSQLUpdateDomain(rInt), + Config: testAccAWSDBInstanceConfig_MSSQLUpdateDomain(rName, directory1, directory2), Check: resource.ComposeTestCheckFunc( testAccCheckAWSDBInstanceExists("aws_db_instance.mssql", &vAfter), - testAccCheckAWSDBInstanceDomainAttributes("corp.notexample.com", &vAfter), - resource.TestCheckResourceAttrSet( - "aws_db_instance.mssql", "domain"), - resource.TestCheckResourceAttrSet( - "aws_db_instance.mssql", "domain_iam_role_name"), + testAccCheckAWSDBInstanceDomainAttributes(directory2, &vAfter), + resource.TestCheckResourceAttrSet("aws_db_instance.mssql", "domain"), + resource.TestCheckResourceAttrSet("aws_db_instance.mssql", "domain_iam_role_name"), ), }, }, @@ -2253,7 +2253,9 @@ func TestAccAWSDBInstance_MSSQL_Domain(t *testing.T) { func TestAccAWSDBInstance_MSSQL_DomainSnapshotRestore(t *testing.T) { var v, vRestoredInstance rds.DBInstance - rInt := acctest.RandInt() + rName := acctest.RandomWithPrefix("tf-acc-test") + + domain := testAccRandomDomainName() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -2262,15 +2264,13 @@ func TestAccAWSDBInstance_MSSQL_DomainSnapshotRestore(t *testing.T) { CheckDestroy: testAccCheckAWSDBInstanceDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSDBInstanceConfig_MSSQLDomainSnapshotRestore(rInt), + Config: testAccAWSDBInstanceConfig_MSSQLDomainSnapshotRestore(rName, domain), Check: resource.ComposeTestCheckFunc( testAccCheckAWSDBInstanceExists("aws_db_instance.mssql_restore", &vRestoredInstance), testAccCheckAWSDBInstanceExists("aws_db_instance.mssql", &v), - testAccCheckAWSDBInstanceDomainAttributes("terraformtesting.com", &vRestoredInstance), - resource.TestCheckResourceAttrSet( - "aws_db_instance.mssql_restore", "domain"), - resource.TestCheckResourceAttrSet( - "aws_db_instance.mssql_restore", "domain_iam_role_name"), + testAccCheckAWSDBInstanceDomainAttributes(domain, &vRestoredInstance), + resource.TestCheckResourceAttrSet("aws_db_instance.mssql_restore", "domain"), + resource.TestCheckResourceAttrSet("aws_db_instance.mssql_restore", "domain_iam_role_name"), ), }, }, @@ -4240,307 +4240,175 @@ resource "aws_security_group_rule" "rds-mssql-1" { `, rInt)) } -func testAccAWSDBInstanceConfig_MSSQLDomain(rInt int) string { - return composeConfig( - testAccAWSDBInstanceConfig_orderableClassSQLServerEx(), - testAccAvailableAZsNoOptInConfig(), - fmt.Sprintf(` -resource "aws_vpc" "foo" { - cidr_block = "10.1.0.0/16" - enable_dns_hostnames = true +func testAccAWSDBInstanceConfig_RDSServiceRole(rName string) string { + return fmt.Sprintf(` +resource "aws_iam_role" "role" { + name = %[1]q - tags = { - Name = "terraform-testacc-db-instance-mssql-domain" - } + assume_role_policy = < Date: Tue, 29 Jun 2021 17:35:38 -0700 Subject: [PATCH 266/398] Adds constant for default email address --- aws/provider_test.go | 4 ++++ aws/resource_aws_cognito_user_pool_test.go | 2 +- aws/resource_aws_ses_email_identity_test.go | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/aws/provider_test.go b/aws/provider_test.go index f96c68c60fe..0a817b8dbcf 100644 --- a/aws/provider_test.go +++ b/aws/provider_test.go @@ -2334,3 +2334,7 @@ func (d domainName) String() string { func testAccRandomDomain() domainName { return domainNameTestTopLevelDomain.RandomSubdomain() } + +// testAccDefaultEmailAddress is the default email address to set as a +// resource or data source parameter for acceptance tests. +const testAccDefaultEmailAddress = "no-reply@hashicorp.com" diff --git a/aws/resource_aws_cognito_user_pool_test.go b/aws/resource_aws_cognito_user_pool_test.go index c872796f5db..aa2b256187e 100644 --- a/aws/resource_aws_cognito_user_pool_test.go +++ b/aws/resource_aws_cognito_user_pool_test.go @@ -790,7 +790,7 @@ func TestAccAWSCognitoUserPool_withEmailConfiguration(t *testing.T) { func TestAccAWSCognitoUserPool_withEmailConfigurationSource(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") - replyTo := "no-reply@hashicorp.com" + replyTo := testAccDefaultEmailAddress resourceName := "aws_cognito_user_pool.test" resourceName2 := "aws_ses_configuration_set.test" diff --git a/aws/resource_aws_ses_email_identity_test.go b/aws/resource_aws_ses_email_identity_test.go index 8e2ce53c759..50af0d57772 100644 --- a/aws/resource_aws_ses_email_identity_test.go +++ b/aws/resource_aws_ses_email_identity_test.go @@ -20,7 +20,7 @@ func init() { } func TestAccAWSSESEmailIdentity_basic(t *testing.T) { - email := "no-reply@hashicorp.com" + email := testAccDefaultEmailAddress resourceName := "aws_ses_email_identity.test" resource.ParallelTest(t, resource.TestCase{ @@ -46,7 +46,7 @@ func TestAccAWSSESEmailIdentity_basic(t *testing.T) { } func TestAccAWSSESEmailIdentity_trailingPeriod(t *testing.T) { - email := "no-reply@hashicorp.com" + email := testAccDefaultEmailAddress resourceName := "aws_ses_email_identity.test" resource.ParallelTest(t, resource.TestCase{ From 39e916d147b9e4057169f9607079ef8706fcbbe5 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Tue, 29 Jun 2021 17:35:49 -0700 Subject: [PATCH 267/398] Naming clean up --- ...urce_aws_acmpca_certificate_authority_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/aws/data_source_aws_acmpca_certificate_authority_test.go b/aws/data_source_aws_acmpca_certificate_authority_test.go index 63fbba10558..a74bff22ca6 100644 --- a/aws/data_source_aws_acmpca_certificate_authority_test.go +++ b/aws/data_source_aws_acmpca_certificate_authority_test.go @@ -13,7 +13,7 @@ func TestAccDataSourceAwsAcmpcaCertificateAuthority_basic(t *testing.T) { resourceName := "aws_acmpca_certificate_authority.test" datasourceName := "data.aws_acmpca_certificate_authority.test" - domainName := testAccRandomDomainName() + commonName := testAccRandomDomainName() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -25,7 +25,7 @@ func TestAccDataSourceAwsAcmpcaCertificateAuthority_basic(t *testing.T) { ExpectError: regexp.MustCompile(`(AccessDeniedException|ResourceNotFoundException)`), }, { - Config: testAccDataSourceAwsAcmpcaCertificateAuthorityConfig_ARN(domainName), + Config: testAccDataSourceAwsAcmpcaCertificateAuthorityConfig_ARN(commonName), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrPair(datasourceName, "arn", resourceName, "arn"), resource.TestCheckResourceAttrPair(datasourceName, "certificate", resourceName, "certificate"), @@ -50,7 +50,7 @@ func TestAccDataSourceAwsAcmpcaCertificateAuthority_S3ObjectAcl(t *testing.T) { resourceName := "aws_acmpca_certificate_authority.test" datasourceName := "data.aws_acmpca_certificate_authority.test" - domainName := testAccRandomDomainName() + commonName := testAccRandomDomainName() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -62,7 +62,7 @@ func TestAccDataSourceAwsAcmpcaCertificateAuthority_S3ObjectAcl(t *testing.T) { ExpectError: regexp.MustCompile(`(AccessDeniedException|ResourceNotFoundException)`), }, { - Config: testAccDataSourceAwsAcmpcaCertificateAuthorityConfigS3ObjectAcl_ARN(domainName), + Config: testAccDataSourceAwsAcmpcaCertificateAuthorityConfigS3ObjectAcl_ARN(commonName), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrPair(datasourceName, "arn", resourceName, "arn"), resource.TestCheckResourceAttrPair(datasourceName, "certificate", resourceName, "certificate"), @@ -87,7 +87,7 @@ func TestAccDataSourceAwsAcmpcaCertificateAuthority_S3ObjectAcl(t *testing.T) { }) } -func testAccDataSourceAwsAcmpcaCertificateAuthorityConfig_ARN(domainName string) string { +func testAccDataSourceAwsAcmpcaCertificateAuthorityConfig_ARN(commonName string) string { return fmt.Sprintf(` resource "aws_acmpca_certificate_authority" "wrong" { permanent_deletion_time_in_days = 7 @@ -118,10 +118,10 @@ resource "aws_acmpca_certificate_authority" "test" { data "aws_acmpca_certificate_authority" "test" { arn = aws_acmpca_certificate_authority.test.arn } -`, domainName) +`, commonName) } -func testAccDataSourceAwsAcmpcaCertificateAuthorityConfigS3ObjectAcl_ARN(domainName string) string { +func testAccDataSourceAwsAcmpcaCertificateAuthorityConfigS3ObjectAcl_ARN(commonName string) string { return fmt.Sprintf(` resource "aws_acmpca_certificate_authority" "wrong" { permanent_deletion_time_in_days = 7 @@ -152,7 +152,7 @@ resource "aws_acmpca_certificate_authority" "test" { data "aws_acmpca_certificate_authority" "test" { arn = aws_acmpca_certificate_authority.test.arn } -`, domainName) +`, commonName) } //lintignore:AWSAT003,AWSAT005 From b321ae2e2add94fb8e81b9fb4cc741592ee40720 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Wed, 30 Jun 2021 11:05:29 -0700 Subject: [PATCH 268/398] Resource naming cleanup --- aws/resource_aws_db_instance_test.go | 43 +++++++++++++++------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/aws/resource_aws_db_instance_test.go b/aws/resource_aws_db_instance_test.go index b3dd3290ef2..6760820584a 100644 --- a/aws/resource_aws_db_instance_test.go +++ b/aws/resource_aws_db_instance_test.go @@ -2217,6 +2217,7 @@ func TestAccAWSDBInstance_MSSQL_TZ(t *testing.T) { func TestAccAWSDBInstance_MSSQL_Domain(t *testing.T) { var vBefore, vAfter rds.DBInstance + resourceName := "aws_db_instance.test" rName := acctest.RandomWithPrefix("tf-acc-test") domain := testAccRandomDomain() @@ -2232,19 +2233,19 @@ func TestAccAWSDBInstance_MSSQL_Domain(t *testing.T) { { Config: testAccAWSDBInstanceConfig_MSSQLDomain(rName, directory1, directory2), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSDBInstanceExists("aws_db_instance.mssql", &vBefore), + testAccCheckAWSDBInstanceExists(resourceName, &vBefore), testAccCheckAWSDBInstanceDomainAttributes(directory1, &vBefore), - resource.TestCheckResourceAttrSet("aws_db_instance.mssql", "domain"), - resource.TestCheckResourceAttrSet("aws_db_instance.mssql", "domain_iam_role_name"), + resource.TestCheckResourceAttrSet(resourceName, "domain"), + resource.TestCheckResourceAttrSet(resourceName, "domain_iam_role_name"), ), }, { Config: testAccAWSDBInstanceConfig_MSSQLUpdateDomain(rName, directory1, directory2), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSDBInstanceExists("aws_db_instance.mssql", &vAfter), + testAccCheckAWSDBInstanceExists(resourceName, &vAfter), testAccCheckAWSDBInstanceDomainAttributes(directory2, &vAfter), - resource.TestCheckResourceAttrSet("aws_db_instance.mssql", "domain"), - resource.TestCheckResourceAttrSet("aws_db_instance.mssql", "domain_iam_role_name"), + resource.TestCheckResourceAttrSet(resourceName, "domain"), + resource.TestCheckResourceAttrSet(resourceName, "domain_iam_role_name"), ), }, }, @@ -2253,6 +2254,8 @@ func TestAccAWSDBInstance_MSSQL_Domain(t *testing.T) { func TestAccAWSDBInstance_MSSQL_DomainSnapshotRestore(t *testing.T) { var v, vRestoredInstance rds.DBInstance + resourceName := "aws_db_instance.test" + originResourceName := "aws_db_instance.origin" rName := acctest.RandomWithPrefix("tf-acc-test") domain := testAccRandomDomainName() @@ -2266,11 +2269,11 @@ func TestAccAWSDBInstance_MSSQL_DomainSnapshotRestore(t *testing.T) { { Config: testAccAWSDBInstanceConfig_MSSQLDomainSnapshotRestore(rName, domain), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSDBInstanceExists("aws_db_instance.mssql_restore", &vRestoredInstance), - testAccCheckAWSDBInstanceExists("aws_db_instance.mssql", &v), + testAccCheckAWSDBInstanceExists(resourceName, &vRestoredInstance), + testAccCheckAWSDBInstanceExists(originResourceName, &v), testAccCheckAWSDBInstanceDomainAttributes(domain, &vRestoredInstance), - resource.TestCheckResourceAttrSet("aws_db_instance.mssql_restore", "domain"), - resource.TestCheckResourceAttrSet("aws_db_instance.mssql_restore", "domain_iam_role_name"), + resource.TestCheckResourceAttrSet(resourceName, "domain"), + resource.TestCheckResourceAttrSet(resourceName, "domain_iam_role_name"), ), }, }, @@ -4368,7 +4371,7 @@ func testAccAWSDBInstanceConfig_MSSQLUpdateDomain(rName, directory1, directory2 return composeConfig( testAccAWSDBInstanceConfig_MSSQLDomain_SharedConfig(rName, directory1), fmt.Sprintf(` -resource "aws_db_instance" "mssql" { +resource "aws_db_instance" "test" { allocated_storage = 20 apply_immediately = true backup_retention_period = 0 @@ -4404,7 +4407,7 @@ func testAccAWSDBInstanceConfig_MSSQLDomainSnapshotRestore(rName, directory stri return composeConfig( testAccAWSDBInstanceConfig_MSSQLDomain_SharedConfig(rName, directory), fmt.Sprintf(` -resource "aws_db_instance" "mssql" { +resource "aws_db_instance" "origin" { allocated_storage = 20 engine = data.aws_rds_orderable_db_instance.test.engine engine_version = data.aws_rds_orderable_db_instance.test.engine_version @@ -4415,29 +4418,29 @@ resource "aws_db_instance" "mssql" { username = "somecrazyusername" } -resource "aws_db_snapshot" "mssql-snap" { - db_instance_identifier = aws_db_instance.mssql.id +resource "aws_db_snapshot" "origin" { + db_instance_identifier = aws_db_instance.origin.id db_snapshot_identifier = %[1]q } -resource "aws_db_instance" "mssql_restore" { +resource "aws_db_instance" "test" { allocated_storage = 20 apply_immediately = true backup_retention_period = 0 db_subnet_group_name = aws_db_subnet_group.test.name - engine = aws_db_instance.mssql.engine - engine_version = aws_db_instance.mssql.engine_version + engine = aws_db_instance.origin.engine + engine_version = aws_db_instance.origin.engine_version identifier = "%[1]s-restore" - instance_class = aws_db_instance.mssql.instance_class + instance_class = aws_db_instance.origin.instance_class password = "somecrazypassword" skip_final_snapshot = true username = "somecrazyusername" vpc_security_group_ids = [aws_security_group.test.id] - domain = aws_directory_service_directory.foo.id + domain = aws_directory_service_directory.directory.id domain_iam_role_name = aws_iam_role.role.name - snapshot_identifier = aws_db_snapshot.mssql-snap.id + snapshot_identifier = aws_db_snapshot.origin.id } `, rName)) } From 2b2fdecacd6fc1e6076adb806950f5f3b7e7133c Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Fri, 2 Jul 2021 15:34:22 -0700 Subject: [PATCH 269/398] Removes hardcoded domain from Route 53 delegation set data source --- ..._source_aws_route53_delegation_set_test.go | 24 +++++++++---------- ...esource_aws_route53_delegation_set_test.go | 10 ++++---- aws/resource_aws_route53_zone_test.go | 1 + 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/aws/data_source_aws_route53_delegation_set_test.go b/aws/data_source_aws_route53_delegation_set_test.go index 0f69e70e305..d3f217c8a57 100644 --- a/aws/data_source_aws_route53_delegation_set_test.go +++ b/aws/data_source_aws_route53_delegation_set_test.go @@ -1,6 +1,7 @@ package aws import ( + "fmt" "regexp" "testing" @@ -12,40 +13,37 @@ func TestAccAWSRoute53DelegationSetDataSource_basic(t *testing.T) { dataSourceName := "data.aws_route53_delegation_set.dset" resourceName := "aws_route53_delegation_set.dset" + zoneName := testAccRandomDomainName() + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, route53.EndpointsID), Providers: testAccProviders, Steps: []resource.TestStep{ { - Config: testAccAWSDataSourceAWSRoute53DelegationSetConfig_basic, + Config: testAccAWSDataSourceAWSRoute53DelegationSetConfig_basic(zoneName), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrPair( - dataSourceName, "name_servers.#", - resourceName, "name_servers.#", - ), - resource.TestMatchResourceAttr( - "data.aws_route53_delegation_set.dset", - "caller_reference", - regexp.MustCompile("DynDNS(.*)"), - ), + resource.TestCheckResourceAttrPair(dataSourceName, "name_servers.#", resourceName, "name_servers.#"), + resource.TestMatchResourceAttr("data.aws_route53_delegation_set.dset", "caller_reference", regexp.MustCompile("DynDNS(.*)")), ), }, }, }) } -const testAccAWSDataSourceAWSRoute53DelegationSetConfig_basic = ` +func testAccAWSDataSourceAWSRoute53DelegationSetConfig_basic(zoneName string) string { + return fmt.Sprintf(` resource "aws_route53_delegation_set" "dset" { reference_name = "DynDNS" } resource "aws_route53_zone" "primary" { - name = "example.xyz" + name = %[1]q delegation_set_id = aws_route53_delegation_set.dset.id } data "aws_route53_delegation_set" "dset" { id = aws_route53_delegation_set.dset.id } -` +`, zoneName) +} diff --git a/aws/resource_aws_route53_delegation_set_test.go b/aws/resource_aws_route53_delegation_set_test.go index f29310b795c..488ab57d5e2 100644 --- a/aws/resource_aws_route53_delegation_set_test.go +++ b/aws/resource_aws_route53_delegation_set_test.go @@ -43,6 +43,8 @@ func TestAccAWSRoute53DelegationSet_withZones(t *testing.T) { refName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_route53_delegation_set.test" + primaryZoneResourceName := "aws_route53_zone.primary" + secondaryZoneResourceName := "aws_route53_zone.secondary" domain := testAccRandomDomainName() zoneName1 := fmt.Sprintf("primary.%s", domain) @@ -58,10 +60,10 @@ func TestAccAWSRoute53DelegationSet_withZones(t *testing.T) { Config: testAccRoute53DelegationSetWithZonesConfig(refName, zoneName1, zoneName2), Check: resource.ComposeTestCheckFunc( testAccCheckRoute53DelegationSetExists(resourceName), - testAccCheckRoute53ZoneExists("aws_route53_zone.primary", &zone), - testAccCheckRoute53ZoneExists("aws_route53_zone.secondary", &zone), - testAccCheckRoute53NameServersMatch(resourceName, "aws_route53_zone.primary"), - testAccCheckRoute53NameServersMatch(resourceName, "aws_route53_zone.secondary"), + testAccCheckRoute53ZoneExists(primaryZoneResourceName, &zone), + testAccCheckRoute53ZoneExists(secondaryZoneResourceName, &zone), + testAccCheckRoute53NameServersMatch(resourceName, primaryZoneResourceName), + testAccCheckRoute53NameServersMatch(resourceName, secondaryZoneResourceName), ), }, { diff --git a/aws/resource_aws_route53_zone_test.go b/aws/resource_aws_route53_zone_test.go index f19b8275a85..07460c33f9b 100644 --- a/aws/resource_aws_route53_zone_test.go +++ b/aws/resource_aws_route53_zone_test.go @@ -682,6 +682,7 @@ func testAccCheckDomainName(zone *route53.GetHostedZoneOutput, domain string) re return fmt.Errorf("Invalid domain name. Expected %s is %s", domain, *zone.HostedZone.Name) } } + func testAccRoute53ZoneConfig(zoneName string) string { return fmt.Sprintf(` resource "aws_route53_zone" "test" { From 51d6bfb5d044f75c7ca8f3922c35398665a48298 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Fri, 2 Jul 2021 17:11:56 -0700 Subject: [PATCH 270/398] Fixes trailing-period SES acceptance test --- aws/resource_aws_ses_email_identity_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_ses_email_identity_test.go b/aws/resource_aws_ses_email_identity_test.go index 50af0d57772..7d0c64d25f3 100644 --- a/aws/resource_aws_ses_email_identity_test.go +++ b/aws/resource_aws_ses_email_identity_test.go @@ -46,7 +46,7 @@ func TestAccAWSSESEmailIdentity_basic(t *testing.T) { } func TestAccAWSSESEmailIdentity_trailingPeriod(t *testing.T) { - email := testAccDefaultEmailAddress + email := fmt.Sprintf("%s.", testAccDefaultEmailAddress) resourceName := "aws_ses_email_identity.test" resource.ParallelTest(t, resource.TestCase{ From 2812625f23616bf15dad2b4675016f784cb9d165 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Tue, 6 Jul 2021 10:49:26 -0700 Subject: [PATCH 271/398] Fixes resource name --- aws/resource_aws_db_instance_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_db_instance_test.go b/aws/resource_aws_db_instance_test.go index 6760820584a..d95d4017680 100644 --- a/aws/resource_aws_db_instance_test.go +++ b/aws/resource_aws_db_instance_test.go @@ -4336,7 +4336,7 @@ func testAccAWSDBInstanceConfig_MSSQLDomain(rName, directory1, directory2 string return composeConfig( testAccAWSDBInstanceConfig_MSSQLDomain_SharedConfig(rName, directory1), fmt.Sprintf(` -resource "aws_db_instance" "mssql" { +resource "aws_db_instance" "test" { allocated_storage = 20 backup_retention_period = 0 db_subnet_group_name = aws_db_subnet_group.test.name From 66433d3ccc8a2a28103ad29bcdae07955702746c Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 9 Jul 2021 16:54:30 -0400 Subject: [PATCH 272/398] Add CHANGELOG entry. --- .changelog/19954.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/19954.txt diff --git a/.changelog/19954.txt b/.changelog/19954.txt new file mode 100644 index 00000000000..1823e0216e5 --- /dev/null +++ b/.changelog/19954.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_guardduty_detector: Add `datasources` argument +``` \ No newline at end of file From 242f45c646cddd23bd6c8f78bc836bad29a4e035 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 9 Jul 2021 16:57:06 -0400 Subject: [PATCH 273/398] r/aws_guardduty_detector: More idiomatic expand/flatten functions. --- aws/resource_aws_guardduty_detector.go | 103 ++++++++++++++----------- 1 file changed, 56 insertions(+), 47 deletions(-) diff --git a/aws/resource_aws_guardduty_detector.go b/aws/resource_aws_guardduty_detector.go index cf9b6bd6da4..23866b0eb34 100644 --- a/aws/resource_aws_guardduty_detector.go +++ b/aws/resource_aws_guardduty_detector.go @@ -25,26 +25,15 @@ func resourceAwsGuardDutyDetector() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "enable": { - Type: schema.TypeBool, - Optional: true, - Default: true, - }, "account_id": { Type: schema.TypeString, Computed: true, }, + "arn": { Type: schema.TypeString, Computed: true, }, - // finding_publishing_frequency is marked as Computed:true since - // GuardDuty member accounts inherit setting from master account - "finding_publishing_frequency": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, "datasources": { Type: schema.TypeList, @@ -71,8 +60,21 @@ func resourceAwsGuardDutyDetector() *schema.Resource { }, }, - "tags": tagsSchema(), + "enable": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + // finding_publishing_frequency is marked as Computed:true since + // GuardDuty member accounts inherit setting from master account + "finding_publishing_frequency": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "tags": tagsSchema(), "tags_all": tagsSchemaComputed(), }, @@ -93,8 +95,8 @@ func resourceAwsGuardDutyDetectorCreate(d *schema.ResourceData, meta interface{} input.FindingPublishingFrequency = aws.String(v.(string)) } - if v, ok := d.GetOk("datasources"); ok { - input.DataSources = expandDataSourceConfigurations(v.([]interface{})) + if v, ok := d.GetOk("datasources"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.DataSources = expandGuardDutyDataSourceConfigurations(v.([]interface{})[0].(map[string]interface{})) } if len(tags) > 0 { @@ -141,13 +143,18 @@ func resourceAwsGuardDutyDetectorRead(d *schema.ResourceData, meta interface{}) d.Set("arn", arn) d.Set("account_id", meta.(*AWSClient).accountid) - d.Set("enable", *gdo.Status == guardduty.DetectorStatusEnabled) - d.Set("finding_publishing_frequency", gdo.FindingPublishingFrequency) - if err := d.Set("datasources", flattenDataSourceConfigurations(gdo.DataSources)); err != nil { - return fmt.Errorf("error setting datasources: %s", err) + if gdo.DataSources != nil { + if err := d.Set("datasources", []interface{}{flattenGuardDutyDataSourceConfigurationsResult(gdo.DataSources)}); err != nil { + return fmt.Errorf("error setting datasources: %w", err) + } + } else { + d.Set("datasources", nil) } + d.Set("enable", aws.StringValue(gdo.Status) == guardduty.DetectorStatusEnabled) + d.Set("finding_publishing_frequency", gdo.FindingPublishingFrequency) + tags := keyvaluetags.GuarddutyKeyValueTags(gdo.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig) //lintignore:AWSR002 @@ -165,7 +172,7 @@ func resourceAwsGuardDutyDetectorRead(d *schema.ResourceData, meta interface{}) func resourceAwsGuardDutyDetectorUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).guarddutyconn - if d.HasChanges("enable", "finding_publishing_frequency", "datasources") { + if d.HasChangesExcept("tags", "tags_all") { input := guardduty.UpdateDetectorInput{ DetectorId: aws.String(d.Id()), Enable: aws.Bool(d.Get("enable").(bool)), @@ -173,7 +180,7 @@ func resourceAwsGuardDutyDetectorUpdate(d *schema.ResourceData, meta interface{} } if d.HasChange("datasources") { - input.DataSources = expandDataSourceConfigurations(d.Get("datasources").([]interface{})) + input.DataSources = expandGuardDutyDataSourceConfigurations(d.Get("datasources").([]interface{})[0].(map[string]interface{})) } log.Printf("[DEBUG] Update GuardDuty Detector: %s", input) @@ -226,56 +233,58 @@ func resourceAwsGuardDutyDetectorDelete(d *schema.ResourceData, meta interface{} return nil } -func expandDataSourceConfigurations(dsc []interface{}) *guardduty.DataSourceConfigurations { - if len(dsc) < 1 || dsc[0] == nil { +func expandGuardDutyDataSourceConfigurations(tfMap map[string]interface{}) *guardduty.DataSourceConfigurations { + if tfMap == nil { return nil } - m := dsc[0].(map[string]interface{}) - - dataSourceConfigurations := &guardduty.DataSourceConfigurations{} + apiObject := &guardduty.DataSourceConfigurations{} - if v, ok := m["s3_logs"]; ok && v != "" && (len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil) { - dataSourceConfigurations.S3Logs = expandS3LogsConfiguration(v.([]interface{})) + if v, ok := tfMap["s3_logs"].([]interface{}); ok && len(v) > 0 { + apiObject.S3Logs = expandGuardDutyS3LogsConfiguration(v[0].(map[string]interface{})) } - return dataSourceConfigurations + return apiObject } -func expandS3LogsConfiguration(slc []interface{}) *guardduty.S3LogsConfiguration { - if len(slc) < 1 || slc[0] == nil { +func expandGuardDutyS3LogsConfiguration(tfMap map[string]interface{}) *guardduty.S3LogsConfiguration { + if tfMap == nil { return nil } - m := slc[0].(map[string]interface{}) + apiObject := &guardduty.S3LogsConfiguration{} - s3LogsConfiguration := &guardduty.S3LogsConfiguration{ - Enable: aws.Bool(m["enable"].(bool)), + if v, ok := tfMap["enable"].(bool); ok { + apiObject.Enable = aws.Bool(v) } - return s3LogsConfiguration + return apiObject } -func flattenDataSourceConfigurations(dsc *guardduty.DataSourceConfigurationsResult) []interface{} { - if dsc == nil { - return []interface{}{} +func flattenGuardDutyDataSourceConfigurationsResult(apiObject *guardduty.DataSourceConfigurationsResult) map[string]interface{} { + if apiObject == nil { + return nil } - m := map[string]interface{}{ - "s3_logs": flattenS3LogsConfiguration(dsc.S3Logs), + tfMap := map[string]interface{}{} + + if v := apiObject.S3Logs; v != nil { + tfMap["s3_logs"] = []interface{}{flattenGuardDutyS3LogsConfigurationResult(v)} } - return []interface{}{m} + return tfMap } -func flattenS3LogsConfiguration(slc *guardduty.S3LogsConfigurationResult) []interface{} { - if slc == nil { - return []interface{}{} +func flattenGuardDutyS3LogsConfigurationResult(apiObject *guardduty.S3LogsConfigurationResult) map[string]interface{} { + if apiObject == nil { + return nil } - m := map[string]interface{}{ - "enable": aws.StringValue(slc.Status) == guardduty.DataSourceStatusEnabled, + tfMap := map[string]interface{}{} + + if v := apiObject.Status; v != nil { + tfMap["enable"] = aws.StringValue(v) == guardduty.DataSourceStatusEnabled } - return []interface{}{m} + return tfMap } From 39f0e5aca7485e6abc21d0f918b706c4c2e73754 Mon Sep 17 00:00:00 2001 From: changelogbot Date: Fri, 9 Jul 2021 21:31:22 +0000 Subject: [PATCH 274/398] Update CHANGELOG.md for #19859 --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1588887e975..2002c23ec12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,15 @@ FEATURES: ENHANCEMENTS: * resource/aws_cognito_user_pool_client: Add the `enable_token_revocation` argument to support targeted sign out ([#20031](https://github.com/hashicorp/terraform-provider-aws/issues/20031)) +* resource/aws_guardduty_detector: Add `datasources` argument ([#19954](https://github.com/hashicorp/terraform-provider-aws/issues/19954)) * resource/fsx_windows_file_system: Add `aliases` argument. ([#20054](https://github.com/hashicorp/terraform-provider-aws/issues/20054)) BUG FIXES: * resource/aws_cognito_user_pool_client: Allow the `default_redirect_uri` argument value to be an empty string ([#20031](https://github.com/hashicorp/terraform-provider-aws/issues/20031)) * resource/aws_cognito_user_pool_client: Retry on `ConcurrentModificationException` ([#20031](https://github.com/hashicorp/terraform-provider-aws/issues/20031)) +* resource/aws_datasync_location_s3: Correctly parse S3 on Outposts location URI ([#19859](https://github.com/hashicorp/terraform-provider-aws/issues/19859)) +* resource/aws_db_instance: Ignore allocated_storage for replica at creation time ([#12548](https://github.com/hashicorp/terraform-provider-aws/issues/12548)) ## 3.49.0 (July 08, 2021) From 7d02775b17d868b039f5a0ddba3beb91cb163471 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 9 Jul 2021 17:34:17 -0400 Subject: [PATCH 275/398] Correct resource name in CHANGELOG entry --- .changelog/20054.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changelog/20054.txt b/.changelog/20054.txt index 17613fbece0..158d54ba1be 100644 --- a/.changelog/20054.txt +++ b/.changelog/20054.txt @@ -1,3 +1,3 @@ ```release-note:enhancement -resource/fsx_windows_file_system: Add `aliases` argument. +resource/aws_fsx_windows_file_system: Add `aliases` argument ``` From e7ded777058dbb3048ab222f85f941fd5fa53796 Mon Sep 17 00:00:00 2001 From: changelogbot Date: Fri, 9 Jul 2021 21:36:46 +0000 Subject: [PATCH 276/398] Update CHANGELOG.md for #20124 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2002c23ec12..7c4e572644d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,8 @@ FEATURES: ENHANCEMENTS: * resource/aws_cognito_user_pool_client: Add the `enable_token_revocation` argument to support targeted sign out ([#20031](https://github.com/hashicorp/terraform-provider-aws/issues/20031)) +* resource/aws_fsx_windows_file_system: Add `aliases` argument ([#20054](https://github.com/hashicorp/terraform-provider-aws/issues/20054)) * resource/aws_guardduty_detector: Add `datasources` argument ([#19954](https://github.com/hashicorp/terraform-provider-aws/issues/19954)) -* resource/fsx_windows_file_system: Add `aliases` argument. ([#20054](https://github.com/hashicorp/terraform-provider-aws/issues/20054)) BUG FIXES: From 8a5033ef9d9637afdaf767d30da28ae6ba6ec2e5 Mon Sep 17 00:00:00 2001 From: Muhammad Panji Date: Sun, 11 Jul 2021 05:40:54 +0700 Subject: [PATCH 277/398] Fix typo in glue_catalog_table (#19921) --- website/docs/r/glue_catalog_table.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/glue_catalog_table.html.markdown b/website/docs/r/glue_catalog_table.html.markdown index 48dcd2d2c37..415d0ea9a7c 100644 --- a/website/docs/r/glue_catalog_table.html.markdown +++ b/website/docs/r/glue_catalog_table.html.markdown @@ -96,7 +96,7 @@ The follow arguments are optional: * `partition_index` - (Optional) Configuration block for a maximum of 3 partition indexes. See [`partition_index`](#partition_index) below. * `partition_keys` - (Optional) Configuration block of columns by which the table is partitioned. Only primitive types are supported as partition keys. See [`partition_keys`](#partition_keys) below. * `retention` - (Optional) Retention time for this table. -* `storage_descriptor` - (Optional) Configuration block for information about the physical storage of this table. For more information, refer to the [Glue Developer Guide](https://docs.aws.amazon.com/glue/latest/dg/aws-glue-api-catalog-tables. html#aws-glue-api-catalog-tables-StorageDescriptor). See [`storage_descriptor`](#storage_descriptor) below. +* `storage_descriptor` - (Optional) Configuration block for information about the physical storage of this table. For more information, refer to the [Glue Developer Guide](https://docs.aws.amazon.com/glue/latest/dg/aws-glue-api-catalog-tables.html#aws-glue-api-catalog-tables-StorageDescriptor). See [`storage_descriptor`](#storage_descriptor) below. * `table_type` - (Optional) Type of this table (EXTERNAL_TABLE, VIRTUAL_VIEW, etc.). While optional, some Athena DDL queries such as `ALTER TABLE` and `SHOW CREATE TABLE` will fail if this argument is empty. * `target_table` - (Optional) Configuration block of a target table for resource linking. See [`target_table`](#target_table) below. * `view_expanded_text` - (Optional) If the table is a view, the expanded text of the view; otherwise null. From fec543cf1de0b2299f2b868c441b1ff6d20cf18a Mon Sep 17 00:00:00 2001 From: Muhammad Panji Date: Sun, 11 Jul 2021 05:49:10 +0700 Subject: [PATCH 278/398] Add reference to AWS documentation for schedule_expression (#20060) --- website/docs/r/cloudwatch_event_rule.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/cloudwatch_event_rule.html.markdown b/website/docs/r/cloudwatch_event_rule.html.markdown index 31a578ca538..000e8e24b36 100644 --- a/website/docs/r/cloudwatch_event_rule.html.markdown +++ b/website/docs/r/cloudwatch_event_rule.html.markdown @@ -64,7 +64,7 @@ The following arguments are supported: * `name` - (Optional) The name of the rule. If omitted, Terraform will assign a random, unique name. Conflicts with `name_prefix`. * `name_prefix` - (Optional) Creates a unique name beginning with the specified prefix. Conflicts with `name`. -* `schedule_expression` - (Optional) The scheduling expression. For example, `cron(0 20 * * ? *)` or `rate(5 minutes)`. At least one of `schedule_expression` or `event_pattern` is required. Can only be used on the default event bus. +* `schedule_expression` - (Optional) The scheduling expression. For example, `cron(0 20 * * ? *)` or `rate(5 minutes)`. At least one of `schedule_expression` or `event_pattern` is required. Can only be used on the default event bus. For more information, refer to the AWS documentation [Schedule Expressions for Rules](https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/ScheduledEvents.html). * `event_bus_name` - (Optional) The event bus to associate with this rule. If you omit this, the `default` event bus is used. * `event_pattern` - (Optional) The event pattern described a JSON object. At least one of `schedule_expression` or `event_pattern` is required. See full documentation of [Events and Event Patterns in EventBridge](https://docs.aws.amazon.com/eventbridge/latest/userguide/eventbridge-and-event-patterns.html) for details. * `description` - (Optional) The description of the rule. From 4dd0e53392111e3f435a24b97d3d2409d3e22838 Mon Sep 17 00:00:00 2001 From: Muhammad Panji Date: Sun, 11 Jul 2021 06:23:41 +0700 Subject: [PATCH 279/398] Add more info for from_address in aws_pinpoint_email_channel (#18050) --- website/docs/r/pinpoint_email_channel.markdown | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/website/docs/r/pinpoint_email_channel.markdown b/website/docs/r/pinpoint_email_channel.markdown index 95a43627952..4a1bbd2c685 100644 --- a/website/docs/r/pinpoint_email_channel.markdown +++ b/website/docs/r/pinpoint_email_channel.markdown @@ -16,7 +16,6 @@ Provides a Pinpoint Email Channel resource. resource "aws_pinpoint_email_channel" "email" { application_id = aws_pinpoint_app.app.application_id from_address = "user@example.com" - identity = aws_ses_domain_identity.identity.arn role_arn = aws_iam_role.role.arn } @@ -74,7 +73,7 @@ The following arguments are supported: * `application_id` - (Required) The application ID. * `enabled` - (Optional) Whether the channel is enabled or disabled. Defaults to `true`. * `configuration_set` - (Optional) The ARN of the Amazon SES configuration set that you want to apply to messages that you send through the channel. -* `from_address` - (Required) The email address used to send emails from. +* `from_address` - (Required) The email address used to send emails from. You can use email only (`user@example.com`) or friendly address (`User `). This field comply with [RFC 5322](https://www.ietf.org/rfc/rfc5322.txt). * `identity` - (Required) The ARN of an identity verified with SES. * `role_arn` - (Optional) The ARN of an IAM Role used to submit events to Mobile Analytics' event ingestion service. From f8914c38ade79ed049b4ec103161c3225ec5d49c Mon Sep 17 00:00:00 2001 From: LCStephens Date: Sun, 11 Jul 2021 10:31:09 -0500 Subject: [PATCH 280/398] add import documentation for aws_cloudfront_cache_policy --- website/docs/r/cloudfront_cache_policy.html.markdown | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/website/docs/r/cloudfront_cache_policy.html.markdown b/website/docs/r/cloudfront_cache_policy.html.markdown index 5f8de3c9744..f1e913a86b5 100644 --- a/website/docs/r/cloudfront_cache_policy.html.markdown +++ b/website/docs/r/cloudfront_cache_policy.html.markdown @@ -91,3 +91,11 @@ In addition to all arguments above, the following attributes are exported: * `etag` - The current version of the cache policy. * `id` - The identifier for the cache policy. + +## Import + +Cloudfront Cache Policies can be imported using the `id`, e.g. + +``` +$ terraform import aws_cloudfront_cache_policy.policy 658327ea-f89d-4fab-a63d-7e88639e58f6 +``` \ No newline at end of file From ccff872e7e1df8cb01119542c44ac80c303f040c Mon Sep 17 00:00:00 2001 From: Raksit Mantanacharu Date: Mon, 12 Jul 2021 00:14:33 +0700 Subject: [PATCH 281/398] docs/r/lex_intent: add name in example usage --- website/docs/r/lex_intent.html.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/website/docs/r/lex_intent.html.markdown b/website/docs/r/lex_intent.html.markdown index 07732ca168c..bf02585efcb 100644 --- a/website/docs/r/lex_intent.html.markdown +++ b/website/docs/r/lex_intent.html.markdown @@ -25,6 +25,7 @@ resource "aws_lex_intent" "order_flowers_intent" { } create_version = false + name = "OrderFlowers" description = "Intent to order a bouquet of flowers for pick up" fulfillment_activity { From 78067375b695ac0d20603090ec67afcb7f0cec87 Mon Sep 17 00:00:00 2001 From: Phil Nichol <35630607+philnichol@users.noreply.github.com> Date: Mon, 12 Jul 2021 00:05:18 +0100 Subject: [PATCH 282/398] updated aws_vpc_endpoint policy documentation to mention that a JSON-formatted string is required --- website/docs/r/vpc_endpoint.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/vpc_endpoint.html.markdown b/website/docs/r/vpc_endpoint.html.markdown index 368dd73fa2d..32553ed50da 100644 --- a/website/docs/r/vpc_endpoint.html.markdown +++ b/website/docs/r/vpc_endpoint.html.markdown @@ -116,7 +116,7 @@ The following arguments are supported: * `service_name` - (Required) The service name. For AWS services the service name is usually in the form `com.amazonaws..` (the SageMaker Notebook service is an exception to this rule, the service name is in the form `aws.sagemaker..notebook`). * `vpc_id` - (Required) The ID of the VPC in which the endpoint will be used. * `auto_accept` - (Optional) Accept the VPC endpoint (the VPC endpoint and service need to be in the same AWS account). -* `policy` - (Optional) A policy to attach to the endpoint that controls access to the service. Defaults to full access. All `Gateway` and some `Interface` endpoints support policies - see the [relevant AWS documentation](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-endpoints-access.html) for more details. For more information about building AWS IAM policy documents with Terraform, see the [AWS IAM Policy Document Guide](https://learn.hashicorp.com/terraform/aws/iam-policy). +* `policy` - (Optional) A policy to attach to the endpoint that controls access to the service. This is a JSON formatted string. Defaults to full access. All `Gateway` and some `Interface` endpoints support policies - see the [relevant AWS documentation](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-endpoints-access.html) for more details. For more information about building AWS IAM policy documents with Terraform, see the [AWS IAM Policy Document Guide](https://learn.hashicorp.com/terraform/aws/iam-policy). * `private_dns_enabled` - (Optional; AWS services and AWS Marketplace partner services only) Whether or not to associate a private hosted zone with the specified VPC. Applicable for endpoints of type `Interface`. Defaults to `false`. * `route_table_ids` - (Optional) One or more route table IDs. Applicable for endpoints of type `Gateway`. From 876c4be9519463df40dbfcc3c07680bc2a87adf2 Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Sun, 11 Jul 2021 21:22:56 -0400 Subject: [PATCH 283/398] * Add Default Tags Support * Update documentation * Add test coverage for resource updates * refactor expand/flatten methods --- .changelog/18032.txt | 3 - .changelog/19307.txt | 4 + aws/resource_aws_appconfig_application.go | 121 ++--- ...resource_aws_appconfig_application_test.go | 154 +++++-- aws/resource_aws_appconfig_environment.go | 308 +++++++++---- ...resource_aws_appconfig_environment_test.go | 434 +++++++++++++----- website/docs/index.html.markdown | 2 + .../r/appconfig_application.html.markdown | 24 +- .../r/appconfig_environment.html.markdown | 60 +-- 9 files changed, 753 insertions(+), 357 deletions(-) delete mode 100644 .changelog/18032.txt diff --git a/.changelog/18032.txt b/.changelog/18032.txt deleted file mode 100644 index bfbca0898be..00000000000 --- a/.changelog/18032.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:new-resource -aws_appconfig_application -``` \ No newline at end of file diff --git a/.changelog/19307.txt b/.changelog/19307.txt index 446c1c8af69..2c375c1a0d9 100644 --- a/.changelog/19307.txt +++ b/.changelog/19307.txt @@ -1,3 +1,7 @@ +```release-note:new-resource +aws_appconfig_application +``` + ```release-note:new-resource aws_appconfig_environment ``` diff --git a/aws/resource_aws_appconfig_application.go b/aws/resource_aws_appconfig_application.go index adcd5c9a8de..34a26b850d1 100644 --- a/aws/resource_aws_appconfig_application.go +++ b/aws/resource_aws_appconfig_application.go @@ -7,6 +7,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/appconfig" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "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" @@ -23,47 +24,51 @@ func resourceAwsAppconfigApplication() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "name": { + "arn": { Type: schema.TypeString, - Required: true, - ValidateFunc: validation.All( - validation.StringLenBetween(1, 64), - ), + Computed: true, }, "description": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.All( - validation.StringLenBetween(0, 1024), - ), + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(0, 1024), }, - "tags": tagsSchema(), - "id": { - Type: schema.TypeString, - Computed: true, - }, - "arn": { - Type: schema.TypeString, - Computed: true, + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 64), }, + "tags": tagsSchema(), + "tags_all": tagsSchemaComputed(), }, + CustomizeDiff: SetTagsDiff, } } func resourceAwsAppconfigApplicationCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).appconfigconn + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig + tags := defaultTagsConfig.MergeTags(keyvaluetags.New(d.Get("tags").(map[string]interface{}))) + applicationName := d.Get("name").(string) - applicationDescription := d.Get("description").(string) input := &appconfig.CreateApplicationInput{ - Name: aws.String(applicationName), - Description: aws.String(applicationDescription), - Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().AppconfigTags(), + Name: aws.String(applicationName), + Tags: tags.IgnoreAws().AppconfigTags(), + } + + if v, ok := d.GetOk("description"); ok { + input.Description = aws.String(v.(string)) } app, err := conn.CreateApplication(input) + if err != nil { - return fmt.Errorf("Error creating AppConfig application: %s", err) + return fmt.Errorf("error creating AppConfig Application (%s): %w", applicationName, err) + } + + if app == nil { + return fmt.Errorf("error creating AppConfig Application (%s): empty response", applicationName) } d.SetId(aws.StringValue(app.Id)) @@ -73,6 +78,7 @@ func resourceAwsAppconfigApplicationCreate(d *schema.ResourceData, meta interfac func resourceAwsAppconfigApplicationRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).appconfigconn + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig input := &appconfig.GetApplicationInput{ @@ -81,21 +87,21 @@ func resourceAwsAppconfigApplicationRead(d *schema.ResourceData, meta interface{ output, err := conn.GetApplication(input) - if !d.IsNewResource() && isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, appconfig.ErrCodeResourceNotFoundException) { log.Printf("[WARN] Appconfig Application (%s) not found, removing from state", d.Id()) d.SetId("") return nil } if err != nil { - return fmt.Errorf("error getting AppConfig Application (%s): %s", d.Id(), err) + return fmt.Errorf("error getting AppConfig Application (%s): %w", d.Id(), err) } if output == nil { return fmt.Errorf("error getting AppConfig Application (%s): empty response", d.Id()) } - appARN := arn.ARN{ + arn := arn.ARN{ AccountID: meta.(*AWSClient).accountid, Partition: meta.(*AWSClient).partition, Region: meta.(*AWSClient).region, @@ -103,17 +109,25 @@ func resourceAwsAppconfigApplicationRead(d *schema.ResourceData, meta interface{ Service: "appconfig", }.String() - d.Set("arn", appARN) + d.Set("arn", arn) d.Set("name", output.Name) d.Set("description", output.Description) - tags, err := keyvaluetags.AppconfigListTags(conn, appARN) + tags, err := keyvaluetags.AppconfigListTags(conn, arn) + if err != nil { - return fmt.Errorf("error getting tags for AppConfig Application (%s): %s", d.Id(), err) + return fmt.Errorf("error listing tags for AppConfig Application (%s): %w", d.Id(), err) + } + + tags = tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig) + + //lintignore:AWSR002 + if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting tags: %w", err) } - if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { - return fmt.Errorf("error setting tags: %s", err) + if err := d.Set("tags_all", tags.Map()); err != nil { + return fmt.Errorf("error setting tags_all: %w", err) } return nil @@ -122,35 +136,32 @@ func resourceAwsAppconfigApplicationRead(d *schema.ResourceData, meta interface{ func resourceAwsAppconfigApplicationUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).appconfigconn - if d.HasChange("tags") { - o, n := d.GetChange("tags") - if err := keyvaluetags.AppconfigUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { - return fmt.Errorf("error updating AppConfig (%s) tags: %s", d.Id(), err) + if d.HasChangesExcept("tags", "tags_all") { + + updateInput := &appconfig.UpdateApplicationInput{ + ApplicationId: aws.String(d.Id()), } - } - appDesc := d.Get("description").(string) - appName := d.Get("name").(string) + if d.HasChange("description") { + updateInput.Description = aws.String(d.Get("description").(string)) + } - updateInput := &appconfig.UpdateApplicationInput{ - ApplicationId: aws.String(d.Id()), - Description: aws.String(appDesc), - Name: aws.String(appName), - } + if d.HasChange("name") { + updateInput.Name = aws.String(d.Get("name").(string)) + } - if d.HasChange("description") { - _, n := d.GetChange("description") - updateInput.Description = aws.String(n.(string)) - } + _, err := conn.UpdateApplication(updateInput) - if d.HasChange("name") { - _, n := d.GetChange("name") - updateInput.Name = aws.String(n.(string)) + if err != nil { + return fmt.Errorf("error updating AppConfig Application(%s): %w", d.Id(), err) + } } - _, err := conn.UpdateApplication(updateInput) - if err != nil { - return fmt.Errorf("error updating AppConfig Application(%s): %s", d.Id(), err) + if d.HasChange("tags_all") { + o, n := d.GetChange("tags_all") + if err := keyvaluetags.AppconfigUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return fmt.Errorf("error updating AppConfig Application (%s) tags: %w", d.Get("arn").(string), err) + } } return resourceAwsAppconfigApplicationRead(d, meta) @@ -165,12 +176,12 @@ func resourceAwsAppconfigApplicationDelete(d *schema.ResourceData, meta interfac _, err := conn.DeleteApplication(input) - if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + if tfawserr.ErrCodeEquals(err, appconfig.ErrCodeResourceNotFoundException) { return nil } if err != nil { - return fmt.Errorf("error deleting Appconfig Application (%s): %s", d.Id(), err) + return fmt.Errorf("error deleting Appconfig Application (%s): %w", d.Id(), err) } return nil diff --git a/aws/resource_aws_appconfig_application_test.go b/aws/resource_aws_appconfig_application_test.go index 41ff1c82031..e0091036001 100644 --- a/aws/resource_aws_appconfig_application_test.go +++ b/aws/resource_aws_appconfig_application_test.go @@ -2,20 +2,21 @@ package aws import ( "fmt" + "regexp" "testing" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/appconfig" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) func TestAccAWSAppConfigApplication_basic(t *testing.T) { - var application appconfig.GetApplicationOutput rName := acctest.RandomWithPrefix("tf-acc-test") - rDesc := acctest.RandomWithPrefix("desc") resourceName := "aws_appconfig_application.test" + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), @@ -23,13 +24,12 @@ func TestAccAWSAppConfigApplication_basic(t *testing.T) { CheckDestroy: testAccCheckAppConfigApplicationDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAppConfigApplicationName(rName, rDesc), + Config: testAccAWSAppConfigApplicationConfigName(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigApplicationExists(resourceName, &application), + testAccCheckAWSAppConfigApplicationExists(resourceName), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "appconfig", regexp.MustCompile(`application/[a-z0-9]{4,7}`)), resource.TestCheckResourceAttr(resourceName, "name", rName), - testAccCheckAWSAppConfigApplicationARN(resourceName, &application), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), - resource.TestCheckResourceAttr(resourceName, "description", rDesc), ), }, { @@ -42,10 +42,7 @@ func TestAccAWSAppConfigApplication_basic(t *testing.T) { } func TestAccAWSAppConfigApplication_disappears(t *testing.T) { - var application appconfig.GetApplicationOutput - rName := acctest.RandomWithPrefix("tf-acc-test") - rDesc := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_appconfig_application.test" resource.ParallelTest(t, resource.TestCase{ @@ -55,10 +52,10 @@ func TestAccAWSAppConfigApplication_disappears(t *testing.T) { CheckDestroy: testAccCheckAppConfigApplicationDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAppConfigApplicationName(rName, rDesc), + Config: testAccAWSAppConfigApplicationConfigName(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigApplicationExists(resourceName, &application), - testAccCheckAWSAppConfigApplicationDisappears(&application), + testAccCheckAWSAppConfigApplicationExists(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsAppconfigApplication(), resourceName), ), ExpectNonEmptyPlan: true, }, @@ -66,9 +63,86 @@ func TestAccAWSAppConfigApplication_disappears(t *testing.T) { }) } -func TestAccAWSAppConfigApplication_Tags(t *testing.T) { - var application appconfig.GetApplicationOutput +func TestAccAWSAppConfigApplication_updateName(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + rNameUpdated := acctest.RandomWithPrefix("tf-acc-test-update") + resourceName := "aws_appconfig_application.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigApplicationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigApplicationConfigName(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigApplicationExists(resourceName), + ), + }, + { + Config: testAccAWSAppConfigApplicationConfigName(rNameUpdated), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigApplicationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", rNameUpdated), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSAppConfigApplication_updateDescription(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + description := acctest.RandomWithPrefix("tf-acc-test-update") + resourceName := "aws_appconfig_application.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigApplicationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigApplicationConfigDescription(rName, rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigApplicationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "description", rName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSAppConfigApplicationConfigDescription(rName, description), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigApplicationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "description", description), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + // Test Description Removal + Config: testAccAWSAppConfigApplicationConfigName(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigApplicationExists(resourceName), + ), + }, + }, + }) +} +func TestAccAWSAppConfigApplication_Tags(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_appconfig_application.test" @@ -81,7 +155,7 @@ func TestAccAWSAppConfigApplication_Tags(t *testing.T) { { Config: testAccAWSAppConfigApplicationTags1(rName, "key1", "value1"), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigApplicationExists(resourceName, &application), + testAccCheckAWSAppConfigApplicationExists(resourceName), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), ), @@ -94,7 +168,7 @@ func TestAccAWSAppConfigApplication_Tags(t *testing.T) { { Config: testAccAWSAppConfigApplicationTags2(rName, "key1", "value1updated", "key2", "value2"), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigApplicationExists(resourceName, &application), + testAccCheckAWSAppConfigApplicationExists(resourceName), resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), @@ -103,7 +177,7 @@ func TestAccAWSAppConfigApplication_Tags(t *testing.T) { { Config: testAccAWSAppConfigApplicationTags1(rName, "key2", "value2"), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigApplicationExists(resourceName, &application), + testAccCheckAWSAppConfigApplicationExists(resourceName), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), ), @@ -126,12 +200,12 @@ func testAccCheckAppConfigApplicationDestroy(s *terraform.State) error { output, err := conn.GetApplication(input) - if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + if tfawserr.ErrCodeEquals(err, appconfig.ErrCodeResourceNotFoundException) { continue } if err != nil { - return err + return fmt.Errorf("error reading AppConfig Application (%s): %w", rs.Primary.ID, err) } if output != nil { @@ -140,24 +214,9 @@ func testAccCheckAppConfigApplicationDestroy(s *terraform.State) error { } return nil - -} - -func testAccCheckAWSAppConfigApplicationDisappears(application *appconfig.GetApplicationOutput) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).appconfigconn - - input := &appconfig.DeleteApplicationInput{ - ApplicationId: aws.String(*application.Id), - } - - _, err := conn.DeleteApplication(input) - - return err - } } -func testAccCheckAWSAppConfigApplicationExists(resourceName string, application *appconfig.GetApplicationOutput) resource.TestCheckFunc { +func testAccCheckAWSAppConfigApplicationExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] if !ok { @@ -175,29 +234,34 @@ func testAccCheckAWSAppConfigApplicationExists(resourceName string, application } output, err := conn.GetApplication(input) + if err != nil { - return err + return fmt.Errorf("error reading AppConfig Application (%s): %w", rs.Primary.ID, err) } - *application = *output + if output == nil { + return fmt.Errorf("AppConfig Application (%s) not found", rs.Primary.ID) + } return nil } } -func testAccCheckAWSAppConfigApplicationARN(resourceName string, application *appconfig.GetApplicationOutput) resource.TestCheckFunc { - return func(s *terraform.State) error { - return testAccCheckResourceAttrRegionalARN(resourceName, "arn", "appconfig", fmt.Sprintf("application/%s", aws.StringValue(application.Id)))(s) - } +func testAccAWSAppConfigApplicationConfigName(rName string) string { + return fmt.Sprintf(` +resource "aws_appconfig_application" "test" { + name = %[1]q +} +`, rName) } -func testAccAWSAppConfigApplicationName(rName, rDesc string) string { +func testAccAWSAppConfigApplicationConfigDescription(rName, description string) string { return fmt.Sprintf(` resource "aws_appconfig_application" "test" { - name = %[1]q - description = %[2]q + name = %q + description = %q } -`, rName, rDesc) +`, rName, description) } func testAccAWSAppConfigApplicationTags1(rName, tagKey1, tagValue1 string) string { diff --git a/aws/resource_aws_appconfig_environment.go b/aws/resource_aws_appconfig_environment.go index 3a66831f269..415bfce0c2f 100644 --- a/aws/resource_aws_appconfig_environment.go +++ b/aws/resource_aws_appconfig_environment.go @@ -8,6 +8,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/appconfig" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "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" @@ -20,48 +21,42 @@ func resourceAwsAppconfigEnvironment() *schema.Resource { Update: resourceAwsAppconfigEnvironmentUpdate, Delete: resourceAwsAppconfigEnvironmentDelete, Importer: &schema.ResourceImporter{ - State: resourceAwsAppconfigEnvironmentImport, + State: schema.ImportStatePassthrough, }, Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.All( - validation.StringLenBetween(1, 64), - ), - }, "application_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.All( - validation.StringLenBetween(4, 7), - ), - }, - "description": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.All( - validation.StringLenBetween(0, 1024), - ), + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(4, 7), }, - "tags": tagsSchema(), "arn": { Type: schema.TypeString, Computed: true, }, - "monitors": { - Type: schema.TypeList, + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 64), + }, + "description": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(0, 1024), + }, + "monitor": { + Type: schema.TypeSet, Optional: true, MaxItems: 5, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "alarm_arn": { Type: schema.TypeString, - Optional: true, + Required: true, ValidateFunc: validation.All( - validation.StringLenBetween(20, 2048), + validation.StringLenBetween(1, 2048), + validateArn, ), }, "alarm_role_arn": { @@ -69,107 +64,125 @@ func resourceAwsAppconfigEnvironment() *schema.Resource { Optional: true, ValidateFunc: validation.All( validation.StringLenBetween(20, 2048), + validateArn, ), }, }, }, }, + "state": { + Type: schema.TypeString, + Computed: true, + }, + "tags": tagsSchema(), + "tags_all": tagsSchemaComputed(), }, + CustomizeDiff: SetTagsDiff, } } func resourceAwsAppconfigEnvironmentCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).appconfigconn + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig + tags := defaultTagsConfig.MergeTags(keyvaluetags.New(d.Get("tags").(map[string]interface{}))) + + appId := d.Get("application_id").(string) input := &appconfig.CreateEnvironmentInput{ Name: aws.String(d.Get("name").(string)), - Description: aws.String(d.Get("description").(string)), - ApplicationId: aws.String(d.Get("application_id").(string)), - Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().AppconfigTags(), - Monitors: expandAppconfigEnvironmentMonitors(d.Get("monitors").([]interface{})), + ApplicationId: aws.String(appId), + Tags: tags.IgnoreAws().AppconfigTags(), } - environment, err := conn.CreateEnvironment(input) - if err != nil { - return fmt.Errorf("Error creating AppConfig Environment: %s", err) + if v, ok := d.GetOk("description"); ok { + input.Description = aws.String(v.(string)) } - d.SetId(aws.StringValue(environment.Id)) + if v, ok := d.GetOk("monitor"); ok && v.(*schema.Set).Len() > 0 { + input.Monitors = expandAppconfigEnvironmentMonitors(v.(*schema.Set).List()) + } - return resourceAwsAppconfigEnvironmentRead(d, meta) -} + environment, err := conn.CreateEnvironment(input) -func expandAppconfigEnvironmentMonitors(list []interface{}) []*appconfig.Monitor { - monitors := make([]*appconfig.Monitor, len(list)) - for i, monitorInterface := range list { - m := monitorInterface.(map[string]interface{}) - monitors[i] = &appconfig.Monitor{ - AlarmArn: aws.String(m["alarm_arn"].(string)), - AlarmRoleArn: aws.String(m["alarm_role_arn"].(string)), - } + if err != nil { + return fmt.Errorf("error creating AppConfig Environment for Application (%s): %w", appId, err) } - return monitors -} -func flattenAwsAppconfigEnvironmentMonitors(monitors []*appconfig.Monitor) []interface{} { - list := make([]interface{}, len(monitors)) - for i, monitor := range monitors { - list[i] = map[string]interface{}{ - "alarm_arn": aws.StringValue(monitor.AlarmArn), - "alarm_role_arn": aws.StringValue(monitor.AlarmRoleArn), - } + if environment == nil { + return fmt.Errorf("error creating AppConfig Environment for Application (%s): empty response", appId) } - return list + + d.SetId(fmt.Sprintf("%s:%s", aws.StringValue(environment.Id), aws.StringValue(environment.ApplicationId))) + + return resourceAwsAppconfigEnvironmentRead(d, meta) } func resourceAwsAppconfigEnvironmentRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).appconfigconn + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig - appID := d.Get("application_id").(string) + envID, appID, err := resourceAwsAppconfigEnvironmentParseID(d.Id()) + + if err != nil { + return err + } input := &appconfig.GetEnvironmentInput{ ApplicationId: aws.String(appID), - EnvironmentId: aws.String(d.Id()), + EnvironmentId: aws.String(envID), } output, err := conn.GetEnvironment(input) - if !d.IsNewResource() && isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { - log.Printf("[WARN] Appconfig Environment (%s) not found, removing from state", d.Id()) + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, appconfig.ErrCodeResourceNotFoundException) { + log.Printf("[WARN] Appconfig Environment (%s) for Application (%s) not found, removing from state", envID, appID) d.SetId("") return nil } if err != nil { - return fmt.Errorf("error getting AppConfig Environment (%s): %s", d.Id(), err) + return fmt.Errorf("error getting AppConfig Environment (%s) for Application (%s): %w", envID, appID, err) } if output == nil { - return fmt.Errorf("error getting AppConfig Environment (%s): empty response", d.Id()) + return fmt.Errorf("error getting AppConfig Environment (%s) for Application (%s): empty response", envID, appID) } - d.Set("name", output.Name) - d.Set("description", output.Description) d.Set("application_id", output.ApplicationId) - d.Set("monitors", flattenAwsAppconfigEnvironmentMonitors(output.Monitors)) + d.Set("description", output.Description) + d.Set("name", output.Name) + d.Set("state", output.State) + + if err := d.Set("monitor", flattenAwsAppconfigEnvironmentMonitors(output.Monitors)); err != nil { + return fmt.Errorf("error setting monitor: %w", err) + } - environmentARN := arn.ARN{ + arn := arn.ARN{ AccountID: meta.(*AWSClient).accountid, Partition: meta.(*AWSClient).partition, Region: meta.(*AWSClient).region, - Resource: fmt.Sprintf("application/%s/environment/%s", appID, d.Id()), + Resource: fmt.Sprintf("application/%s/environment/%s", appID, envID), Service: "appconfig", }.String() - d.Set("arn", environmentARN) - tags, err := keyvaluetags.AppconfigListTags(conn, environmentARN) + d.Set("arn", arn) + + tags, err := keyvaluetags.AppconfigListTags(conn, arn) + if err != nil { - return fmt.Errorf("error getting tags for AppConfig Environment (%s): %s", d.Id(), err) + return fmt.Errorf("error listing tags for AppConfig Environment (%s): %s", d.Id(), err) } - if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { - return fmt.Errorf("error setting tags: %s", err) + tags = tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig) + + //lintignore:AWSR002 + if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting tags: %w", err) + } + + if err := d.Set("tags_all", tags.Map()); err != nil { + return fmt.Errorf("error setting tags_all: %w", err) } return nil @@ -178,33 +191,42 @@ func resourceAwsAppconfigEnvironmentRead(d *schema.ResourceData, meta interface{ func resourceAwsAppconfigEnvironmentUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).appconfigconn - updateInput := &appconfig.UpdateEnvironmentInput{ - EnvironmentId: aws.String(d.Id()), - ApplicationId: aws.String(d.Get("application_id").(string)), - } + if d.HasChangesExcept("tags", "tags_all") { + envID, appID, err := resourceAwsAppconfigEnvironmentParseID(d.Id()) - if d.HasChange("description") { - updateInput.Description = aws.String(d.Get("description").(string)) - } + if err != nil { + return err + } - if d.HasChange("name") { - updateInput.Name = aws.String(d.Get("name").(string)) - } + updateInput := &appconfig.UpdateEnvironmentInput{ + EnvironmentId: aws.String(envID), + ApplicationId: aws.String(appID), + } - if d.HasChange("tags") { - o, n := d.GetChange("tags") - if err := keyvaluetags.AppconfigUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { - return fmt.Errorf("error updating AppConfig (%s) tags: %s", d.Id(), err) + if d.HasChange("description") { + updateInput.Description = aws.String(d.Get("description").(string)) + } + + if d.HasChange("name") { + updateInput.Name = aws.String(d.Get("name").(string)) } - } - if d.HasChange("monitors") { - updateInput.Monitors = expandAppconfigEnvironmentMonitors(d.Get("monitors").([]interface{})) + if d.HasChange("monitor") { + updateInput.Monitors = expandAppconfigEnvironmentMonitors(d.Get("monitor").(*schema.Set).List()) + } + + _, err = conn.UpdateEnvironment(updateInput) + + if err != nil { + return fmt.Errorf("error updating AppConfig Environment (%s) for Application (%s): %w", envID, appID, err) + } } - _, err := conn.UpdateEnvironment(updateInput) - if err != nil { - return fmt.Errorf("error updating AppConfig Environment(%s): %s", d.Id(), err) + if d.HasChange("tags_all") { + o, n := d.GetChange("tags_all") + if err := keyvaluetags.AppconfigUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return fmt.Errorf("error updating AppConfig Environment (%s) tags: %w", d.Get("arn").(string), err) + } } return resourceAwsAppconfigEnvironmentRead(d, meta) @@ -213,32 +235,114 @@ func resourceAwsAppconfigEnvironmentUpdate(d *schema.ResourceData, meta interfac func resourceAwsAppconfigEnvironmentDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).appconfigconn + envID, appID, err := resourceAwsAppconfigEnvironmentParseID(d.Id()) + + if err != nil { + return err + } + input := &appconfig.DeleteEnvironmentInput{ - EnvironmentId: aws.String(d.Id()), - ApplicationId: aws.String(d.Get("application_id").(string)), + EnvironmentId: aws.String(envID), + ApplicationId: aws.String(appID), } - _, err := conn.DeleteEnvironment(input) + _, err = conn.DeleteEnvironment(input) - if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + if tfawserr.ErrCodeEquals(err, appconfig.ErrCodeResourceNotFoundException) { return nil } if err != nil { - return fmt.Errorf("error deleting Appconfig Environment (%s): %s", d.Id(), err) + return fmt.Errorf("error deleting Appconfig Environment (%s) for Application (%s): %w", envID, appID, err) } return nil } -func resourceAwsAppconfigEnvironmentImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - parts := strings.Split(d.Id(), "/") - if len(parts) != 2 { - return []*schema.ResourceData{}, fmt.Errorf("Wrong format of resource: %s. Please follow 'application-id/environment-id'", d.Id()) +func resourceAwsAppconfigEnvironmentParseID(id string) (string, string, error) { + parts := strings.Split(id, ":") + + if len(parts) != 2 || parts[0] == "" || parts[1] == "" { + return "", "", fmt.Errorf("unexpected format of ID (%q), expected environmentID:applicationID", id) + } + + return parts[0], parts[1], nil +} + +func expandAppconfigEnvironmentMonitor(tfMap map[string]interface{}) *appconfig.Monitor { + if tfMap == nil { + return nil + } + + monitor := &appconfig.Monitor{} + + if v, ok := tfMap["alarm_arn"].(string); ok && v != "" { + monitor.AlarmArn = aws.String(v) } - d.SetId(parts[1]) - d.Set("application_id", parts[0]) + if v, ok := tfMap["alarm_role_arn"].(string); ok && v != "" { + monitor.AlarmRoleArn = aws.String(v) + } + + return monitor +} + +func expandAppconfigEnvironmentMonitors(tfList []interface{}) []*appconfig.Monitor { + // AppConfig API requires a 0 length slice instead of a nil value + // when updating from N monitors to 0/nil monitors + monitors := make([]*appconfig.Monitor, 0) + + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + + if !ok { + continue + } + + monitor := expandAppconfigEnvironmentMonitor(tfMap) + + if monitor == nil { + continue + } + + monitors = append(monitors, monitor) + } + + return monitors +} + +func flattenAwsAppconfigEnvironmentMonitor(monitor *appconfig.Monitor) map[string]interface{} { + if monitor == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := monitor.AlarmArn; v != nil { + tfMap["alarm_arn"] = aws.StringValue(v) + } + + if v := monitor.AlarmRoleArn; v != nil { + tfMap["alarm_role_arn"] = aws.StringValue(v) + } + + return tfMap +} + +func flattenAwsAppconfigEnvironmentMonitors(monitors []*appconfig.Monitor) []interface{} { + if len(monitors) == 0 { + return nil + } + + var tfList []interface{} + + for _, monitor := range monitors { + if monitor == nil { + continue + } + + tfList = append(tfList, flattenAwsAppconfigEnvironmentMonitor(monitor)) + } - return []*schema.ResourceData{d}, nil + return tfList } diff --git a/aws/resource_aws_appconfig_environment_test.go b/aws/resource_aws_appconfig_environment_test.go index fff5d3affa1..3845bc9e41c 100644 --- a/aws/resource_aws_appconfig_environment_test.go +++ b/aws/resource_aws_appconfig_environment_test.go @@ -2,24 +2,22 @@ package aws import ( "fmt" + "regexp" "testing" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/appconfig" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) func TestAccAWSAppConfigEnvironment_basic(t *testing.T) { - var environment appconfig.GetEnvironmentOutput - roleName := acctest.RandomWithPrefix("tf-acc-test") - alarmName := acctest.RandomWithPrefix("tf-acc-test") - appName := acctest.RandomWithPrefix("tf-acc-test") - appDesc := acctest.RandomWithPrefix("desc") - envName := acctest.RandomWithPrefix("tf-acc-test") - envDesc := acctest.RandomWithPrefix("desc") + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_appconfig_environment.test" + appResourceName := "aws_appconfig_application.test" + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), @@ -27,18 +25,19 @@ func TestAccAWSAppConfigEnvironment_basic(t *testing.T) { CheckDestroy: testAccCheckAppConfigEnvironmentDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAppConfigEnvironmentWithMonitors(roleName, alarmName, appName, appDesc, envName, envDesc), + Config: testAccAWSAppConfigEnvironmentConfigBasic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigEnvironmentExists(resourceName, &environment), - resource.TestCheckResourceAttr(resourceName, "name", envName), - testAccCheckAWSAppConfigEnvironmentARN(resourceName, &environment), + testAccCheckAWSAppConfigEnvironmentExists(resourceName), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "appconfig", regexp.MustCompile(`application/[a-z0-9]{4,7}/environment/[a-z0-9]{4,7}`)), + resource.TestCheckResourceAttrPair(resourceName, "application_id", appResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "monitor.#", "0"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttrSet(resourceName, "state"), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), - resource.TestCheckResourceAttr(resourceName, "description", envDesc), ), }, { ResourceName: resourceName, - ImportStateIdFunc: testAccAWSAppConfigEnvironmentImportStateIdFunc(resourceName), ImportState: true, ImportStateVerify: true, }, @@ -47,12 +46,7 @@ func TestAccAWSAppConfigEnvironment_basic(t *testing.T) { } func TestAccAWSAppConfigEnvironment_disappears(t *testing.T) { - var environment appconfig.GetEnvironmentOutput - - appName := acctest.RandomWithPrefix("tf-acc-test") - appDesc := acctest.RandomWithPrefix("desc") - envName := acctest.RandomWithPrefix("tf-acc-test") - envDesc := acctest.RandomWithPrefix("desc") + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_appconfig_environment.test" resource.ParallelTest(t, resource.TestCase{ @@ -62,10 +56,10 @@ func TestAccAWSAppConfigEnvironment_disappears(t *testing.T) { CheckDestroy: testAccCheckAppConfigEnvironmentDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAppConfigEnvironment(appName, appDesc, envName, envDesc), + Config: testAccAWSAppConfigEnvironmentConfigBasic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigEnvironmentExists(resourceName, &environment), - testAccCheckAWSAppConfigEnvironmentDisappears(&environment), + testAccCheckAWSAppConfigEnvironmentExists(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsAppconfigEnvironment(), resourceName), ), ExpectNonEmptyPlan: true, }, @@ -73,9 +67,185 @@ func TestAccAWSAppConfigEnvironment_disappears(t *testing.T) { }) } -func TestAccAWSAppConfigEnvironment_Tags(t *testing.T) { - var environment appconfig.GetEnvironmentOutput +func TestAccAWSAppConfigEnvironment_updateName(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + rNameUpdated := acctest.RandomWithPrefix("tf-acc-test-update") + resourceName := "aws_appconfig_environment.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigEnvironmentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigEnvironmentConfigBasic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigEnvironmentExists(resourceName), + ), + }, + { + Config: testAccAWSAppConfigEnvironmentConfigBasic(rNameUpdated), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigEnvironmentExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", rNameUpdated), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} +func TestAccAWSAppConfigEnvironment_updateDescription(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + description := acctest.RandomWithPrefix("tf-acc-test-update") + resourceName := "aws_appconfig_environment.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigEnvironmentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigEnvironmentConfigDescription(rName, rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigEnvironmentExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "description", rName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSAppConfigEnvironmentConfigDescription(rName, description), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigEnvironmentExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "description", description), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + // Test Description Removal + Config: testAccAWSAppConfigEnvironmentConfigBasic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigEnvironmentExists(resourceName), + ), + }, + }, + }) +} + +func TestAccAWSAppConfigEnvironment_Monitors(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_appconfig_environment.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigEnvironmentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigEnvironmentWithMonitors(rName, 1), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigEnvironmentExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "monitor.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "monitor.*.alarm_arn", "aws_cloudwatch_metric_alarm.test.0", "arn"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "monitor.*.alarm_role_arn", "aws_iam_role.test", "arn"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSAppConfigEnvironmentWithMonitors(rName, 2), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigEnvironmentExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "monitor.#", "2"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "monitor.*.alarm_arn", "aws_cloudwatch_metric_alarm.test.0", "arn"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "monitor.*.alarm_role_arn", "aws_iam_role.test", "arn"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "monitor.*.alarm_arn", "aws_cloudwatch_metric_alarm.test.1", "arn"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "monitor.*.alarm_role_arn", "aws_iam_role.test", "arn"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + // Test Monitor Removal + Config: testAccAWSAppConfigEnvironmentConfigBasic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigEnvironmentExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "monitor.#", "0"), + ), + }, + }, + }) +} + +func TestAccAWSAppConfigEnvironment_MultipleEnvironments(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName1 := "aws_appconfig_environment.test" + resourceName2 := "aws_appconfig_environment.test2" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigEnvironmentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigEnvironmentConfigMultiple(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigEnvironmentExists(resourceName1), + testAccCheckAWSAppConfigEnvironmentExists(resourceName2), + ), + }, + { + ResourceName: resourceName1, + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: resourceName2, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSAppConfigEnvironmentConfigBasic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigEnvironmentExists(resourceName1), + ), + }, + { + ResourceName: resourceName1, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSAppConfigEnvironment_Tags(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_appconfig_environment.test" @@ -88,21 +258,20 @@ func TestAccAWSAppConfigEnvironment_Tags(t *testing.T) { { Config: testAccAWSAppConfigEnvironmentTags1(rName, "key1", "value1"), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigEnvironmentExists(resourceName, &environment), + testAccCheckAWSAppConfigEnvironmentExists(resourceName), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), ), }, { ResourceName: resourceName, - ImportStateIdFunc: testAccAWSAppConfigEnvironmentImportStateIdFunc(resourceName), ImportState: true, ImportStateVerify: true, }, { Config: testAccAWSAppConfigEnvironmentTags2(rName, "key1", "value1updated", "key2", "value2"), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigEnvironmentExists(resourceName, &environment), + testAccCheckAWSAppConfigEnvironmentExists(resourceName), resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), @@ -111,7 +280,7 @@ func TestAccAWSAppConfigEnvironment_Tags(t *testing.T) { { Config: testAccAWSAppConfigEnvironmentTags1(rName, "key2", "value2"), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigEnvironmentExists(resourceName, &environment), + testAccCheckAWSAppConfigEnvironmentExists(resourceName), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), ), @@ -128,43 +297,36 @@ func testAccCheckAppConfigEnvironmentDestroy(s *terraform.State) error { continue } + envID, appID, err := resourceAwsAppconfigEnvironmentParseID(rs.Primary.ID) + + if err != nil { + return err + } + input := &appconfig.GetEnvironmentInput{ - ApplicationId: aws.String(rs.Primary.Attributes["application_id"]), - EnvironmentId: aws.String(rs.Primary.ID), + ApplicationId: aws.String(appID), + EnvironmentId: aws.String(envID), } output, err := conn.GetEnvironment(input) - if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + if tfawserr.ErrCodeEquals(err, appconfig.ErrCodeResourceNotFoundException) { continue } if err != nil { - return err + return fmt.Errorf("error reading AppConfig Environment (%s) for Application (%s): %w", envID, appID, err) } if output != nil { - return fmt.Errorf("AppConfig Environment (%s) still exists", rs.Primary.ID) + return fmt.Errorf("AppConfig Environment (%s) for Application (%s) still exists", envID, appID) } } return nil } -func testAccCheckAWSAppConfigEnvironmentDisappears(environment *appconfig.GetEnvironmentOutput) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).appconfigconn - - _, err := conn.DeleteEnvironment(&appconfig.DeleteEnvironmentInput{ - ApplicationId: environment.ApplicationId, - EnvironmentId: environment.Id, - }) - - return err - } -} - -func testAccCheckAWSAppConfigEnvironmentExists(resourceName string, environment *appconfig.GetEnvironmentOutput) resource.TestCheckFunc { +func testAccCheckAWSAppConfigEnvironmentExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] if !ok { @@ -175,84 +337,64 @@ func testAccCheckAWSAppConfigEnvironmentExists(resourceName string, environment return fmt.Errorf("Resource (%s) ID not set", resourceName) } - conn := testAccProvider.Meta().(*AWSClient).appconfigconn + envID, appID, err := resourceAwsAppconfigEnvironmentParseID(rs.Primary.ID) - output, err := conn.GetEnvironment(&appconfig.GetEnvironmentInput{ - ApplicationId: aws.String(rs.Primary.Attributes["application_id"]), - EnvironmentId: aws.String(rs.Primary.ID), - }) if err != nil { return err } - *environment = *output + conn := testAccProvider.Meta().(*AWSClient).appconfigconn - return nil - } -} + input := &appconfig.GetEnvironmentInput{ + ApplicationId: aws.String(appID), + EnvironmentId: aws.String(envID), + } -func testAccCheckAWSAppConfigEnvironmentARN(resourceName string, environment *appconfig.GetEnvironmentOutput) resource.TestCheckFunc { - return func(s *terraform.State) error { - return testAccCheckResourceAttrRegionalARN(resourceName, "arn", "appconfig", fmt.Sprintf("application/%s/environment/%s", aws.StringValue(environment.ApplicationId), aws.StringValue(environment.Id)))(s) - } -} + output, err := conn.GetEnvironment(input) -func testAccAWSAppConfigEnvironmentWithMonitors(roleName, alarmName, appName, appDesc, envName, envDesc string) string { - return testAccAWSAppConfigMonitor_ServiceRole(roleName) + testAccAWSCloudWatchMetricAlarmConfig(alarmName) + testAccAWSAppConfigApplicationName(appName, appDesc) + fmt.Sprintf(` -resource "aws_appconfig_environment" "test" { - name = %[1]q - description = %[2]q - application_id = aws_appconfig_application.test.id + if err != nil { + return err + } - monitors { - alarm_arn = aws_cloudwatch_metric_alarm.test.arn - alarm_role_arn = aws_iam_role.test.arn - } -} -`, envName, envDesc) -} + if output == nil { + return fmt.Errorf("AppConfig Environment (%s) for Application (%s) not found", envID, appID) + } -func testAccAWSAppConfigEnvironment(appName, appDesc, envName, envDesc string) string { - return testAccAWSAppConfigApplicationName(appName, appDesc) + fmt.Sprintf(` -resource "aws_appconfig_environment" "test" { - name = %[1]q - description = %[2]q - application_id = aws_appconfig_application.test.id -} -`, envName, envDesc) + return nil + } } -func testAccAWSAppConfigEnvironmentTags1(rName, tagKey1, tagValue1 string) string { - return testAccAWSAppConfigApplicationTags1(rName, tagKey1, tagValue1) + fmt.Sprintf(` +func testAccAWSAppConfigEnvironmentConfigBasic(rName string) string { + return composeConfig( + testAccAWSAppConfigApplicationConfigName(rName), + fmt.Sprintf(` resource "aws_appconfig_environment" "test" { - name = %[1]q + name = %q application_id = aws_appconfig_application.test.id - - tags = { - %[2]q = %[3]q - } } -`, rName, tagKey1, tagValue1) +`, rName)) } -func testAccAWSAppConfigEnvironmentTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { - return testAccAWSAppConfigApplicationTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2) + fmt.Sprintf(` +func testAccAWSAppConfigEnvironmentConfigDescription(rName, description string) string { + return composeConfig( + testAccAWSAppConfigApplicationConfigName(rName), + fmt.Sprintf(` resource "aws_appconfig_environment" "test" { - name = %[1]q + name = %q + description = %q application_id = aws_appconfig_application.test.id - - tags = { - %[2]q = %[3]q - %[4]q = %[5]q - } } -`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +`, rName, description)) } -func testAccAWSAppConfigMonitor_ServiceRole(rName string) string { - return fmt.Sprintf(` +func testAccAWSAppConfigEnvironmentWithMonitors(rName string, count int) string { + return composeConfig( + testAccAWSAppConfigApplicationConfigName(rName), + fmt.Sprintf(` +data "aws_partition" "current" {} + resource "aws_iam_role" "test" { - name = "%s" + name = %[1]q assume_role_policy = < Date: Sun, 11 Jul 2021 21:41:05 -0400 Subject: [PATCH 284/398] update attributes reference in env resource --- website/docs/r/appconfig_environment.html.markdown | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/docs/r/appconfig_environment.html.markdown b/website/docs/r/appconfig_environment.html.markdown index a743773d67c..506e3b1266b 100644 --- a/website/docs/r/appconfig_environment.html.markdown +++ b/website/docs/r/appconfig_environment.html.markdown @@ -42,10 +42,10 @@ resource "aws_appconfig_application" "example" { The following arguments are supported: +* `application_id` - (Required, Forces new resource) The AppConfig application ID. Must be between 4 and 7 characters in length. * `name` - (Required) The name for the environment. Must be between 1 and 64 characters in length. -* `application_id` - (Required) The AppConfig application ID. Must be between 4 and 7 characters in length. * `description` - (Optional) The description of the environment. Can be at most 1024 characters. -* `monitor` - (Optional) Set of Amazon CloudWatch alarms to monitor during the deployment process. Maximum of 5. Detailed below. +* `monitor` - (Optional) Set of Amazon CloudWatch alarms to monitor during the deployment process. Maximum of 5. See [Monitor](#monitor) below for more details. * `tags` - (Optional) A map of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](/docs/providers/aws/index.html#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. ### Monitor @@ -68,5 +68,5 @@ In addition to all arguments above, the following attributes are exported: AppConfig Environments can be imported by using the environment ID and application ID separated by a colon (`:`), e.g. ``` -$ terraform import aws_appconfig_environment.example 71abcde/11xxxxx +$ terraform import aws_appconfig_environment.example 71abcde:11xxxxx ``` From 4e0217c2e20c876d296fd4a56d1c4104873efccf Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Tue, 11 May 2021 22:07:31 +0900 Subject: [PATCH 285/398] feat: add aws_appconfig_configuration_profile --- aws/provider.go | 1 + ...rce_aws_appconfig_configuration_profile.go | 222 ++++++++++++++++++ 2 files changed, 223 insertions(+) create mode 100644 aws/resource_aws_appconfig_configuration_profile.go diff --git a/aws/provider.go b/aws/provider.go index 90236fbf4ec..48e60dbd52c 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -508,6 +508,7 @@ func Provider() *schema.Provider { "aws_appautoscaling_scheduled_action": resourceAwsAppautoscalingScheduledAction(), "aws_appconfig_application": resourceAwsAppconfigApplication(), "aws_appconfig_environment": resourceAwsAppconfigEnvironment(), + "aws_appconfig_configuration_profile": resourceAwsAppconfigConfigurationProfile(), "aws_appmesh_gateway_route": resourceAwsAppmeshGatewayRoute(), "aws_appmesh_mesh": resourceAwsAppmeshMesh(), "aws_appmesh_route": resourceAwsAppmeshRoute(), diff --git a/aws/resource_aws_appconfig_configuration_profile.go b/aws/resource_aws_appconfig_configuration_profile.go new file mode 100644 index 00000000000..700fe6f0f61 --- /dev/null +++ b/aws/resource_aws_appconfig_configuration_profile.go @@ -0,0 +1,222 @@ +package aws + +import ( + "fmt" + "log" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" + "github.com/aws/aws-sdk-go/service/appconfig" + "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" +) + +func resourceAwsAppconfigConfigurationProfile() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsAppconfigConfigurationProfileCreate, + Read: resourceAwsAppconfigConfigurationProfileRead, + Update: resourceAwsAppconfigConfigurationProfileUpdate, + Delete: resourceAwsAppconfigConfigurationProfileDelete, + Importer: &schema.ResourceImporter{ + State: resourceAwsAppconfigConfigurationProfileImport, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 64), + ), + }, + "application_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(4, 7), + ), + }, + "location_uri": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(0, 2048), + ), + }, + "description": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.All( + validation.StringLenBetween(0, 1024), + ), + }, + "retrieval_role_arn": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.All( + validation.StringLenBetween(20, 2048), + ), + }, + // "validators": { + // Type: schema.TypeString, + // Optional: true, + // ValidateFunc: validation.All( + // validation.StringLenBetween(20, 2048), + // ), + // }, + "tags": tagsSchema(), + "arn": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceAwsAppconfigConfigurationProfileCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).appconfigconn + + input := &appconfig.CreateConfigurationProfileInput{ + Name: aws.String(d.Get("name").(string)), + Description: aws.String(d.Get("description").(string)), + LocationUri: aws.String(d.Get("location_uri").(string)), + RetrievalRoleArn: aws.String(d.Get("retrieval_role_arn").(string)), + ApplicationId: aws.String(d.Get("application_id").(string)), + Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().AppconfigTags(), + } + + profile, err := conn.CreateConfigurationProfile(input) + if err != nil { + return fmt.Errorf("Error creating AppConfig ConfigurationProfile: %s", err) + } + + d.SetId(aws.StringValue(profile.Id)) + + return resourceAwsAppconfigConfigurationProfileRead(d, meta) +} + +func resourceAwsAppconfigConfigurationProfileRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).appconfigconn + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + + appID := d.Get("application_id").(string) + + input := &appconfig.GetConfigurationProfileInput{ + ApplicationId: aws.String(appID), + ConfigurationProfileId: aws.String(d.Id()), + } + + output, err := conn.GetConfigurationProfile(input) + + if !d.IsNewResource() && isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + log.Printf("[WARN] Appconfig ConfigurationProfile (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error getting AppConfig ConfigurationProfile (%s): %s", d.Id(), err) + } + + if output == nil { + return fmt.Errorf("error getting AppConfig ConfigurationProfile (%s): empty response", d.Id()) + } + + d.Set("name", output.Name) + d.Set("description", output.Description) + d.Set("application_id", output.ApplicationId) + d.Set("location_uri", output.LocationUri) + d.Set("retrieval_role_arn", output.RetrievalRoleArn) + + profileARN := arn.ARN{ + AccountID: meta.(*AWSClient).accountid, + Partition: meta.(*AWSClient).partition, + Region: meta.(*AWSClient).region, + Resource: fmt.Sprintf("application/%s/configurationprofile/%s", appID, d.Id()), + Service: "appconfig", + }.String() + d.Set("arn", profileARN) + + tags, err := keyvaluetags.AppconfigListTags(conn, profileARN) + if err != nil { + return fmt.Errorf("error getting tags for AppConfig ConfigurationProfile (%s): %s", d.Id(), err) + } + + if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting tags: %s", err) + } + + return nil +} + +func resourceAwsAppconfigConfigurationProfileUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).appconfigconn + + updateInput := &appconfig.UpdateConfigurationProfileInput{ + ConfigurationProfileId: aws.String(d.Id()), + ApplicationId: aws.String(d.Get("application_id").(string)), + } + + if d.HasChange("description") { + updateInput.Description = aws.String(d.Get("description").(string)) + } + + if d.HasChange("name") { + updateInput.Name = aws.String(d.Get("name").(string)) + } + + if d.HasChange("retrieval_role_arn") { + updateInput.Name = aws.String(d.Get("retrieval_role_arn").(string)) + } + + if d.HasChange("tags") { + o, n := d.GetChange("tags") + if err := keyvaluetags.AppconfigUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return fmt.Errorf("error updating AppConfig (%s) tags: %s", d.Id(), err) + } + } + + _, err := conn.UpdateConfigurationProfile(updateInput) + if err != nil { + return fmt.Errorf("error updating AppConfig ConfigurationProfile (%s): %s", d.Id(), err) + } + + return resourceAwsAppconfigConfigurationProfileRead(d, meta) +} + +func resourceAwsAppconfigConfigurationProfileDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).appconfigconn + + input := &appconfig.DeleteConfigurationProfileInput{ + ConfigurationProfileId: aws.String(d.Id()), + ApplicationId: aws.String(d.Get("application_id").(string)), + } + + _, err := conn.DeleteConfigurationProfile(input) + + if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + return nil + } + + if err != nil { + return fmt.Errorf("error deleting Appconfig ConfigurationProfile (%s): %s", d.Id(), err) + } + + return nil +} + +func resourceAwsAppconfigConfigurationProfileImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + parts := strings.Split(d.Id(), "/") + if len(parts) != 2 { + return []*schema.ResourceData{}, fmt.Errorf("Wrong format of resource: %s. Please follow 'application-id/configurationprofile-id'", d.Id()) + } + + d.SetId(parts[1]) + d.Set("application_id", parts[0]) + + return []*schema.ResourceData{d}, nil +} From 4fde4995f204465b51e2e7173f2e78b7afa38294 Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Tue, 11 May 2021 22:44:58 +0900 Subject: [PATCH 286/398] feat: support AppConfig Validators --- ...rce_aws_appconfig_configuration_profile.go | 59 ++++++++++++++++--- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/aws/resource_aws_appconfig_configuration_profile.go b/aws/resource_aws_appconfig_configuration_profile.go index 700fe6f0f61..c0fce741008 100644 --- a/aws/resource_aws_appconfig_configuration_profile.go +++ b/aws/resource_aws_appconfig_configuration_profile.go @@ -61,13 +61,29 @@ func resourceAwsAppconfigConfigurationProfile() *schema.Resource { validation.StringLenBetween(20, 2048), ), }, - // "validators": { - // Type: schema.TypeString, - // Optional: true, - // ValidateFunc: validation.All( - // validation.StringLenBetween(20, 2048), - // ), - // }, + "validators": { + Type: schema.TypeList, + Optional: true, + MaxItems: 2, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "content": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.All( + validation.StringLenBetween(0, 32768), + ), + }, + "type": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + "JSON_SCHEMA", "LAMBDA", + }, false), + }, + }, + }, + }, "tags": tagsSchema(), "arn": { Type: schema.TypeString, @@ -86,6 +102,7 @@ func resourceAwsAppconfigConfigurationProfileCreate(d *schema.ResourceData, meta LocationUri: aws.String(d.Get("location_uri").(string)), RetrievalRoleArn: aws.String(d.Get("retrieval_role_arn").(string)), ApplicationId: aws.String(d.Get("application_id").(string)), + Validators: expandAppconfigValidators(d.Get("validators").([]interface{})), Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().AppconfigTags(), } @@ -99,6 +116,29 @@ func resourceAwsAppconfigConfigurationProfileCreate(d *schema.ResourceData, meta return resourceAwsAppconfigConfigurationProfileRead(d, meta) } +func expandAppconfigValidators(list []interface{}) []*appconfig.Validator { + validators := make([]*appconfig.Validator, len(list)) + for i, validatorInterface := range list { + m := validatorInterface.(map[string]interface{}) + validators[i] = &appconfig.Validator{ + Content: aws.String(m["content"].(string)), + Type: aws.String(m["type"].(string)), + } + } + return validators +} + +func flattenAwsAppconfigValidators(validators []*appconfig.Validator) []interface{} { + list := make([]interface{}, len(validators)) + for i, validator := range validators { + list[i] = map[string]interface{}{ + "content": aws.StringValue(validator.Content), + "type": aws.StringValue(validator.Type), + } + } + return list +} + func resourceAwsAppconfigConfigurationProfileRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).appconfigconn ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig @@ -131,6 +171,7 @@ func resourceAwsAppconfigConfigurationProfileRead(d *schema.ResourceData, meta i d.Set("application_id", output.ApplicationId) d.Set("location_uri", output.LocationUri) d.Set("retrieval_role_arn", output.RetrievalRoleArn) + d.Set("validators", flattenAwsAppconfigValidators(output.Validators)) profileARN := arn.ARN{ AccountID: meta.(*AWSClient).accountid, @@ -180,6 +221,10 @@ func resourceAwsAppconfigConfigurationProfileUpdate(d *schema.ResourceData, meta } } + if d.HasChange("validators") { + updateInput.Validators = expandAppconfigValidators(d.Get("validators").([]interface{})) + } + _, err := conn.UpdateConfigurationProfile(updateInput) if err != nil { return fmt.Errorf("error updating AppConfig ConfigurationProfile (%s): %s", d.Id(), err) From 94edc9e54bf9069ce11df63a08a5c307c886cd47 Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Tue, 11 May 2021 22:53:50 +0900 Subject: [PATCH 287/398] docs: add a document of aws_appconfig_configuration_profile --- ...config_configuration_profile.html.markdown | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 website/docs/r/appconfig_configuration_profile.html.markdown diff --git a/website/docs/r/appconfig_configuration_profile.html.markdown b/website/docs/r/appconfig_configuration_profile.html.markdown new file mode 100644 index 00000000000..862e1648474 --- /dev/null +++ b/website/docs/r/appconfig_configuration_profile.html.markdown @@ -0,0 +1,67 @@ +--- +subcategory: "AppConfig" +layout: "aws" +page_title: "AWS: aws_appconfig_configuration_profile" +description: |- + Provides an AppConfig Configuration Profile resource. +--- + +# Resource: aws_appconfig_configuration_profile + +Provides an AppConfig Configuration Profile resource. + +## Example Usage + +### AppConfig Configuration Profile + +```hcl +resource "aws_appconfig_configuration_profile" "production" { + name = "test" + description = "test" + application_id = aws_appconfig_application.test.id + validators { + content = "arn:aws:lambda:us-east-1:111111111111:function:test" + type = "LAMBDA" + } +} + +resource "aws_appconfig_application" "test" { + name = "test" + description = "Test" + tags = { + Type = "Test" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +- `name` - (Required) The environment name. Must be between 1 and 64 characters in length. +- `application_id` - (Required) The application id. Must be between 4 and 7 characters in length. +- `description` - (Optional) The description of the environment. Can be at most 1024 characters. +- `location_uri` - (Optional) A URI to locate the configuration. +- `validators` - (Optional) A list of methods for validating the configuration. Detailed below. +- `retrieval_role_arn` - (Optional) The ARN of an IAM role with permission to access the configuration at the specified LocationUri. +- `tags` - (Optional) A map of tags to assign to the resource. + +### validator + +- `content` - (Optional) Either the JSON Schema content or the Amazon Resource Name (ARN) of an AWS Lambda function. +- `type` - (Optional) AWS AppConfig supports validators of type `JSON_SCHEMA` and `LAMBDA` + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +- `arn` - The Amazon Resource Name (ARN) of the AppConfig Configuration Profile. +- `id` - The AppConfig Configuration Profile ID + +## Import + +`aws_appconfig_configuration_profile` can be imported by the Application ID and Configuration Profile ID, e.g. + +``` +$ terraform import aws_appconfig_configuration_profile.test 71abcde/11xxxxx +``` From a2d6abe3bad4c99e69360d3879c7d44a62d48ed1 Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Wed, 12 May 2021 07:20:57 +0900 Subject: [PATCH 288/398] test: add tests for aws_appconfig_configuration_profile --- ...ws_appconfig_configuration_profile_test.go | 258 ++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 aws/resource_aws_appconfig_configuration_profile_test.go diff --git a/aws/resource_aws_appconfig_configuration_profile_test.go b/aws/resource_aws_appconfig_configuration_profile_test.go new file mode 100644 index 00000000000..a25999e52ff --- /dev/null +++ b/aws/resource_aws_appconfig_configuration_profile_test.go @@ -0,0 +1,258 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/appconfig" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccAWSAppConfigConfigurationProfile_basic(t *testing.T) { + var profile appconfig.GetConfigurationProfileOutput + profileName := acctest.RandomWithPrefix("tf-acc-test") + profileDesc := acctest.RandomWithPrefix("desc") + resourceName := "aws_appconfig_configuration_profile.test" + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigConfigurationProfileDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigConfigurationProfile(profileName, profileDesc), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigConfigurationProfileExists(resourceName, &profile), + resource.TestCheckResourceAttr(resourceName, "name", profileName), + testAccCheckAWSAppConfigConfigurationProfileARN(resourceName, &profile), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "description", profileDesc), + ), + }, + { + ResourceName: resourceName, + ImportStateIdFunc: testAccAWSAppConfigConfigurationProfileImportStateIdFunc(resourceName), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSAppConfigConfigurationProfile_disappears(t *testing.T) { + var profile appconfig.GetConfigurationProfileOutput + + profileName := acctest.RandomWithPrefix("tf-acc-test") + profileDesc := acctest.RandomWithPrefix("desc") + resourceName := "aws_appconfig_configuration_profile.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigConfigurationProfileDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigConfigurationProfile(profileName, profileDesc), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigConfigurationProfileExists(resourceName, &profile), + testAccCheckAWSAppConfigConfigurationProfileDisappears(&profile), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAWSAppConfigConfigurationProfile_Tags(t *testing.T) { + var profile appconfig.GetConfigurationProfileOutput + + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_appconfig_configuration_profile.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigConfigurationProfileDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigConfigurationProfileTags1(rName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigConfigurationProfileExists(resourceName, &profile), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportStateIdFunc: testAccAWSAppConfigConfigurationProfileImportStateIdFunc(resourceName), + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSAppConfigConfigurationProfileTags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigConfigurationProfileExists(resourceName, &profile), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccAWSAppConfigConfigurationProfileTags1(rName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigConfigurationProfileExists(resourceName, &profile), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + }, + }) +} + +func testAccCheckAppConfigConfigurationProfileDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).appconfigconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_appconfig_configuration_profile" { + continue + } + + input := &appconfig.GetConfigurationProfileInput{ + ApplicationId: aws.String(rs.Primary.Attributes["application_id"]), + ConfigurationProfileId: aws.String(rs.Primary.ID), + } + + output, err := conn.GetConfigurationProfile(input) + + if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + continue + } + + if err != nil { + return err + } + + if output != nil { + return fmt.Errorf("AppConfig ConfigurationProfile (%s) still exists", rs.Primary.ID) + } + } + + return nil +} + +func testAccCheckAWSAppConfigConfigurationProfileDisappears(profile *appconfig.GetConfigurationProfileOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).appconfigconn + + _, err := conn.DeleteConfigurationProfile(&appconfig.DeleteConfigurationProfileInput{ + ApplicationId: profile.ApplicationId, + ConfigurationProfileId: profile.Id, + }) + + return err + } +} + +func testAccCheckAWSAppConfigConfigurationProfileExists(resourceName string, profile *appconfig.GetConfigurationProfileOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Resource not found: %s", resourceName) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("Resource (%s) ID not set", resourceName) + } + + conn := testAccProvider.Meta().(*AWSClient).appconfigconn + + output, err := conn.GetConfigurationProfile(&appconfig.GetConfigurationProfileInput{ + ApplicationId: aws.String(rs.Primary.Attributes["application_id"]), + ConfigurationProfileId: aws.String(rs.Primary.ID), + }) + if err != nil { + return err + } + + *profile = *output + + return nil + } +} + +func testAccCheckAWSAppConfigConfigurationProfileARN(resourceName string, profile *appconfig.GetConfigurationProfileOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + return testAccCheckResourceAttrRegionalARN(resourceName, "arn", "appconfig", fmt.Sprintf("application/%s/configurationprofile/%s", aws.StringValue(profile.ApplicationId), aws.StringValue(profile.Id)))(s) + } +} + +func testAccAWSAppConfigConfigurationProfile(profileName, profileDesc string) string { + appName := acctest.RandomWithPrefix("tf-acc-test") + appDesc := acctest.RandomWithPrefix("desc") + return testAccAWSAppConfigApplicationName(appName, appDesc) + fmt.Sprintf(` +resource "aws_appconfig_configuration_profile" "test" { + name = %[1]q + description = %[2]q + application_id = aws_appconfig_application.test.id + validators { + type = "JSON_SCHEMA" + content = jsonencode({ + "$schema" = "http://json-schema.org/draft-04/schema#" + title = "$id$" + description = "BasicFeatureToggle-1" + type = "object" + additionalProperties = false + patternProperties = { + "[^\\s]+$" = { + type = "boolean" + } + } + minProperties = 1 + }) + } +} +`, profileName, profileDesc) +} + +func testAccAWSAppConfigConfigurationProfileTags1(rName, tagKey1, tagValue1 string) string { + return testAccAWSAppConfigApplicationTags1(rName, tagKey1, tagValue1) + fmt.Sprintf(` +resource "aws_appconfig_configuration_profile" "test" { + name = %[1]q + application_id = aws_appconfig_application.test.id + + tags = { + %[2]q = %[3]q + } +} +`, rName, tagKey1, tagValue1) +} + +func testAccAWSAppConfigConfigurationProfileTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return testAccAWSAppConfigApplicationTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2) + fmt.Sprintf(` +resource "aws_appconfig_configuration_profile" "test" { + name = %[1]q + application_id = aws_appconfig_application.test.id + + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } +} +`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +} + +func testAccAWSAppConfigConfigurationProfileImportStateIdFunc(resourceName string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return "", fmt.Errorf("Not Found: %s", resourceName) + } + + return fmt.Sprintf("%s/%s", rs.Primary.Attributes["application_id"], rs.Primary.ID), nil + } +} From 16e0a34871217880c8abd1f306483212f6a2f09b Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Wed, 12 May 2021 09:39:35 +0900 Subject: [PATCH 289/398] docs: fix document of aws_appconfig_configuration_profile --- website/docs/r/appconfig_configuration_profile.html.markdown | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/website/docs/r/appconfig_configuration_profile.html.markdown b/website/docs/r/appconfig_configuration_profile.html.markdown index 862e1648474..01da1fef385 100644 --- a/website/docs/r/appconfig_configuration_profile.html.markdown +++ b/website/docs/r/appconfig_configuration_profile.html.markdown @@ -19,6 +19,7 @@ resource "aws_appconfig_configuration_profile" "production" { name = "test" description = "test" application_id = aws_appconfig_application.test.id + location_uri = "hosted" validators { content = "arn:aws:lambda:us-east-1:111111111111:function:test" type = "LAMBDA" @@ -39,9 +40,9 @@ resource "aws_appconfig_application" "test" { The following arguments are supported: - `name` - (Required) The environment name. Must be between 1 and 64 characters in length. -- `application_id` - (Required) The application id. Must be between 4 and 7 characters in length. +- `application_id` - (Required, Forces new resource) The application id. Must be between 4 and 7 characters in length. +- `location_uri` - (Required, Forces new resource) A URI to locate the configuration. - `description` - (Optional) The description of the environment. Can be at most 1024 characters. -- `location_uri` - (Optional) A URI to locate the configuration. - `validators` - (Optional) A list of methods for validating the configuration. Detailed below. - `retrieval_role_arn` - (Optional) The ARN of an IAM role with permission to access the configuration at the specified LocationUri. - `tags` - (Optional) A map of tags to assign to the resource. From 5e42c00bc2cc40074bd9e6ed24cf1f1b4ccce5ab Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Wed, 12 May 2021 09:41:16 +0900 Subject: [PATCH 290/398] test: specify location_uri --- aws/resource_aws_appconfig_configuration_profile_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aws/resource_aws_appconfig_configuration_profile_test.go b/aws/resource_aws_appconfig_configuration_profile_test.go index a25999e52ff..8b08b7130e7 100644 --- a/aws/resource_aws_appconfig_configuration_profile_test.go +++ b/aws/resource_aws_appconfig_configuration_profile_test.go @@ -199,6 +199,7 @@ resource "aws_appconfig_configuration_profile" "test" { name = %[1]q description = %[2]q application_id = aws_appconfig_application.test.id + location_uri = "hosted" validators { type = "JSON_SCHEMA" content = jsonencode({ @@ -224,6 +225,7 @@ func testAccAWSAppConfigConfigurationProfileTags1(rName, tagKey1, tagValue1 stri resource "aws_appconfig_configuration_profile" "test" { name = %[1]q application_id = aws_appconfig_application.test.id + location_uri = "hosted" tags = { %[2]q = %[3]q @@ -237,6 +239,7 @@ func testAccAWSAppConfigConfigurationProfileTags2(rName, tagKey1, tagValue1, tag resource "aws_appconfig_configuration_profile" "test" { name = %[1]q application_id = aws_appconfig_application.test.id + location_uri = "hosted" tags = { %[2]q = %[3]q From d5338d41244480fb3b9346fa065d78ba7ab124dd Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Wed, 12 May 2021 09:42:45 +0900 Subject: [PATCH 291/398] docs: fix typo --- website/docs/r/appconfig_configuration_profile.html.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/r/appconfig_configuration_profile.html.markdown b/website/docs/r/appconfig_configuration_profile.html.markdown index 01da1fef385..c3974e546ee 100644 --- a/website/docs/r/appconfig_configuration_profile.html.markdown +++ b/website/docs/r/appconfig_configuration_profile.html.markdown @@ -39,10 +39,10 @@ resource "aws_appconfig_application" "test" { The following arguments are supported: -- `name` - (Required) The environment name. Must be between 1 and 64 characters in length. +- `name` - (Required) The configuration profile name. Must be between 1 and 64 characters in length. - `application_id` - (Required, Forces new resource) The application id. Must be between 4 and 7 characters in length. - `location_uri` - (Required, Forces new resource) A URI to locate the configuration. -- `description` - (Optional) The description of the environment. Can be at most 1024 characters. +- `description` - (Optional) The description of the configuration profile. Can be at most 1024 characters. - `validators` - (Optional) A list of methods for validating the configuration. Detailed below. - `retrieval_role_arn` - (Optional) The ARN of an IAM role with permission to access the configuration at the specified LocationUri. - `tags` - (Optional) A map of tags to assign to the resource. From 82aff38a9d140f5c72d8ae8e350b1c4320706a39 Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Wed, 12 May 2021 09:45:38 +0900 Subject: [PATCH 292/398] style: format code --- aws/resource_aws_appconfig_configuration_profile_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/aws/resource_aws_appconfig_configuration_profile_test.go b/aws/resource_aws_appconfig_configuration_profile_test.go index 8b08b7130e7..4b873474fde 100644 --- a/aws/resource_aws_appconfig_configuration_profile_test.go +++ b/aws/resource_aws_appconfig_configuration_profile_test.go @@ -203,10 +203,10 @@ resource "aws_appconfig_configuration_profile" "test" { validators { type = "JSON_SCHEMA" content = jsonencode({ - "$schema" = "http://json-schema.org/draft-04/schema#" - title = "$id$" - description = "BasicFeatureToggle-1" - type = "object" + "$schema" = "http://json-schema.org/draft-04/schema#" + title = "$id$" + description = "BasicFeatureToggle-1" + type = "object" additionalProperties = false patternProperties = { "[^\\s]+$" = { From 2f911b800a68ad481266593f95905d863273947d Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Wed, 12 May 2021 22:31:15 +0900 Subject: [PATCH 293/398] fix: fix typo --- aws/resource_aws_appconfig_configuration_profile.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_appconfig_configuration_profile.go b/aws/resource_aws_appconfig_configuration_profile.go index c0fce741008..331c1fc6372 100644 --- a/aws/resource_aws_appconfig_configuration_profile.go +++ b/aws/resource_aws_appconfig_configuration_profile.go @@ -211,7 +211,7 @@ func resourceAwsAppconfigConfigurationProfileUpdate(d *schema.ResourceData, meta } if d.HasChange("retrieval_role_arn") { - updateInput.Name = aws.String(d.Get("retrieval_role_arn").(string)) + updateInput.RetrievalRoleArn = aws.String(d.Get("retrieval_role_arn").(string)) } if d.HasChange("tags") { From eae43b84de10f91104bddc6ab64dfc00402ca4c4 Mon Sep 17 00:00:00 2001 From: Suzuki Shunsuke Date: Fri, 14 May 2021 20:36:12 +0900 Subject: [PATCH 294/398] fix: set RetrievalRoleArn only when the value isn't empty To avoid the following error. ``` Error: Error creating AppConfig ConfigurationProfile: InvalidParameter: 1 validation error(s) found. - minimum field size of 20, CreateConfigurationProfileInput.RetrievalRoleArn. ``` --- ...ource_aws_appconfig_configuration_profile.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/aws/resource_aws_appconfig_configuration_profile.go b/aws/resource_aws_appconfig_configuration_profile.go index 331c1fc6372..321aba0c793 100644 --- a/aws/resource_aws_appconfig_configuration_profile.go +++ b/aws/resource_aws_appconfig_configuration_profile.go @@ -97,13 +97,16 @@ func resourceAwsAppconfigConfigurationProfileCreate(d *schema.ResourceData, meta conn := meta.(*AWSClient).appconfigconn input := &appconfig.CreateConfigurationProfileInput{ - Name: aws.String(d.Get("name").(string)), - Description: aws.String(d.Get("description").(string)), - LocationUri: aws.String(d.Get("location_uri").(string)), - RetrievalRoleArn: aws.String(d.Get("retrieval_role_arn").(string)), - ApplicationId: aws.String(d.Get("application_id").(string)), - Validators: expandAppconfigValidators(d.Get("validators").([]interface{})), - Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().AppconfigTags(), + Name: aws.String(d.Get("name").(string)), + Description: aws.String(d.Get("description").(string)), + LocationUri: aws.String(d.Get("location_uri").(string)), + ApplicationId: aws.String(d.Get("application_id").(string)), + Validators: expandAppconfigValidators(d.Get("validators").([]interface{})), + Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().AppconfigTags(), + } + + if retrievalRoleARN := d.Get("retrieval_role_arn").(string); retrievalRoleARN != "" { + input.RetrievalRoleArn = aws.String(retrievalRoleARN) } profile, err := conn.CreateConfigurationProfile(input) From 9b6b71a320b34214bdde8ad2cb45216cd812a906 Mon Sep 17 00:00:00 2001 From: Suzuki Shunsuke Date: Mon, 12 Jul 2021 12:05:24 +0900 Subject: [PATCH 295/398] fix: fix the error `undefined: testAccAWSAppConfigApplicationName` --- aws/resource_aws_appconfig_configuration_profile_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_appconfig_configuration_profile_test.go b/aws/resource_aws_appconfig_configuration_profile_test.go index 4b873474fde..f594dad9bf6 100644 --- a/aws/resource_aws_appconfig_configuration_profile_test.go +++ b/aws/resource_aws_appconfig_configuration_profile_test.go @@ -194,7 +194,7 @@ func testAccCheckAWSAppConfigConfigurationProfileARN(resourceName string, profil func testAccAWSAppConfigConfigurationProfile(profileName, profileDesc string) string { appName := acctest.RandomWithPrefix("tf-acc-test") appDesc := acctest.RandomWithPrefix("desc") - return testAccAWSAppConfigApplicationName(appName, appDesc) + fmt.Sprintf(` + return testAccAWSAppConfigApplicationConfigDescription(appName, appDesc) + fmt.Sprintf(` resource "aws_appconfig_configuration_profile" "test" { name = %[1]q description = %[2]q From df7dcdabe328dda424215919fe1e78c7e68ef336 Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Tue, 11 May 2021 22:07:31 +0900 Subject: [PATCH 296/398] feat: add aws_appconfig_configuration_profile --- aws/provider.go | 1 + ...rce_aws_appconfig_configuration_profile.go | 222 ++++++++++++++++++ 2 files changed, 223 insertions(+) create mode 100644 aws/resource_aws_appconfig_configuration_profile.go diff --git a/aws/provider.go b/aws/provider.go index 90236fbf4ec..48e60dbd52c 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -508,6 +508,7 @@ func Provider() *schema.Provider { "aws_appautoscaling_scheduled_action": resourceAwsAppautoscalingScheduledAction(), "aws_appconfig_application": resourceAwsAppconfigApplication(), "aws_appconfig_environment": resourceAwsAppconfigEnvironment(), + "aws_appconfig_configuration_profile": resourceAwsAppconfigConfigurationProfile(), "aws_appmesh_gateway_route": resourceAwsAppmeshGatewayRoute(), "aws_appmesh_mesh": resourceAwsAppmeshMesh(), "aws_appmesh_route": resourceAwsAppmeshRoute(), diff --git a/aws/resource_aws_appconfig_configuration_profile.go b/aws/resource_aws_appconfig_configuration_profile.go new file mode 100644 index 00000000000..700fe6f0f61 --- /dev/null +++ b/aws/resource_aws_appconfig_configuration_profile.go @@ -0,0 +1,222 @@ +package aws + +import ( + "fmt" + "log" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" + "github.com/aws/aws-sdk-go/service/appconfig" + "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" +) + +func resourceAwsAppconfigConfigurationProfile() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsAppconfigConfigurationProfileCreate, + Read: resourceAwsAppconfigConfigurationProfileRead, + Update: resourceAwsAppconfigConfigurationProfileUpdate, + Delete: resourceAwsAppconfigConfigurationProfileDelete, + Importer: &schema.ResourceImporter{ + State: resourceAwsAppconfigConfigurationProfileImport, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 64), + ), + }, + "application_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(4, 7), + ), + }, + "location_uri": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(0, 2048), + ), + }, + "description": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.All( + validation.StringLenBetween(0, 1024), + ), + }, + "retrieval_role_arn": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.All( + validation.StringLenBetween(20, 2048), + ), + }, + // "validators": { + // Type: schema.TypeString, + // Optional: true, + // ValidateFunc: validation.All( + // validation.StringLenBetween(20, 2048), + // ), + // }, + "tags": tagsSchema(), + "arn": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceAwsAppconfigConfigurationProfileCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).appconfigconn + + input := &appconfig.CreateConfigurationProfileInput{ + Name: aws.String(d.Get("name").(string)), + Description: aws.String(d.Get("description").(string)), + LocationUri: aws.String(d.Get("location_uri").(string)), + RetrievalRoleArn: aws.String(d.Get("retrieval_role_arn").(string)), + ApplicationId: aws.String(d.Get("application_id").(string)), + Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().AppconfigTags(), + } + + profile, err := conn.CreateConfigurationProfile(input) + if err != nil { + return fmt.Errorf("Error creating AppConfig ConfigurationProfile: %s", err) + } + + d.SetId(aws.StringValue(profile.Id)) + + return resourceAwsAppconfigConfigurationProfileRead(d, meta) +} + +func resourceAwsAppconfigConfigurationProfileRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).appconfigconn + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + + appID := d.Get("application_id").(string) + + input := &appconfig.GetConfigurationProfileInput{ + ApplicationId: aws.String(appID), + ConfigurationProfileId: aws.String(d.Id()), + } + + output, err := conn.GetConfigurationProfile(input) + + if !d.IsNewResource() && isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + log.Printf("[WARN] Appconfig ConfigurationProfile (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error getting AppConfig ConfigurationProfile (%s): %s", d.Id(), err) + } + + if output == nil { + return fmt.Errorf("error getting AppConfig ConfigurationProfile (%s): empty response", d.Id()) + } + + d.Set("name", output.Name) + d.Set("description", output.Description) + d.Set("application_id", output.ApplicationId) + d.Set("location_uri", output.LocationUri) + d.Set("retrieval_role_arn", output.RetrievalRoleArn) + + profileARN := arn.ARN{ + AccountID: meta.(*AWSClient).accountid, + Partition: meta.(*AWSClient).partition, + Region: meta.(*AWSClient).region, + Resource: fmt.Sprintf("application/%s/configurationprofile/%s", appID, d.Id()), + Service: "appconfig", + }.String() + d.Set("arn", profileARN) + + tags, err := keyvaluetags.AppconfigListTags(conn, profileARN) + if err != nil { + return fmt.Errorf("error getting tags for AppConfig ConfigurationProfile (%s): %s", d.Id(), err) + } + + if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting tags: %s", err) + } + + return nil +} + +func resourceAwsAppconfigConfigurationProfileUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).appconfigconn + + updateInput := &appconfig.UpdateConfigurationProfileInput{ + ConfigurationProfileId: aws.String(d.Id()), + ApplicationId: aws.String(d.Get("application_id").(string)), + } + + if d.HasChange("description") { + updateInput.Description = aws.String(d.Get("description").(string)) + } + + if d.HasChange("name") { + updateInput.Name = aws.String(d.Get("name").(string)) + } + + if d.HasChange("retrieval_role_arn") { + updateInput.Name = aws.String(d.Get("retrieval_role_arn").(string)) + } + + if d.HasChange("tags") { + o, n := d.GetChange("tags") + if err := keyvaluetags.AppconfigUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return fmt.Errorf("error updating AppConfig (%s) tags: %s", d.Id(), err) + } + } + + _, err := conn.UpdateConfigurationProfile(updateInput) + if err != nil { + return fmt.Errorf("error updating AppConfig ConfigurationProfile (%s): %s", d.Id(), err) + } + + return resourceAwsAppconfigConfigurationProfileRead(d, meta) +} + +func resourceAwsAppconfigConfigurationProfileDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).appconfigconn + + input := &appconfig.DeleteConfigurationProfileInput{ + ConfigurationProfileId: aws.String(d.Id()), + ApplicationId: aws.String(d.Get("application_id").(string)), + } + + _, err := conn.DeleteConfigurationProfile(input) + + if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + return nil + } + + if err != nil { + return fmt.Errorf("error deleting Appconfig ConfigurationProfile (%s): %s", d.Id(), err) + } + + return nil +} + +func resourceAwsAppconfigConfigurationProfileImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + parts := strings.Split(d.Id(), "/") + if len(parts) != 2 { + return []*schema.ResourceData{}, fmt.Errorf("Wrong format of resource: %s. Please follow 'application-id/configurationprofile-id'", d.Id()) + } + + d.SetId(parts[1]) + d.Set("application_id", parts[0]) + + return []*schema.ResourceData{d}, nil +} From bf46b7fc3f37f1229331fa50666e7a2b295616c6 Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Tue, 11 May 2021 22:44:58 +0900 Subject: [PATCH 297/398] feat: support AppConfig Validators --- ...rce_aws_appconfig_configuration_profile.go | 59 ++++++++++++++++--- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/aws/resource_aws_appconfig_configuration_profile.go b/aws/resource_aws_appconfig_configuration_profile.go index 700fe6f0f61..c0fce741008 100644 --- a/aws/resource_aws_appconfig_configuration_profile.go +++ b/aws/resource_aws_appconfig_configuration_profile.go @@ -61,13 +61,29 @@ func resourceAwsAppconfigConfigurationProfile() *schema.Resource { validation.StringLenBetween(20, 2048), ), }, - // "validators": { - // Type: schema.TypeString, - // Optional: true, - // ValidateFunc: validation.All( - // validation.StringLenBetween(20, 2048), - // ), - // }, + "validators": { + Type: schema.TypeList, + Optional: true, + MaxItems: 2, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "content": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.All( + validation.StringLenBetween(0, 32768), + ), + }, + "type": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + "JSON_SCHEMA", "LAMBDA", + }, false), + }, + }, + }, + }, "tags": tagsSchema(), "arn": { Type: schema.TypeString, @@ -86,6 +102,7 @@ func resourceAwsAppconfigConfigurationProfileCreate(d *schema.ResourceData, meta LocationUri: aws.String(d.Get("location_uri").(string)), RetrievalRoleArn: aws.String(d.Get("retrieval_role_arn").(string)), ApplicationId: aws.String(d.Get("application_id").(string)), + Validators: expandAppconfigValidators(d.Get("validators").([]interface{})), Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().AppconfigTags(), } @@ -99,6 +116,29 @@ func resourceAwsAppconfigConfigurationProfileCreate(d *schema.ResourceData, meta return resourceAwsAppconfigConfigurationProfileRead(d, meta) } +func expandAppconfigValidators(list []interface{}) []*appconfig.Validator { + validators := make([]*appconfig.Validator, len(list)) + for i, validatorInterface := range list { + m := validatorInterface.(map[string]interface{}) + validators[i] = &appconfig.Validator{ + Content: aws.String(m["content"].(string)), + Type: aws.String(m["type"].(string)), + } + } + return validators +} + +func flattenAwsAppconfigValidators(validators []*appconfig.Validator) []interface{} { + list := make([]interface{}, len(validators)) + for i, validator := range validators { + list[i] = map[string]interface{}{ + "content": aws.StringValue(validator.Content), + "type": aws.StringValue(validator.Type), + } + } + return list +} + func resourceAwsAppconfigConfigurationProfileRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).appconfigconn ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig @@ -131,6 +171,7 @@ func resourceAwsAppconfigConfigurationProfileRead(d *schema.ResourceData, meta i d.Set("application_id", output.ApplicationId) d.Set("location_uri", output.LocationUri) d.Set("retrieval_role_arn", output.RetrievalRoleArn) + d.Set("validators", flattenAwsAppconfigValidators(output.Validators)) profileARN := arn.ARN{ AccountID: meta.(*AWSClient).accountid, @@ -180,6 +221,10 @@ func resourceAwsAppconfigConfigurationProfileUpdate(d *schema.ResourceData, meta } } + if d.HasChange("validators") { + updateInput.Validators = expandAppconfigValidators(d.Get("validators").([]interface{})) + } + _, err := conn.UpdateConfigurationProfile(updateInput) if err != nil { return fmt.Errorf("error updating AppConfig ConfigurationProfile (%s): %s", d.Id(), err) From b68724aa4f78b09d76fec19e677fddc44258b2c3 Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Tue, 11 May 2021 22:53:50 +0900 Subject: [PATCH 298/398] docs: add a document of aws_appconfig_configuration_profile --- ...config_configuration_profile.html.markdown | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 website/docs/r/appconfig_configuration_profile.html.markdown diff --git a/website/docs/r/appconfig_configuration_profile.html.markdown b/website/docs/r/appconfig_configuration_profile.html.markdown new file mode 100644 index 00000000000..862e1648474 --- /dev/null +++ b/website/docs/r/appconfig_configuration_profile.html.markdown @@ -0,0 +1,67 @@ +--- +subcategory: "AppConfig" +layout: "aws" +page_title: "AWS: aws_appconfig_configuration_profile" +description: |- + Provides an AppConfig Configuration Profile resource. +--- + +# Resource: aws_appconfig_configuration_profile + +Provides an AppConfig Configuration Profile resource. + +## Example Usage + +### AppConfig Configuration Profile + +```hcl +resource "aws_appconfig_configuration_profile" "production" { + name = "test" + description = "test" + application_id = aws_appconfig_application.test.id + validators { + content = "arn:aws:lambda:us-east-1:111111111111:function:test" + type = "LAMBDA" + } +} + +resource "aws_appconfig_application" "test" { + name = "test" + description = "Test" + tags = { + Type = "Test" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +- `name` - (Required) The environment name. Must be between 1 and 64 characters in length. +- `application_id` - (Required) The application id. Must be between 4 and 7 characters in length. +- `description` - (Optional) The description of the environment. Can be at most 1024 characters. +- `location_uri` - (Optional) A URI to locate the configuration. +- `validators` - (Optional) A list of methods for validating the configuration. Detailed below. +- `retrieval_role_arn` - (Optional) The ARN of an IAM role with permission to access the configuration at the specified LocationUri. +- `tags` - (Optional) A map of tags to assign to the resource. + +### validator + +- `content` - (Optional) Either the JSON Schema content or the Amazon Resource Name (ARN) of an AWS Lambda function. +- `type` - (Optional) AWS AppConfig supports validators of type `JSON_SCHEMA` and `LAMBDA` + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +- `arn` - The Amazon Resource Name (ARN) of the AppConfig Configuration Profile. +- `id` - The AppConfig Configuration Profile ID + +## Import + +`aws_appconfig_configuration_profile` can be imported by the Application ID and Configuration Profile ID, e.g. + +``` +$ terraform import aws_appconfig_configuration_profile.test 71abcde/11xxxxx +``` From 5c1b1da230281fff6a652e21873e5cd93413622a Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Wed, 12 May 2021 07:20:57 +0900 Subject: [PATCH 299/398] test: add tests for aws_appconfig_configuration_profile --- ...ws_appconfig_configuration_profile_test.go | 258 ++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 aws/resource_aws_appconfig_configuration_profile_test.go diff --git a/aws/resource_aws_appconfig_configuration_profile_test.go b/aws/resource_aws_appconfig_configuration_profile_test.go new file mode 100644 index 00000000000..a25999e52ff --- /dev/null +++ b/aws/resource_aws_appconfig_configuration_profile_test.go @@ -0,0 +1,258 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/appconfig" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccAWSAppConfigConfigurationProfile_basic(t *testing.T) { + var profile appconfig.GetConfigurationProfileOutput + profileName := acctest.RandomWithPrefix("tf-acc-test") + profileDesc := acctest.RandomWithPrefix("desc") + resourceName := "aws_appconfig_configuration_profile.test" + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigConfigurationProfileDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigConfigurationProfile(profileName, profileDesc), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigConfigurationProfileExists(resourceName, &profile), + resource.TestCheckResourceAttr(resourceName, "name", profileName), + testAccCheckAWSAppConfigConfigurationProfileARN(resourceName, &profile), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "description", profileDesc), + ), + }, + { + ResourceName: resourceName, + ImportStateIdFunc: testAccAWSAppConfigConfigurationProfileImportStateIdFunc(resourceName), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSAppConfigConfigurationProfile_disappears(t *testing.T) { + var profile appconfig.GetConfigurationProfileOutput + + profileName := acctest.RandomWithPrefix("tf-acc-test") + profileDesc := acctest.RandomWithPrefix("desc") + resourceName := "aws_appconfig_configuration_profile.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigConfigurationProfileDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigConfigurationProfile(profileName, profileDesc), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigConfigurationProfileExists(resourceName, &profile), + testAccCheckAWSAppConfigConfigurationProfileDisappears(&profile), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAWSAppConfigConfigurationProfile_Tags(t *testing.T) { + var profile appconfig.GetConfigurationProfileOutput + + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_appconfig_configuration_profile.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigConfigurationProfileDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigConfigurationProfileTags1(rName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigConfigurationProfileExists(resourceName, &profile), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportStateIdFunc: testAccAWSAppConfigConfigurationProfileImportStateIdFunc(resourceName), + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSAppConfigConfigurationProfileTags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigConfigurationProfileExists(resourceName, &profile), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccAWSAppConfigConfigurationProfileTags1(rName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigConfigurationProfileExists(resourceName, &profile), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + }, + }) +} + +func testAccCheckAppConfigConfigurationProfileDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).appconfigconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_appconfig_configuration_profile" { + continue + } + + input := &appconfig.GetConfigurationProfileInput{ + ApplicationId: aws.String(rs.Primary.Attributes["application_id"]), + ConfigurationProfileId: aws.String(rs.Primary.ID), + } + + output, err := conn.GetConfigurationProfile(input) + + if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + continue + } + + if err != nil { + return err + } + + if output != nil { + return fmt.Errorf("AppConfig ConfigurationProfile (%s) still exists", rs.Primary.ID) + } + } + + return nil +} + +func testAccCheckAWSAppConfigConfigurationProfileDisappears(profile *appconfig.GetConfigurationProfileOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).appconfigconn + + _, err := conn.DeleteConfigurationProfile(&appconfig.DeleteConfigurationProfileInput{ + ApplicationId: profile.ApplicationId, + ConfigurationProfileId: profile.Id, + }) + + return err + } +} + +func testAccCheckAWSAppConfigConfigurationProfileExists(resourceName string, profile *appconfig.GetConfigurationProfileOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Resource not found: %s", resourceName) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("Resource (%s) ID not set", resourceName) + } + + conn := testAccProvider.Meta().(*AWSClient).appconfigconn + + output, err := conn.GetConfigurationProfile(&appconfig.GetConfigurationProfileInput{ + ApplicationId: aws.String(rs.Primary.Attributes["application_id"]), + ConfigurationProfileId: aws.String(rs.Primary.ID), + }) + if err != nil { + return err + } + + *profile = *output + + return nil + } +} + +func testAccCheckAWSAppConfigConfigurationProfileARN(resourceName string, profile *appconfig.GetConfigurationProfileOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + return testAccCheckResourceAttrRegionalARN(resourceName, "arn", "appconfig", fmt.Sprintf("application/%s/configurationprofile/%s", aws.StringValue(profile.ApplicationId), aws.StringValue(profile.Id)))(s) + } +} + +func testAccAWSAppConfigConfigurationProfile(profileName, profileDesc string) string { + appName := acctest.RandomWithPrefix("tf-acc-test") + appDesc := acctest.RandomWithPrefix("desc") + return testAccAWSAppConfigApplicationName(appName, appDesc) + fmt.Sprintf(` +resource "aws_appconfig_configuration_profile" "test" { + name = %[1]q + description = %[2]q + application_id = aws_appconfig_application.test.id + validators { + type = "JSON_SCHEMA" + content = jsonencode({ + "$schema" = "http://json-schema.org/draft-04/schema#" + title = "$id$" + description = "BasicFeatureToggle-1" + type = "object" + additionalProperties = false + patternProperties = { + "[^\\s]+$" = { + type = "boolean" + } + } + minProperties = 1 + }) + } +} +`, profileName, profileDesc) +} + +func testAccAWSAppConfigConfigurationProfileTags1(rName, tagKey1, tagValue1 string) string { + return testAccAWSAppConfigApplicationTags1(rName, tagKey1, tagValue1) + fmt.Sprintf(` +resource "aws_appconfig_configuration_profile" "test" { + name = %[1]q + application_id = aws_appconfig_application.test.id + + tags = { + %[2]q = %[3]q + } +} +`, rName, tagKey1, tagValue1) +} + +func testAccAWSAppConfigConfigurationProfileTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return testAccAWSAppConfigApplicationTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2) + fmt.Sprintf(` +resource "aws_appconfig_configuration_profile" "test" { + name = %[1]q + application_id = aws_appconfig_application.test.id + + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } +} +`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +} + +func testAccAWSAppConfigConfigurationProfileImportStateIdFunc(resourceName string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return "", fmt.Errorf("Not Found: %s", resourceName) + } + + return fmt.Sprintf("%s/%s", rs.Primary.Attributes["application_id"], rs.Primary.ID), nil + } +} From ccc26509da0c853bc94db101564794e2cda9802a Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Wed, 12 May 2021 09:39:35 +0900 Subject: [PATCH 300/398] docs: fix document of aws_appconfig_configuration_profile --- website/docs/r/appconfig_configuration_profile.html.markdown | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/website/docs/r/appconfig_configuration_profile.html.markdown b/website/docs/r/appconfig_configuration_profile.html.markdown index 862e1648474..01da1fef385 100644 --- a/website/docs/r/appconfig_configuration_profile.html.markdown +++ b/website/docs/r/appconfig_configuration_profile.html.markdown @@ -19,6 +19,7 @@ resource "aws_appconfig_configuration_profile" "production" { name = "test" description = "test" application_id = aws_appconfig_application.test.id + location_uri = "hosted" validators { content = "arn:aws:lambda:us-east-1:111111111111:function:test" type = "LAMBDA" @@ -39,9 +40,9 @@ resource "aws_appconfig_application" "test" { The following arguments are supported: - `name` - (Required) The environment name. Must be between 1 and 64 characters in length. -- `application_id` - (Required) The application id. Must be between 4 and 7 characters in length. +- `application_id` - (Required, Forces new resource) The application id. Must be between 4 and 7 characters in length. +- `location_uri` - (Required, Forces new resource) A URI to locate the configuration. - `description` - (Optional) The description of the environment. Can be at most 1024 characters. -- `location_uri` - (Optional) A URI to locate the configuration. - `validators` - (Optional) A list of methods for validating the configuration. Detailed below. - `retrieval_role_arn` - (Optional) The ARN of an IAM role with permission to access the configuration at the specified LocationUri. - `tags` - (Optional) A map of tags to assign to the resource. From 404d894fab1ed83001fcd6f1c9c4b5841d48faab Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Wed, 12 May 2021 09:41:16 +0900 Subject: [PATCH 301/398] test: specify location_uri --- aws/resource_aws_appconfig_configuration_profile_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aws/resource_aws_appconfig_configuration_profile_test.go b/aws/resource_aws_appconfig_configuration_profile_test.go index a25999e52ff..8b08b7130e7 100644 --- a/aws/resource_aws_appconfig_configuration_profile_test.go +++ b/aws/resource_aws_appconfig_configuration_profile_test.go @@ -199,6 +199,7 @@ resource "aws_appconfig_configuration_profile" "test" { name = %[1]q description = %[2]q application_id = aws_appconfig_application.test.id + location_uri = "hosted" validators { type = "JSON_SCHEMA" content = jsonencode({ @@ -224,6 +225,7 @@ func testAccAWSAppConfigConfigurationProfileTags1(rName, tagKey1, tagValue1 stri resource "aws_appconfig_configuration_profile" "test" { name = %[1]q application_id = aws_appconfig_application.test.id + location_uri = "hosted" tags = { %[2]q = %[3]q @@ -237,6 +239,7 @@ func testAccAWSAppConfigConfigurationProfileTags2(rName, tagKey1, tagValue1, tag resource "aws_appconfig_configuration_profile" "test" { name = %[1]q application_id = aws_appconfig_application.test.id + location_uri = "hosted" tags = { %[2]q = %[3]q From ad8463fb8c15ea27858b13264aa480cec07968b6 Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Wed, 12 May 2021 09:42:45 +0900 Subject: [PATCH 302/398] docs: fix typo --- website/docs/r/appconfig_configuration_profile.html.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/r/appconfig_configuration_profile.html.markdown b/website/docs/r/appconfig_configuration_profile.html.markdown index 01da1fef385..c3974e546ee 100644 --- a/website/docs/r/appconfig_configuration_profile.html.markdown +++ b/website/docs/r/appconfig_configuration_profile.html.markdown @@ -39,10 +39,10 @@ resource "aws_appconfig_application" "test" { The following arguments are supported: -- `name` - (Required) The environment name. Must be between 1 and 64 characters in length. +- `name` - (Required) The configuration profile name. Must be between 1 and 64 characters in length. - `application_id` - (Required, Forces new resource) The application id. Must be between 4 and 7 characters in length. - `location_uri` - (Required, Forces new resource) A URI to locate the configuration. -- `description` - (Optional) The description of the environment. Can be at most 1024 characters. +- `description` - (Optional) The description of the configuration profile. Can be at most 1024 characters. - `validators` - (Optional) A list of methods for validating the configuration. Detailed below. - `retrieval_role_arn` - (Optional) The ARN of an IAM role with permission to access the configuration at the specified LocationUri. - `tags` - (Optional) A map of tags to assign to the resource. From e4d694558a6223cba25f7a4371da18d73ed6fbae Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Wed, 12 May 2021 09:45:38 +0900 Subject: [PATCH 303/398] style: format code --- aws/resource_aws_appconfig_configuration_profile_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/aws/resource_aws_appconfig_configuration_profile_test.go b/aws/resource_aws_appconfig_configuration_profile_test.go index 8b08b7130e7..4b873474fde 100644 --- a/aws/resource_aws_appconfig_configuration_profile_test.go +++ b/aws/resource_aws_appconfig_configuration_profile_test.go @@ -203,10 +203,10 @@ resource "aws_appconfig_configuration_profile" "test" { validators { type = "JSON_SCHEMA" content = jsonencode({ - "$schema" = "http://json-schema.org/draft-04/schema#" - title = "$id$" - description = "BasicFeatureToggle-1" - type = "object" + "$schema" = "http://json-schema.org/draft-04/schema#" + title = "$id$" + description = "BasicFeatureToggle-1" + type = "object" additionalProperties = false patternProperties = { "[^\\s]+$" = { From 68586b5810e52e0bfd7a865dc53ee995c7a27585 Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Wed, 12 May 2021 22:31:15 +0900 Subject: [PATCH 304/398] fix: fix typo --- aws/resource_aws_appconfig_configuration_profile.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_appconfig_configuration_profile.go b/aws/resource_aws_appconfig_configuration_profile.go index c0fce741008..331c1fc6372 100644 --- a/aws/resource_aws_appconfig_configuration_profile.go +++ b/aws/resource_aws_appconfig_configuration_profile.go @@ -211,7 +211,7 @@ func resourceAwsAppconfigConfigurationProfileUpdate(d *schema.ResourceData, meta } if d.HasChange("retrieval_role_arn") { - updateInput.Name = aws.String(d.Get("retrieval_role_arn").(string)) + updateInput.RetrievalRoleArn = aws.String(d.Get("retrieval_role_arn").(string)) } if d.HasChange("tags") { From ef6252e9e01014ad5ed113a92736d24834579977 Mon Sep 17 00:00:00 2001 From: Suzuki Shunsuke Date: Fri, 14 May 2021 20:36:12 +0900 Subject: [PATCH 305/398] fix: set RetrievalRoleArn only when the value isn't empty To avoid the following error. ``` Error: Error creating AppConfig ConfigurationProfile: InvalidParameter: 1 validation error(s) found. - minimum field size of 20, CreateConfigurationProfileInput.RetrievalRoleArn. ``` --- ...ource_aws_appconfig_configuration_profile.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/aws/resource_aws_appconfig_configuration_profile.go b/aws/resource_aws_appconfig_configuration_profile.go index 331c1fc6372..321aba0c793 100644 --- a/aws/resource_aws_appconfig_configuration_profile.go +++ b/aws/resource_aws_appconfig_configuration_profile.go @@ -97,13 +97,16 @@ func resourceAwsAppconfigConfigurationProfileCreate(d *schema.ResourceData, meta conn := meta.(*AWSClient).appconfigconn input := &appconfig.CreateConfigurationProfileInput{ - Name: aws.String(d.Get("name").(string)), - Description: aws.String(d.Get("description").(string)), - LocationUri: aws.String(d.Get("location_uri").(string)), - RetrievalRoleArn: aws.String(d.Get("retrieval_role_arn").(string)), - ApplicationId: aws.String(d.Get("application_id").(string)), - Validators: expandAppconfigValidators(d.Get("validators").([]interface{})), - Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().AppconfigTags(), + Name: aws.String(d.Get("name").(string)), + Description: aws.String(d.Get("description").(string)), + LocationUri: aws.String(d.Get("location_uri").(string)), + ApplicationId: aws.String(d.Get("application_id").(string)), + Validators: expandAppconfigValidators(d.Get("validators").([]interface{})), + Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().AppconfigTags(), + } + + if retrievalRoleARN := d.Get("retrieval_role_arn").(string); retrievalRoleARN != "" { + input.RetrievalRoleArn = aws.String(retrievalRoleARN) } profile, err := conn.CreateConfigurationProfile(input) From 2f17a3d1350e668db928108c9b9940a259875908 Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Wed, 12 May 2021 09:02:12 +0900 Subject: [PATCH 306/398] feat: add aws_appconfig_hosted_configuration_version --- aws/provider.go | 1 + ..._appconfig_hosted_configuration_version.go | 168 ++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 aws/resource_aws_appconfig_hosted_configuration_version.go diff --git a/aws/provider.go b/aws/provider.go index 48e60dbd52c..6555293a310 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -509,6 +509,7 @@ func Provider() *schema.Provider { "aws_appconfig_application": resourceAwsAppconfigApplication(), "aws_appconfig_environment": resourceAwsAppconfigEnvironment(), "aws_appconfig_configuration_profile": resourceAwsAppconfigConfigurationProfile(), + "aws_appconfig_hosted_configuration_version": resourceAwsAppconfigHostedConfigurationVersion(), "aws_appmesh_gateway_route": resourceAwsAppmeshGatewayRoute(), "aws_appmesh_mesh": resourceAwsAppmeshMesh(), "aws_appmesh_route": resourceAwsAppmeshRoute(), diff --git a/aws/resource_aws_appconfig_hosted_configuration_version.go b/aws/resource_aws_appconfig_hosted_configuration_version.go new file mode 100644 index 00000000000..f7471fe0331 --- /dev/null +++ b/aws/resource_aws_appconfig_hosted_configuration_version.go @@ -0,0 +1,168 @@ +package aws + +import ( + "fmt" + "log" + "strconv" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/appconfig" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceAwsAppconfigHostedConfigurationVersion() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsAppconfigHostedConfigurationVersionCreate, + Read: resourceAwsAppconfigHostedConfigurationVersionRead, + Update: resourceAwsAppconfigHostedConfigurationVersionUpdate, + Delete: resourceAwsAppconfigHostedConfigurationVersionDelete, + Importer: &schema.ResourceImporter{ + State: resourceAwsAppconfigHostedConfigurationVersionImport, + }, + + Schema: map[string]*schema.Schema{ + "application_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(4, 7), + ), + }, + "configuration_profile_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(4, 7), + ), + }, + "content": { + Type: schema.TypeString, + Required: true, + }, + "content_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 255), + ), + }, + "description": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.All( + validation.StringLenBetween(0, 1024), + ), + }, + "version_number": { + Type: schema.TypeInt, + Computed: true, + }, + }, + } +} + +func resourceAwsAppconfigHostedConfigurationVersionCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).appconfigconn + + appID := d.Get("application_id").(string) + profileID := d.Get("configuration_profile_id").(string) + + input := &appconfig.CreateHostedConfigurationVersionInput{ + ApplicationId: aws.String(appID), + ConfigurationProfileId: aws.String(profileID), + Content: []byte(d.Get("content").(string)), + ContentType: aws.String(d.Get("content_type").(string)), + Description: aws.String(d.Get("description").(string)), + } + + hcv, err := conn.CreateHostedConfigurationVersion(input) + if err != nil { + return fmt.Errorf("Error creating AppConfig HostedConfigurationVersion: %s", err) + } + + d.SetId(fmt.Sprintf("%s/%s/%d", appID, profileID, aws.Int64Value(hcv.VersionNumber))) + d.Set("version_number", hcv.VersionNumber) + + return resourceAwsAppconfigHostedConfigurationVersionRead(d, meta) +} + +func resourceAwsAppconfigHostedConfigurationVersionRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).appconfigconn + + input := &appconfig.GetHostedConfigurationVersionInput{ + ApplicationId: aws.String(d.Get("application_id").(string)), + ConfigurationProfileId: aws.String(d.Get("configuration_profile_id").(string)), + VersionNumber: aws.Int64(int64(d.Get("version_number").(int))), + } + + output, err := conn.GetHostedConfigurationVersion(input) + + if !d.IsNewResource() && isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + log.Printf("[WARN] Appconfig HostedConfigurationVersion (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error getting AppConfig HostedConfigurationVersion (%s): %s", d.Id(), err) + } + + if output == nil { + return fmt.Errorf("error getting AppConfig HostedConfigurationVersion (%s): empty response", d.Id()) + } + + d.Set("description", output.Description) + d.Set("content", string(output.Content)) + d.Set("content_type", output.ContentType) + + return nil +} + +func resourceAwsAppconfigHostedConfigurationVersionUpdate(d *schema.ResourceData, meta interface{}) error { + return resourceAwsAppconfigHostedConfigurationVersionCreate(d, meta) +} + +func resourceAwsAppconfigHostedConfigurationVersionDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).appconfigconn + + input := &appconfig.DeleteHostedConfigurationVersionInput{ + ConfigurationProfileId: aws.String(d.Get("configuration_profile_id").(string)), + ApplicationId: aws.String(d.Get("application_id").(string)), + VersionNumber: aws.Int64(d.Get("version_number").(int64)), + } + + _, err := conn.DeleteHostedConfigurationVersion(input) + + if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + return nil + } + + if err != nil { + return fmt.Errorf("error deleting Appconfig HostedConfigurationVersion (%s): %s", d.Id(), err) + } + + return nil +} + +func resourceAwsAppconfigHostedConfigurationVersionImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + parts := strings.Split(d.Id(), "/") + if len(parts) != 3 { + return nil, fmt.Errorf("Wrong format of resource: %s. Please follow 'application-id/configurationprofile-id/version-number'", d.Id()) + } + + verString := parts[2] + verNumber, err := strconv.Atoi(verString) + if err != nil { + return nil, fmt.Errorf("version-number must be integer: %s: %w", verString, err) + } + + d.Set("application_id", parts[0]) + d.Set("configuration_profile_id", parts[1]) + d.Set("version_number", verNumber) + + return []*schema.ResourceData{d}, nil +} From 7578a0ec4c8b9563bda6dbe9067a629a1c2de803 Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Wed, 12 May 2021 09:34:13 +0900 Subject: [PATCH 307/398] docs: add document of appconfig_hosted_configuration_version --- ...hosted_configuration_version.html.markdown | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 website/docs/r/appconfig_hosted_configuration_version.html.markdown diff --git a/website/docs/r/appconfig_hosted_configuration_version.html.markdown b/website/docs/r/appconfig_hosted_configuration_version.html.markdown new file mode 100644 index 00000000000..1482f5ecb80 --- /dev/null +++ b/website/docs/r/appconfig_hosted_configuration_version.html.markdown @@ -0,0 +1,62 @@ +--- +subcategory: "AppConfig" +layout: "aws" +page_title: "AWS: aws_appconfig_hosted_configuration_version" +description: |- + Provides an AppConfig Hosted Configuration Version resource. +--- + +# Resource: aws_appconfig_hosted_configuration_version + +Provides an AppConfig Hosted Configuration Version resource. + +## Example Usage + +### AppConfig Hosted Configuration Version + +```hcl +resource "aws_appconfig_hosted_configuration_version" "production" { + application_id = aws_appconfig_application.test.id + configuration_profile_id = aws_appconfig_configuration_profile.test.id + description = "test" + content_type = "application/json" + content = jsonencode({ + foo = "foo" + }) +} + +resource "aws_appconfig_application" "test" { + name = "test" +} + +resource "aws_appconfig_configuration_profile" "test" { + name = "test" + application_id = aws_appconfig_application.test.id + location_uri = "hosted" +} +``` + +## Argument Reference + +The following arguments are supported: + +- `application_id` - (Required) The application id. +- `configuration_profile_id` - (Required) The configuration profile ID. +- `content` - (Required) The content of the configuration or the configuration data. +- `content_type` - (Required) A standard MIME type describing the format of the configuration content. +- `description` - (Optional) A description of the configuration. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +- `version_number` - hosted configuration version +- `id` - `//` + +## Import + +`aws_appconfig_hosted_configuration_version` can be imported by the Application ID and Configuration Profile ID and Hosted Configuration Version Number, e.g. + +``` +$ terraform import aws_appconfig_hosted_configuration_version.test 71abcde/11xxxxx/2 +``` From 9d464419e3de6c32d48a6c954f664da7604853e4 Mon Sep 17 00:00:00 2001 From: Suzuki Shunsuke Date: Fri, 14 May 2021 21:37:43 +0900 Subject: [PATCH 308/398] test: add tests of aws_appconfig_hosted_configuration_version --- ...onfig_hosted_configuration_version_test.go | 178 ++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 aws/resource_aws_appconfig_hosted_configuration_version_test.go diff --git a/aws/resource_aws_appconfig_hosted_configuration_version_test.go b/aws/resource_aws_appconfig_hosted_configuration_version_test.go new file mode 100644 index 00000000000..94971188037 --- /dev/null +++ b/aws/resource_aws_appconfig_hosted_configuration_version_test.go @@ -0,0 +1,178 @@ +package aws + +import ( + "fmt" + "strconv" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/appconfig" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccAWSAppConfigHostedConfigurationVersion_basic(t *testing.T) { + var out appconfig.GetHostedConfigurationVersionOutput + resourceName := "aws_appconfig_hosted_configuration_version.test" + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigHostedConfigurationVersionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigHostedConfigurationVersion(), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigHostedConfigurationVersionExists(resourceName, &out), + testAccCheckAWSAppConfigHostedConfigurationVersionARN(resourceName, &out), + resource.TestCheckResourceAttr(resourceName, "description", "hosted configuration version description"), + ), + }, + { + ResourceName: resourceName, + ImportStateIdFunc: testAccAWSAppConfigHostedConfigurationVersionImportStateIdFunc(resourceName), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSAppConfigHostedConfigurationVersion_disappears(t *testing.T) { + var out appconfig.GetHostedConfigurationVersionOutput + resourceName := "aws_appconfig_hosted_configuration_version.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigHostedConfigurationVersionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigHostedConfigurationVersion(), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigHostedConfigurationVersionExists(resourceName, &out), + testAccCheckAWSAppConfigHostedConfigurationVersionDisappears(&out), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckAppConfigHostedConfigurationVersionDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).appconfigconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_appconfig_hosted_configuration_version" { + continue + } + versionNumber, err := strconv.Atoi(rs.Primary.Attributes["version_number"]) + if err != nil { + return fmt.Errorf("failed to convert version_number into int (%s): %w", rs.Primary.Attributes["version_number"], err) + } + + input := &appconfig.GetHostedConfigurationVersionInput{ + ApplicationId: aws.String(rs.Primary.Attributes["application_id"]), + ConfigurationProfileId: aws.String(rs.Primary.Attributes["configuration_profile_id"]), + VersionNumber: aws.Int64(int64(versionNumber)), + } + + output, err := conn.GetHostedConfigurationVersion(input) + + if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + continue + } + + if err != nil { + return err + } + + if output != nil { + return fmt.Errorf("AppConfig HostedConfigurationVersion (%s) still exists", rs.Primary.ID) + } + } + + return nil +} + +func testAccCheckAWSAppConfigHostedConfigurationVersionDisappears(hcv *appconfig.GetHostedConfigurationVersionOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).appconfigconn + + _, err := conn.DeleteHostedConfigurationVersion(&appconfig.DeleteHostedConfigurationVersionInput{ + ApplicationId: hcv.ApplicationId, + ConfigurationProfileId: hcv.ConfigurationProfileId, + VersionNumber: hcv.VersionNumber, + }) + + return err + } +} + +func testAccCheckAWSAppConfigHostedConfigurationVersionExists(resourceName string, hcv *appconfig.GetHostedConfigurationVersionOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Resource not found: %s", resourceName) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("Resource (%s) ID not set", resourceName) + } + + conn := testAccProvider.Meta().(*AWSClient).appconfigconn + + versionNumber, err := strconv.Atoi(rs.Primary.Attributes["version_number"]) + if err != nil { + return fmt.Errorf("failed to convert version_number into int (%s): %w", rs.Primary.Attributes["version_number"], err) + } + + output, err := conn.GetHostedConfigurationVersion(&appconfig.GetHostedConfigurationVersionInput{ + ApplicationId: aws.String(rs.Primary.Attributes["application_id"]), + ConfigurationProfileId: aws.String(rs.Primary.Attributes["configuration_profile_id"]), + VersionNumber: aws.Int64(int64(versionNumber)), + }) + if err != nil { + return err + } + + *hcv = *output + + return nil + } +} + +func testAccCheckAWSAppConfigHostedConfigurationVersionARN(resourceName string, hcv *appconfig.GetHostedConfigurationVersionOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + return testAccCheckResourceAttrRegionalARN(resourceName, "arn", "appconfig", fmt.Sprintf("application/%s/configurationprofile/%s/hostedconfigurationversion/%d", aws.StringValue(hcv.ApplicationId), aws.StringValue(hcv.ConfigurationProfileId), aws.Int64Value(hcv.VersionNumber)))(s) + } +} + +func testAccAWSAppConfigHostedConfigurationVersion() string { + appName := acctest.RandomWithPrefix("tf-acc-test") + profileName := acctest.RandomWithPrefix("tf-acc-test") + return testAccAWSAppConfigApplicationName(appName, "test") + testAccAWSAppConfigConfigurationProfile(profileName, "test") + ` +resource "aws_appconfig_hosted_configuration_version" "test" { + application_id = aws_appconfig_application.test.id + configuration_profile_id = aws_appconfig_configuration_profile.test.id + description = "hosted configuration version description" + content_type = "application/json" + content = jsonencode({ + foo = "foo" + }) +} +` +} + +func testAccAWSAppConfigHostedConfigurationVersionImportStateIdFunc(resourceName string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return "", fmt.Errorf("Not Found: %s", resourceName) + } + + return rs.Primary.ID, nil + } +} From ec19afb46a36de3f7dcbdb64cadf51f819943da0 Mon Sep 17 00:00:00 2001 From: Suzuki Shunsuke Date: Mon, 12 Jul 2021 12:05:24 +0900 Subject: [PATCH 309/398] fix: fix the error `undefined: testAccAWSAppConfigApplicationName` --- aws/resource_aws_appconfig_configuration_profile_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_appconfig_configuration_profile_test.go b/aws/resource_aws_appconfig_configuration_profile_test.go index 4b873474fde..f594dad9bf6 100644 --- a/aws/resource_aws_appconfig_configuration_profile_test.go +++ b/aws/resource_aws_appconfig_configuration_profile_test.go @@ -194,7 +194,7 @@ func testAccCheckAWSAppConfigConfigurationProfileARN(resourceName string, profil func testAccAWSAppConfigConfigurationProfile(profileName, profileDesc string) string { appName := acctest.RandomWithPrefix("tf-acc-test") appDesc := acctest.RandomWithPrefix("desc") - return testAccAWSAppConfigApplicationName(appName, appDesc) + fmt.Sprintf(` + return testAccAWSAppConfigApplicationConfigDescription(appName, appDesc) + fmt.Sprintf(` resource "aws_appconfig_configuration_profile" "test" { name = %[1]q description = %[2]q From 3a621b2fb5b76f826e6544780f2c1ca27eab3d68 Mon Sep 17 00:00:00 2001 From: Suzuki Shunsuke Date: Mon, 12 Jul 2021 12:15:55 +0900 Subject: [PATCH 310/398] fix: fix the error `undefined: testAccAWSAppConfigApplicationName` --- aws/resource_aws_appconfig_hosted_configuration_version_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_appconfig_hosted_configuration_version_test.go b/aws/resource_aws_appconfig_hosted_configuration_version_test.go index 94971188037..8215e67cf9a 100644 --- a/aws/resource_aws_appconfig_hosted_configuration_version_test.go +++ b/aws/resource_aws_appconfig_hosted_configuration_version_test.go @@ -153,7 +153,7 @@ func testAccCheckAWSAppConfigHostedConfigurationVersionARN(resourceName string, func testAccAWSAppConfigHostedConfigurationVersion() string { appName := acctest.RandomWithPrefix("tf-acc-test") profileName := acctest.RandomWithPrefix("tf-acc-test") - return testAccAWSAppConfigApplicationName(appName, "test") + testAccAWSAppConfigConfigurationProfile(profileName, "test") + ` + return testAccAWSAppConfigApplicationConfigDescription(appName, "test") + testAccAWSAppConfigConfigurationProfile(profileName, "test") + ` resource "aws_appconfig_hosted_configuration_version" "test" { application_id = aws_appconfig_application.test.id configuration_profile_id = aws_appconfig_configuration_profile.test.id From b43c26ad1a9e67be834dd4cfd6b4770e6c7bd5ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jul 2021 06:10:59 +0000 Subject: [PATCH 311/398] build(deps): bump github.com/aws/aws-sdk-go in /awsproviderlint Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.39.3 to 1.39.4. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Changelog](https://github.com/aws/aws-sdk-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.39.3...v1.39.4) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- awsproviderlint/go.mod | 2 +- awsproviderlint/go.sum | 4 +-- .../aws/aws-sdk-go/aws/endpoints/defaults.go | 25 +++++++++++++++++++ .../github.com/aws/aws-sdk-go/aws/version.go | 2 +- awsproviderlint/vendor/modules.txt | 2 +- 5 files changed, 30 insertions(+), 5 deletions(-) diff --git a/awsproviderlint/go.mod b/awsproviderlint/go.mod index c457e5dd1a1..1a77c9b93d8 100644 --- a/awsproviderlint/go.mod +++ b/awsproviderlint/go.mod @@ -3,7 +3,7 @@ module github.com/terraform-providers/terraform-provider-aws/awsproviderlint go 1.16 require ( - github.com/aws/aws-sdk-go v1.39.3 + github.com/aws/aws-sdk-go v1.39.4 github.com/bflad/tfproviderlint v0.27.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.7.0 golang.org/x/tools v0.0.0-20201028111035-eafbe7b904eb diff --git a/awsproviderlint/go.sum b/awsproviderlint/go.sum index 4816e45d0e3..b9de0de284d 100644 --- a/awsproviderlint/go.sum +++ b/awsproviderlint/go.sum @@ -70,8 +70,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= github.com/aws/aws-sdk-go v1.25.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.39.3 h1:JMDk7p+AV89MdVy/ZcFWAGivWIE3vXOsRriFjFWVcIY= -github.com/aws/aws-sdk-go v1.39.3/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= +github.com/aws/aws-sdk-go v1.39.4 h1:nXBChUaG5cinrl3yg4/rUyssOOLH/ohk4S9K03kJirE= +github.com/aws/aws-sdk-go v1.39.4/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/bflad/gopaniccheck v0.1.0 h1:tJftp+bv42ouERmUMWLoUn/5bi/iQZjHPznM00cP/bU= github.com/bflad/gopaniccheck v0.1.0/go.mod h1:ZCj2vSr7EqVeDaqVsWN4n2MwdROx1YL+LFo47TSWtsA= github.com/bflad/tfproviderlint v0.27.0 h1:KXF+dYaWJ/OSVyWIrk2NIYgQBMDDSOC4VQB/P+P5nhI= diff --git a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go index cebbe397201..60ff236dfd5 100644 --- a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go +++ b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go @@ -3843,6 +3843,18 @@ var awsPartition = partition{ "iotwireless": service{ Endpoints: endpoints{ + "ap-northeast-1": endpoint{ + Hostname: "api.iotwireless.ap-northeast-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-northeast-1", + }, + }, + "ap-southeast-2": endpoint{ + Hostname: "api.iotwireless.ap-southeast-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-southeast-2", + }, + }, "eu-west-1": endpoint{ Hostname: "api.iotwireless.eu-west-1.amazonaws.com", CredentialScope: credentialScope{ @@ -3855,6 +3867,12 @@ var awsPartition = partition{ Region: "us-east-1", }, }, + "us-west-2": endpoint{ + Hostname: "api.iotwireless.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, }, }, "kafka": service{ @@ -8442,6 +8460,13 @@ var awscnPartition = partition{ }, }, }, + "transcribestreaming": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, "transfer": service{ Endpoints: endpoints{ diff --git a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/version.go b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/version.go index 19a602480f0..8b253ba02b1 100644 --- a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/version.go +++ b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/version.go @@ -5,4 +5,4 @@ package aws const SDKName = "aws-sdk-go" // SDKVersion is the version of this SDK -const SDKVersion = "1.39.3" +const SDKVersion = "1.39.4" diff --git a/awsproviderlint/vendor/modules.txt b/awsproviderlint/vendor/modules.txt index 6f58fbb8f97..a9d77a9d5ef 100644 --- a/awsproviderlint/vendor/modules.txt +++ b/awsproviderlint/vendor/modules.txt @@ -14,7 +14,7 @@ github.com/agext/levenshtein github.com/apparentlymart/go-textseg/v12/textseg # github.com/apparentlymart/go-textseg/v13 v13.0.0 github.com/apparentlymart/go-textseg/v13/textseg -# github.com/aws/aws-sdk-go v1.39.3 +# github.com/aws/aws-sdk-go v1.39.4 ## explicit github.com/aws/aws-sdk-go/aws github.com/aws/aws-sdk-go/aws/arn From eb885d126da4e273c9abd688a3bfeb9dd128f797 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jul 2021 06:15:03 +0000 Subject: [PATCH 312/398] build(deps): bump github.com/aws/aws-sdk-go from 1.39.3 to 1.39.4 Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.39.3 to 1.39.4. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Changelog](https://github.com/aws/aws-sdk-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.39.3...v1.39.4) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4e23c51ff63..13234f81e5e 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.16 require ( github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect - github.com/aws/aws-sdk-go v1.39.3 + github.com/aws/aws-sdk-go v1.39.4 github.com/beevik/etree v1.1.0 github.com/fatih/color v1.9.0 // indirect github.com/hashicorp/aws-sdk-go-base v0.7.1 diff --git a/go.sum b/go.sum index c0acb2cd1b6..fc92a009e9d 100644 --- a/go.sum +++ b/go.sum @@ -66,8 +66,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= github.com/aws/aws-sdk-go v1.25.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.31.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/aws/aws-sdk-go v1.39.3 h1:JMDk7p+AV89MdVy/ZcFWAGivWIE3vXOsRriFjFWVcIY= -github.com/aws/aws-sdk-go v1.39.3/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= +github.com/aws/aws-sdk-go v1.39.4 h1:nXBChUaG5cinrl3yg4/rUyssOOLH/ohk4S9K03kJirE= +github.com/aws/aws-sdk-go v1.39.4/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= From a0d51c5230321458e9d90e3d50dfd049313008a6 Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Mon, 12 Jul 2021 02:42:29 -0400 Subject: [PATCH 313/398] CR updates; add CHANGELOG entry --- .changelog/19320.txt | 3 + aws/provider.go | 2 +- ...rce_aws_appconfig_configuration_profile.go | 357 +++++++++----- ...ws_appconfig_configuration_profile_test.go | 453 +++++++++++++++--- aws/resource_aws_appconfig_environment.go | 12 +- ...resource_aws_appconfig_environment_test.go | 2 +- website/docs/index.html.markdown | 1 + ...config_configuration_profile.html.markdown | 55 ++- 8 files changed, 648 insertions(+), 237 deletions(-) create mode 100644 .changelog/19320.txt diff --git a/.changelog/19320.txt b/.changelog/19320.txt new file mode 100644 index 00000000000..c4cdbdca9d4 --- /dev/null +++ b/.changelog/19320.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_appconfig_configuration_profile +``` diff --git a/aws/provider.go b/aws/provider.go index 48e60dbd52c..29aa1e41d05 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -507,8 +507,8 @@ func Provider() *schema.Provider { "aws_appautoscaling_policy": resourceAwsAppautoscalingPolicy(), "aws_appautoscaling_scheduled_action": resourceAwsAppautoscalingScheduledAction(), "aws_appconfig_application": resourceAwsAppconfigApplication(), - "aws_appconfig_environment": resourceAwsAppconfigEnvironment(), "aws_appconfig_configuration_profile": resourceAwsAppconfigConfigurationProfile(), + "aws_appconfig_environment": resourceAwsAppconfigEnvironment(), "aws_appmesh_gateway_route": resourceAwsAppmeshGatewayRoute(), "aws_appmesh_mesh": resourceAwsAppmeshMesh(), "aws_appmesh_route": resourceAwsAppmeshRoute(), diff --git a/aws/resource_aws_appconfig_configuration_profile.go b/aws/resource_aws_appconfig_configuration_profile.go index 321aba0c793..6a17f212012 100644 --- a/aws/resource_aws_appconfig_configuration_profile.go +++ b/aws/resource_aws_appconfig_configuration_profile.go @@ -3,11 +3,13 @@ package aws import ( "fmt" "log" + "regexp" "strings" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/appconfig" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "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" @@ -20,178 +22,187 @@ func resourceAwsAppconfigConfigurationProfile() *schema.Resource { Update: resourceAwsAppconfigConfigurationProfileUpdate, Delete: resourceAwsAppconfigConfigurationProfileDelete, Importer: &schema.ResourceImporter{ - State: resourceAwsAppconfigConfigurationProfileImport, + State: schema.ImportStatePassthrough, }, Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.All( - validation.StringLenBetween(1, 64), - ), - }, "application_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringMatch(regexp.MustCompile(`[a-z0-9]{4,7}`), ""), + }, + "arn": { Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.All( - validation.StringLenBetween(4, 7), - ), + Computed: true, }, - "location_uri": { + "configuration_profile_id": { Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.All( - validation.StringLenBetween(0, 2048), - ), + Computed: true, }, "description": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.All( - validation.StringLenBetween(0, 1024), - ), + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(0, 1024), + }, + "location_uri": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 2048), + }, + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 64), }, "retrieval_role_arn": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.All( - validation.StringLenBetween(20, 2048), - ), + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateArn, }, - "validators": { - Type: schema.TypeList, + "tags": tagsSchema(), + "tags_all": tagsSchemaComputed(), + "validator": { + Type: schema.TypeSet, Optional: true, MaxItems: 2, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "content": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.All( - validation.StringLenBetween(0, 32768), + Type: schema.TypeString, + Optional: true, + Sensitive: true, + ValidateFunc: validation.Any( + validation.StringIsJSON, + validateArn, ), + DiffSuppressFunc: suppressEquivalentJsonDiffs, }, "type": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice([]string{ - "JSON_SCHEMA", "LAMBDA", - }, false), + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(appconfig.ValidatorType_Values(), false), }, }, }, }, - "tags": tagsSchema(), - "arn": { - Type: schema.TypeString, - Computed: true, - }, }, + CustomizeDiff: SetTagsDiff, } } func resourceAwsAppconfigConfigurationProfileCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).appconfigconn + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig + tags := defaultTagsConfig.MergeTags(keyvaluetags.New(d.Get("tags").(map[string]interface{}))) + + appId := d.Get("application_id").(string) + name := d.Get("name").(string) input := &appconfig.CreateConfigurationProfileInput{ - Name: aws.String(d.Get("name").(string)), - Description: aws.String(d.Get("description").(string)), + ApplicationId: aws.String(appId), LocationUri: aws.String(d.Get("location_uri").(string)), - ApplicationId: aws.String(d.Get("application_id").(string)), - Validators: expandAppconfigValidators(d.Get("validators").([]interface{})), - Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().AppconfigTags(), + Name: aws.String(name), + Tags: tags.IgnoreAws().AppconfigTags(), } - if retrievalRoleARN := d.Get("retrieval_role_arn").(string); retrievalRoleARN != "" { - input.RetrievalRoleArn = aws.String(retrievalRoleARN) + if v, ok := d.GetOk("description"); ok { + input.Description = aws.String(v.(string)) } - profile, err := conn.CreateConfigurationProfile(input) - if err != nil { - return fmt.Errorf("Error creating AppConfig ConfigurationProfile: %s", err) + if v, ok := d.GetOk("retrieval_role_arn"); ok { + input.RetrievalRoleArn = aws.String(v.(string)) } - d.SetId(aws.StringValue(profile.Id)) + if v, ok := d.GetOk("validator"); ok && v.(*schema.Set).Len() > 0 { + input.Validators = expandAppconfigValidators(v.(*schema.Set).List()) + } - return resourceAwsAppconfigConfigurationProfileRead(d, meta) -} + profile, err := conn.CreateConfigurationProfile(input) -func expandAppconfigValidators(list []interface{}) []*appconfig.Validator { - validators := make([]*appconfig.Validator, len(list)) - for i, validatorInterface := range list { - m := validatorInterface.(map[string]interface{}) - validators[i] = &appconfig.Validator{ - Content: aws.String(m["content"].(string)), - Type: aws.String(m["type"].(string)), - } + if err != nil { + return fmt.Errorf("error creating AppConfig Configuration Profile (%s) for Application (%s): %w", name, appId, err) } - return validators -} -func flattenAwsAppconfigValidators(validators []*appconfig.Validator) []interface{} { - list := make([]interface{}, len(validators)) - for i, validator := range validators { - list[i] = map[string]interface{}{ - "content": aws.StringValue(validator.Content), - "type": aws.StringValue(validator.Type), - } + if profile == nil { + return fmt.Errorf("error creating AppConfig Configuration Profile (%s) for Application (%s): empty response", name, appId) } - return list + + d.SetId(fmt.Sprintf("%s:%s", aws.StringValue(profile.Id), aws.StringValue(profile.ApplicationId))) + + return resourceAwsAppconfigConfigurationProfileRead(d, meta) } func resourceAwsAppconfigConfigurationProfileRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).appconfigconn + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig - appID := d.Get("application_id").(string) + confProfID, appID, err := resourceAwsAppconfigConfigurationProfileParseID(d.Id()) + + if err != nil { + return err + } input := &appconfig.GetConfigurationProfileInput{ ApplicationId: aws.String(appID), - ConfigurationProfileId: aws.String(d.Id()), + ConfigurationProfileId: aws.String(confProfID), } output, err := conn.GetConfigurationProfile(input) - if !d.IsNewResource() && isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { - log.Printf("[WARN] Appconfig ConfigurationProfile (%s) not found, removing from state", d.Id()) + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, appconfig.ErrCodeResourceNotFoundException) { + log.Printf("[WARN] AppConfig Configuration Profile (%s) for Application (%s) not found, removing from state", confProfID, appID) d.SetId("") return nil } if err != nil { - return fmt.Errorf("error getting AppConfig ConfigurationProfile (%s): %s", d.Id(), err) + return fmt.Errorf("error getting AppConfig Configuration Profile (%s) for Application (%s): %w", confProfID, appID, err) } if output == nil { - return fmt.Errorf("error getting AppConfig ConfigurationProfile (%s): empty response", d.Id()) + return fmt.Errorf("error getting AppConfig Configuration Profile (%s) for Application (%s): empty response", confProfID, appID) } - d.Set("name", output.Name) - d.Set("description", output.Description) d.Set("application_id", output.ApplicationId) + d.Set("configuration_profile_id", output.Id) + d.Set("description", output.Description) d.Set("location_uri", output.LocationUri) + d.Set("name", output.Name) + d.Set("retrieval_role_arn", output.RetrievalRoleArn) - d.Set("validators", flattenAwsAppconfigValidators(output.Validators)) - profileARN := arn.ARN{ + if err := d.Set("validator", flattenAwsAppconfigValidators(output.Validators)); err != nil { + return fmt.Errorf("error setting validator: %w", err) + } + + arn := arn.ARN{ AccountID: meta.(*AWSClient).accountid, Partition: meta.(*AWSClient).partition, Region: meta.(*AWSClient).region, - Resource: fmt.Sprintf("application/%s/configurationprofile/%s", appID, d.Id()), + Resource: fmt.Sprintf("application/%s/configurationprofile/%s", appID, confProfID), Service: "appconfig", }.String() - d.Set("arn", profileARN) - tags, err := keyvaluetags.AppconfigListTags(conn, profileARN) + d.Set("arn", arn) + + tags, err := keyvaluetags.AppconfigListTags(conn, arn) + if err != nil { - return fmt.Errorf("error getting tags for AppConfig ConfigurationProfile (%s): %s", d.Id(), err) + return fmt.Errorf("error listing tags for AppConfig Configuration Profile (%s): %w", d.Id(), err) } - if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { - return fmt.Errorf("error setting tags: %s", err) + tags = tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig) + + //lintignore:AWSR002 + if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting tags: %w", err) + } + + if err := d.Set("tags_all", tags.Map()); err != nil { + return fmt.Errorf("error setting tags_all: %w", err) } return nil @@ -200,37 +211,46 @@ func resourceAwsAppconfigConfigurationProfileRead(d *schema.ResourceData, meta i func resourceAwsAppconfigConfigurationProfileUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).appconfigconn - updateInput := &appconfig.UpdateConfigurationProfileInput{ - ConfigurationProfileId: aws.String(d.Id()), - ApplicationId: aws.String(d.Get("application_id").(string)), - } + if d.HasChangesExcept("tags", "tags_all") { + confProfID, appID, err := resourceAwsAppconfigConfigurationProfileParseID(d.Id()) - if d.HasChange("description") { - updateInput.Description = aws.String(d.Get("description").(string)) - } + if err != nil { + return err + } - if d.HasChange("name") { - updateInput.Name = aws.String(d.Get("name").(string)) - } + updateInput := &appconfig.UpdateConfigurationProfileInput{ + ApplicationId: aws.String(appID), + ConfigurationProfileId: aws.String(confProfID), + } - if d.HasChange("retrieval_role_arn") { - updateInput.RetrievalRoleArn = aws.String(d.Get("retrieval_role_arn").(string)) - } + if d.HasChange("description") { + updateInput.Description = aws.String(d.Get("description").(string)) + } - if d.HasChange("tags") { - o, n := d.GetChange("tags") - if err := keyvaluetags.AppconfigUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { - return fmt.Errorf("error updating AppConfig (%s) tags: %s", d.Id(), err) + if d.HasChange("name") { + updateInput.Name = aws.String(d.Get("name").(string)) } - } - if d.HasChange("validators") { - updateInput.Validators = expandAppconfigValidators(d.Get("validators").([]interface{})) + if d.HasChange("retrieval_role_arn") { + updateInput.RetrievalRoleArn = aws.String(d.Get("retrieval_role_arn").(string)) + } + + if d.HasChange("validator") { + updateInput.Validators = expandAppconfigValidators(d.Get("validator").(*schema.Set).List()) + } + + _, err = conn.UpdateConfigurationProfile(updateInput) + + if err != nil { + return fmt.Errorf("error updating AppConfig Configuration Profile (%s) for Application (%s): %w", confProfID, appID, err) + } } - _, err := conn.UpdateConfigurationProfile(updateInput) - if err != nil { - return fmt.Errorf("error updating AppConfig ConfigurationProfile (%s): %s", d.Id(), err) + if d.HasChange("tags_all") { + o, n := d.GetChange("tags_all") + if err := keyvaluetags.AppconfigUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return fmt.Errorf("error updating AppConfig Configuration Profile (%s) tags: %w", d.Get("arn").(string), err) + } } return resourceAwsAppconfigConfigurationProfileRead(d, meta) @@ -239,32 +259,115 @@ func resourceAwsAppconfigConfigurationProfileUpdate(d *schema.ResourceData, meta func resourceAwsAppconfigConfigurationProfileDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).appconfigconn + confProfID, appID, err := resourceAwsAppconfigConfigurationProfileParseID(d.Id()) + + if err != nil { + return err + } + input := &appconfig.DeleteConfigurationProfileInput{ - ConfigurationProfileId: aws.String(d.Id()), - ApplicationId: aws.String(d.Get("application_id").(string)), + ApplicationId: aws.String(appID), + ConfigurationProfileId: aws.String(confProfID), } - _, err := conn.DeleteConfigurationProfile(input) + _, err = conn.DeleteConfigurationProfile(input) - if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + if tfawserr.ErrCodeEquals(err, appconfig.ErrCodeResourceNotFoundException) { return nil } if err != nil { - return fmt.Errorf("error deleting Appconfig ConfigurationProfile (%s): %s", d.Id(), err) + return fmt.Errorf("error deleting AppConfig Configuration Profile (%s) for Application (%s): %w", confProfID, appID, err) } return nil } -func resourceAwsAppconfigConfigurationProfileImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - parts := strings.Split(d.Id(), "/") - if len(parts) != 2 { - return []*schema.ResourceData{}, fmt.Errorf("Wrong format of resource: %s. Please follow 'application-id/configurationprofile-id'", d.Id()) +func resourceAwsAppconfigConfigurationProfileParseID(id string) (string, string, error) { + parts := strings.Split(id, ":") + + if len(parts) != 2 || parts[0] == "" || parts[1] == "" { + return "", "", fmt.Errorf("unexpected format of ID (%q), expected configurationProfileID:applicationID", id) + } + + return parts[0], parts[1], nil +} + +func expandAppconfigValidator(tfMap map[string]interface{}) *appconfig.Validator { + if tfMap == nil { + return nil + } + + validator := &appconfig.Validator{} + + // AppConfig API supports empty content + if v, ok := tfMap["content"].(string); ok { + validator.Content = aws.String(v) + } + + if v, ok := tfMap["type"].(string); ok && v != "" { + validator.Type = aws.String(v) + } + + return validator +} + +func expandAppconfigValidators(tfList []interface{}) []*appconfig.Validator { + // AppConfig API requires a 0 length slice instead of a nil value + // when updating from N validators to 0/nil validators + validators := make([]*appconfig.Validator, 0) + + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + + if !ok { + continue + } + + validator := expandAppconfigValidator(tfMap) + + if validator == nil { + continue + } + + validators = append(validators, validator) } - d.SetId(parts[1]) - d.Set("application_id", parts[0]) + return validators +} + +func flattenAwsAppconfigValidator(validator *appconfig.Validator) map[string]interface{} { + if validator == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := validator.Content; v != nil { + tfMap["content"] = aws.StringValue(v) + } + + if v := validator.Type; v != nil { + tfMap["type"] = aws.StringValue(v) + } + + return tfMap +} + +func flattenAwsAppconfigValidators(validators []*appconfig.Validator) []interface{} { + if len(validators) == 0 { + return nil + } + + var tfList []interface{} + + for _, validator := range validators { + if validator == nil { + continue + } + + tfList = append(tfList, flattenAwsAppconfigValidator(validator)) + } - return []*schema.ResourceData{d}, nil + return tfList } diff --git a/aws/resource_aws_appconfig_configuration_profile_test.go b/aws/resource_aws_appconfig_configuration_profile_test.go index f594dad9bf6..23d98c250e9 100644 --- a/aws/resource_aws_appconfig_configuration_profile_test.go +++ b/aws/resource_aws_appconfig_configuration_profile_test.go @@ -2,20 +2,22 @@ package aws import ( "fmt" + "regexp" "testing" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/appconfig" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) func TestAccAWSAppConfigConfigurationProfile_basic(t *testing.T) { - var profile appconfig.GetConfigurationProfileOutput - profileName := acctest.RandomWithPrefix("tf-acc-test") - profileDesc := acctest.RandomWithPrefix("desc") + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_appconfig_configuration_profile.test" + appResourceName := "aws_appconfig_application.test" + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), @@ -23,18 +25,20 @@ func TestAccAWSAppConfigConfigurationProfile_basic(t *testing.T) { CheckDestroy: testAccCheckAppConfigConfigurationProfileDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAppConfigConfigurationProfile(profileName, profileDesc), + Config: testAccAWSAppConfigConfigurationProfileConfigName(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigConfigurationProfileExists(resourceName, &profile), - resource.TestCheckResourceAttr(resourceName, "name", profileName), - testAccCheckAWSAppConfigConfigurationProfileARN(resourceName, &profile), + testAccCheckAWSAppConfigConfigurationProfileExists(resourceName), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "appconfig", regexp.MustCompile(`application/[a-z0-9]{4,7}/configurationprofile/[a-z0-9]{4,7}`)), + resource.TestCheckResourceAttrPair(resourceName, "application_id", appResourceName, "id"), + resource.TestMatchResourceAttr(resourceName, "configuration_profile_id", regexp.MustCompile(`[a-z0-9]{4,7}`)), + resource.TestCheckResourceAttr(resourceName, "location_uri", "hosted"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "validator.#", "0"), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), - resource.TestCheckResourceAttr(resourceName, "description", profileDesc), ), }, { ResourceName: resourceName, - ImportStateIdFunc: testAccAWSAppConfigConfigurationProfileImportStateIdFunc(resourceName), ImportState: true, ImportStateVerify: true, }, @@ -43,10 +47,7 @@ func TestAccAWSAppConfigConfigurationProfile_basic(t *testing.T) { } func TestAccAWSAppConfigConfigurationProfile_disappears(t *testing.T) { - var profile appconfig.GetConfigurationProfileOutput - - profileName := acctest.RandomWithPrefix("tf-acc-test") - profileDesc := acctest.RandomWithPrefix("desc") + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_appconfig_configuration_profile.test" resource.ParallelTest(t, resource.TestCase{ @@ -56,10 +57,10 @@ func TestAccAWSAppConfigConfigurationProfile_disappears(t *testing.T) { CheckDestroy: testAccCheckAppConfigConfigurationProfileDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAppConfigConfigurationProfile(profileName, profileDesc), + Config: testAccAWSAppConfigConfigurationProfileConfigName(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigConfigurationProfileExists(resourceName, &profile), - testAccCheckAWSAppConfigConfigurationProfileDisappears(&profile), + testAccCheckAWSAppConfigConfigurationProfileExists(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsAppconfigConfigurationProfile(), resourceName), ), ExpectNonEmptyPlan: true, }, @@ -67,9 +68,205 @@ func TestAccAWSAppConfigConfigurationProfile_disappears(t *testing.T) { }) } -func TestAccAWSAppConfigConfigurationProfile_Tags(t *testing.T) { - var profile appconfig.GetConfigurationProfileOutput +func TestAccAWSAppConfigConfigurationProfile_Validators_JSON(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_appconfig_configuration_profile.test" + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigConfigurationProfileDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigConfigurationProfileConfigValidator_JSON(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigConfigurationProfileExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "validator.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "validator.*", map[string]string{ + "type": appconfig.ValidatorTypeJsonSchema, + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSAppConfigConfigurationProfileConfigValidator_NoJSONContent(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigConfigurationProfileExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "validator.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "validator.*", map[string]string{ + "content": "", + "type": appconfig.ValidatorTypeJsonSchema, + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + // Test Validator Removal + Config: testAccAWSAppConfigConfigurationProfileConfigName(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigConfigurationProfileExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "validator.#", "0"), + ), + }, + }, + }) +} + +func TestAccAWSAppConfigConfigurationProfile_Validators_Lambda(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_appconfig_configuration_profile.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigConfigurationProfileDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigConfigurationProfileConfigValidator_Lambda(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigConfigurationProfileExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "validator.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "validator.*.content", "aws_lambda_function.test", "arn"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "validator.*", map[string]string{ + "type": appconfig.ValidatorTypeLambda, + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + // Test Validator Removal + Config: testAccAWSAppConfigConfigurationProfileConfigName(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigConfigurationProfileExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "validator.#", "0"), + ), + }, + }, + }) +} + +func TestAccAWSAppConfigConfigurationProfile_Validators_Multiple(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_appconfig_configuration_profile.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigConfigurationProfileDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigConfigurationProfileConfigValidator_Multiple(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigConfigurationProfileExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "validator.#", "2"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "validator.*", map[string]string{ + "content": "{\"$schema\":\"http://json-schema.org/draft-05/schema#\",\"description\":\"BasicFeatureToggle-1\",\"title\":\"$id$\"}", + "type": appconfig.ValidatorTypeJsonSchema, + }), + resource.TestCheckTypeSetElemAttrPair(resourceName, "validator.*.content", "aws_lambda_function.test", "arn"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "validator.*", map[string]string{ + "type": appconfig.ValidatorTypeLambda, + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSAppConfigConfigurationProfile_updateName(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + rNameUpdated := acctest.RandomWithPrefix("tf-acc-test-update") + resourceName := "aws_appconfig_configuration_profile.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigConfigurationProfileDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigConfigurationProfileConfigName(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigConfigurationProfileExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", rName), + ), + }, + { + Config: testAccAWSAppConfigConfigurationProfileConfigName(rNameUpdated), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigConfigurationProfileExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", rNameUpdated), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSAppConfigConfigurationProfile_updateDescription(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + description := acctest.RandomWithPrefix("tf-acc-test-update") + resourceName := "aws_appconfig_configuration_profile.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigConfigurationProfileDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigConfigurationProfileConfigDescription(rName, rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigConfigurationProfileExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "description", rName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSAppConfigConfigurationProfileConfigDescription(rName, description), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigConfigurationProfileExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "description", description), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSAppConfigConfigurationProfile_Tags(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_appconfig_configuration_profile.test" @@ -82,21 +279,20 @@ func TestAccAWSAppConfigConfigurationProfile_Tags(t *testing.T) { { Config: testAccAWSAppConfigConfigurationProfileTags1(rName, "key1", "value1"), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigConfigurationProfileExists(resourceName, &profile), + testAccCheckAWSAppConfigConfigurationProfileExists(resourceName), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), ), }, { ResourceName: resourceName, - ImportStateIdFunc: testAccAWSAppConfigConfigurationProfileImportStateIdFunc(resourceName), ImportState: true, ImportStateVerify: true, }, { Config: testAccAWSAppConfigConfigurationProfileTags2(rName, "key1", "value1updated", "key2", "value2"), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigConfigurationProfileExists(resourceName, &profile), + testAccCheckAWSAppConfigConfigurationProfileExists(resourceName), resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), @@ -105,7 +301,7 @@ func TestAccAWSAppConfigConfigurationProfile_Tags(t *testing.T) { { Config: testAccAWSAppConfigConfigurationProfileTags1(rName, "key2", "value2"), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigConfigurationProfileExists(resourceName, &profile), + testAccCheckAWSAppConfigConfigurationProfileExists(resourceName), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), ), @@ -122,43 +318,36 @@ func testAccCheckAppConfigConfigurationProfileDestroy(s *terraform.State) error continue } + confProfID, appID, err := resourceAwsAppconfigConfigurationProfileParseID(rs.Primary.ID) + + if err != nil { + return err + } + input := &appconfig.GetConfigurationProfileInput{ - ApplicationId: aws.String(rs.Primary.Attributes["application_id"]), - ConfigurationProfileId: aws.String(rs.Primary.ID), + ApplicationId: aws.String(appID), + ConfigurationProfileId: aws.String(confProfID), } output, err := conn.GetConfigurationProfile(input) - if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + if tfawserr.ErrCodeEquals(err, appconfig.ErrCodeResourceNotFoundException) { continue } if err != nil { - return err + return fmt.Errorf("error reading AppConfig Configuration Profile (%s) for Application (%s): %w", confProfID, appID, err) } if output != nil { - return fmt.Errorf("AppConfig ConfigurationProfile (%s) still exists", rs.Primary.ID) + return fmt.Errorf("AppConfig Configuration Profile (%s) for Application (%s) still exists", confProfID, appID) } } return nil } -func testAccCheckAWSAppConfigConfigurationProfileDisappears(profile *appconfig.GetConfigurationProfileOutput) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).appconfigconn - - _, err := conn.DeleteConfigurationProfile(&appconfig.DeleteConfigurationProfileInput{ - ApplicationId: profile.ApplicationId, - ConfigurationProfileId: profile.Id, - }) - - return err - } -} - -func testAccCheckAWSAppConfigConfigurationProfileExists(resourceName string, profile *appconfig.GetConfigurationProfileOutput) resource.TestCheckFunc { +func testAccCheckAWSAppConfigConfigurationProfileExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] if !ok { @@ -169,39 +358,66 @@ func testAccCheckAWSAppConfigConfigurationProfileExists(resourceName string, pro return fmt.Errorf("Resource (%s) ID not set", resourceName) } + confProfID, appID, err := resourceAwsAppconfigConfigurationProfileParseID(rs.Primary.ID) + + if err != nil { + return err + } + conn := testAccProvider.Meta().(*AWSClient).appconfigconn output, err := conn.GetConfigurationProfile(&appconfig.GetConfigurationProfileInput{ - ApplicationId: aws.String(rs.Primary.Attributes["application_id"]), - ConfigurationProfileId: aws.String(rs.Primary.ID), + ApplicationId: aws.String(appID), + ConfigurationProfileId: aws.String(confProfID), }) + if err != nil { - return err + return fmt.Errorf("error reading AppConfig Configuration Profile (%s) for Application (%s): %w", confProfID, appID, err) } - *profile = *output + if output == nil { + return fmt.Errorf("AppConfig Configuration Profile (%s) for Application (%s) not found", confProfID, appID) + } return nil } } -func testAccCheckAWSAppConfigConfigurationProfileARN(resourceName string, profile *appconfig.GetConfigurationProfileOutput) resource.TestCheckFunc { - return func(s *terraform.State) error { - return testAccCheckResourceAttrRegionalARN(resourceName, "arn", "appconfig", fmt.Sprintf("application/%s/configurationprofile/%s", aws.StringValue(profile.ApplicationId), aws.StringValue(profile.Id)))(s) - } +func testAccAWSAppConfigConfigurationProfileConfigName(rName string) string { + return composeConfig( + testAccAWSAppConfigApplicationConfigName(rName), + fmt.Sprintf(` +resource "aws_appconfig_configuration_profile" "test" { + application_id = aws_appconfig_application.test.id + name = %q + location_uri = "hosted" +} +`, rName)) } -func testAccAWSAppConfigConfigurationProfile(profileName, profileDesc string) string { - appName := acctest.RandomWithPrefix("tf-acc-test") - appDesc := acctest.RandomWithPrefix("desc") - return testAccAWSAppConfigApplicationConfigDescription(appName, appDesc) + fmt.Sprintf(` +func testAccAWSAppConfigConfigurationProfileConfigDescription(rName, description string) string { + return composeConfig( + testAccAWSAppConfigApplicationConfigDescription(rName, description), + fmt.Sprintf(` resource "aws_appconfig_configuration_profile" "test" { + application_id = aws_appconfig_application.test.id name = %[1]q description = %[2]q + location_uri = "hosted" +} +`, rName, description)) +} + +func testAccAWSAppConfigConfigurationProfileConfigValidator_JSON(rName string) string { + return composeConfig( + testAccAWSAppConfigApplicationConfigName(rName), + fmt.Sprintf(` +resource "aws_appconfig_configuration_profile" "test" { application_id = aws_appconfig_application.test.id + name = %q location_uri = "hosted" - validators { - type = "JSON_SCHEMA" + + validator { content = jsonencode({ "$schema" = "http://json-schema.org/draft-04/schema#" title = "$id$" @@ -215,47 +431,138 @@ resource "aws_appconfig_configuration_profile" "test" { } minProperties = 1 }) + + type = "JSON_SCHEMA" } } -`, profileName, profileDesc) +`, rName)) } -func testAccAWSAppConfigConfigurationProfileTags1(rName, tagKey1, tagValue1 string) string { - return testAccAWSAppConfigApplicationTags1(rName, tagKey1, tagValue1) + fmt.Sprintf(` +func testAccAWSAppConfigConfigurationProfileConfigValidator_NoJSONContent(rName string) string { + return composeConfig( + testAccAWSAppConfigApplicationConfigName(rName), + fmt.Sprintf(` +resource "aws_appconfig_configuration_profile" "test" { + application_id = aws_appconfig_application.test.id + name = %q + location_uri = "hosted" + + validator { + type = "JSON_SCHEMA" + } +} +`, rName)) +} + +func testAccAWSAppConfigApplicationConfigLambdaBase(rName string) string { + return fmt.Sprintf(` +data "aws_partition" "current" {} + +resource "aws_iam_role" "lambda" { + name = "%[1]s-lambda" + + assume_role_policy = <` or the Amazon Resource Name (ARN). For a parameter, specify either the parameter name in the format `ssm-parameter://` or the ARN. For an Amazon S3 object, specify the URI in the following format: `s3:///`. +* `name` - (Required) The name for the configuration profile. Must be between 1 and 64 characters in length. +* `description` - (Optional) The description of the configuration profile. Can be at most 1024 characters. +* `retrieval_role_arn` - (Optional) The ARN of an IAM role with permission to access the configuration at the specified `location_uri`. A retrieval role ARN is not required for configurations stored in the AWS AppConfig `hosted` configuration store. It is required for all other sources that store your configuration. +* `tags` - (Optional) A map of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](/docs/providers/aws/index.html#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. +* `validator` - (Optional) A set of methods for validating the configuration. Maximum of 2. See [Validator](#validator) below for more details. + +### Validator -### validator +The `validator` block supports the following: -- `content` - (Optional) Either the JSON Schema content or the Amazon Resource Name (ARN) of an AWS Lambda function. -- `type` - (Optional) AWS AppConfig supports validators of type `JSON_SCHEMA` and `LAMBDA` +* `content` - (Optional, Required when `type` is `LAMBDA`) Either the JSON Schema content or the Amazon Resource Name (ARN) of an AWS Lambda function. +* `type` - (Optional) The type of validator. Valid values: `JSON_SCHEMA` and `LAMBDA`. ## Attributes Reference In addition to all arguments above, the following attributes are exported: -- `arn` - The Amazon Resource Name (ARN) of the AppConfig Configuration Profile. -- `id` - The AppConfig Configuration Profile ID +* `arn` - The Amazon Resource Name (ARN) of the AppConfig Configuration Profile. +* `configuration_profile_id` - The configuration profile ID. +* `id` - The AppConfig configuration profile ID and application ID separated by a colon (`:`). +* `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](/docs/providers/aws/index.html#default_tags-configuration-block). ## Import -`aws_appconfig_configuration_profile` can be imported by the Application ID and Configuration Profile ID, e.g. +AppConfig Configuration Profiles can be imported by using the configuration profile ID and application ID separated by a colon (`:`), e.g. ``` -$ terraform import aws_appconfig_configuration_profile.test 71abcde/11xxxxx +$ terraform import aws_appconfig_configuration_profile.example 71abcde:11xxxxx ``` From 9ed0d31da9dee9652bbbd82c6dacaf81303b7c2d Mon Sep 17 00:00:00 2001 From: Andy Richardson Date: Mon, 12 Jul 2021 11:37:08 +0100 Subject: [PATCH 314/398] Amend misleading module name --- website/docs/r/lambda_function.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/lambda_function.html.markdown b/website/docs/r/lambda_function.html.markdown index 3a41dc3f529..f9266d2371b 100644 --- a/website/docs/r/lambda_function.html.markdown +++ b/website/docs/r/lambda_function.html.markdown @@ -47,7 +47,7 @@ resource "aws_lambda_function" "test_lambda" { filename = "lambda_function_payload.zip" function_name = "lambda_function_name" role = aws_iam_role.iam_for_lambda.arn - handler = "exports.test" + handler = "index.test" # The filebase64sha256() function is available in Terraform 0.11.12 and later # For Terraform 0.11.11 and earlier, use the base64sha256() function and the file() function: From f97a52e4e2a55da63e8cd101d63f095266243c23 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 12 Jul 2021 09:28:04 -0400 Subject: [PATCH 315/398] Fix 'terrafmt' errors. --- ...e_aws_dx_gateway_association_proposal_test.go | 16 ++++++++-------- aws/resource_aws_dx_gateway_association_test.go | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/aws/resource_aws_dx_gateway_association_proposal_test.go b/aws/resource_aws_dx_gateway_association_proposal_test.go index 5cc620a0520..02c976285ed 100644 --- a/aws/resource_aws_dx_gateway_association_proposal_test.go +++ b/aws/resource_aws_dx_gateway_association_proposal_test.go @@ -416,11 +416,11 @@ func testAccDxGatewayAssociationProposalConfig_endOfLifeVpn(rName string, rBgpAs data "aws_caller_identity" "current" {} resource "aws_dx_gateway_association" "test" { -provider = "awsalternate" + provider = "awsalternate" -proposal_id = aws_dx_gateway_association_proposal.test.id -dx_gateway_id = aws_dx_gateway.test.id -associated_gateway_owner_account_id = data.aws_caller_identity.current.account_id + proposal_id = aws_dx_gateway_association_proposal.test.id + dx_gateway_id = aws_dx_gateway.test.id + associated_gateway_owner_account_id = data.aws_caller_identity.current.account_id } `) } @@ -430,11 +430,11 @@ func testAccDxGatewayAssociationProposalConfig_endOfLifeTgw(rName string, rBgpAs data "aws_caller_identity" "current" {} resource "aws_dx_gateway_association" "test" { -provider = "awsalternate" + provider = "awsalternate" -proposal_id = aws_dx_gateway_association_proposal.test.id -dx_gateway_id = aws_dx_gateway.test.id -associated_gateway_owner_account_id = data.aws_caller_identity.current.account_id + proposal_id = aws_dx_gateway_association_proposal.test.id + dx_gateway_id = aws_dx_gateway.test.id + associated_gateway_owner_account_id = data.aws_caller_identity.current.account_id } `) } diff --git a/aws/resource_aws_dx_gateway_association_test.go b/aws/resource_aws_dx_gateway_association_test.go index 9a72d222fea..8d324e0c482 100644 --- a/aws/resource_aws_dx_gateway_association_test.go +++ b/aws/resource_aws_dx_gateway_association_test.go @@ -711,10 +711,10 @@ resource "aws_dx_gateway_association_proposal" "test" { } resource "aws_dx_gateway_association_proposal" "test2" { - dx_gateway_id = aws_dx_gateway.test.id - dx_gateway_owner_account_id = aws_dx_gateway.test.owner_account_id - associated_gateway_id = aws_vpn_gateway_attachment.test.vpn_gateway_id - } + dx_gateway_id = aws_dx_gateway.test.id + dx_gateway_owner_account_id = aws_dx_gateway.test.owner_account_id + associated_gateway_id = aws_vpn_gateway_attachment.test.vpn_gateway_id +} # Accepter resource "aws_dx_gateway_association" "test" { From dceee240f1466a3faff1adce7c64a7e0ea411da8 Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Mon, 12 Jul 2021 09:52:01 -0400 Subject: [PATCH 316/398] CR updates; add CHANGELOG entry --- .changelog/19324.txt | 3 + aws/provider.go | 1 - ...rce_aws_appconfig_configuration_profile.go | 2 +- aws/resource_aws_appconfig_environment.go | 2 +- ..._appconfig_hosted_configuration_version.go | 142 ++++++++++-------- ...onfig_hosted_configuration_version_test.go | 113 ++++++-------- website/docs/index.html.markdown | 1 + ...hosted_configuration_version.html.markdown | 44 +++--- 8 files changed, 153 insertions(+), 155 deletions(-) create mode 100644 .changelog/19324.txt diff --git a/.changelog/19324.txt b/.changelog/19324.txt new file mode 100644 index 00000000000..14d91b6069a --- /dev/null +++ b/.changelog/19324.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_appconfig_hosted_configuration_version +``` diff --git a/aws/provider.go b/aws/provider.go index c060a254917..f8570bba6aa 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -509,7 +509,6 @@ func Provider() *schema.Provider { "aws_appconfig_application": resourceAwsAppconfigApplication(), "aws_appconfig_configuration_profile": resourceAwsAppconfigConfigurationProfile(), "aws_appconfig_environment": resourceAwsAppconfigEnvironment(), - "aws_appconfig_configuration_profile": resourceAwsAppconfigConfigurationProfile(), "aws_appconfig_hosted_configuration_version": resourceAwsAppconfigHostedConfigurationVersion(), "aws_appmesh_gateway_route": resourceAwsAppmeshGatewayRoute(), "aws_appmesh_mesh": resourceAwsAppmeshMesh(), diff --git a/aws/resource_aws_appconfig_configuration_profile.go b/aws/resource_aws_appconfig_configuration_profile.go index 6a17f212012..585b468fe78 100644 --- a/aws/resource_aws_appconfig_configuration_profile.go +++ b/aws/resource_aws_appconfig_configuration_profile.go @@ -287,7 +287,7 @@ func resourceAwsAppconfigConfigurationProfileParseID(id string) (string, string, parts := strings.Split(id, ":") if len(parts) != 2 || parts[0] == "" || parts[1] == "" { - return "", "", fmt.Errorf("unexpected format of ID (%q), expected configurationProfileID:applicationID", id) + return "", "", fmt.Errorf("unexpected format of ID (%q), expected ConfigurationProfileID:ApplicationID", id) } return parts[0], parts[1], nil diff --git a/aws/resource_aws_appconfig_environment.go b/aws/resource_aws_appconfig_environment.go index 413b95f8f94..14f4d67513e 100644 --- a/aws/resource_aws_appconfig_environment.go +++ b/aws/resource_aws_appconfig_environment.go @@ -261,7 +261,7 @@ func resourceAwsAppconfigEnvironmentParseID(id string) (string, string, error) { parts := strings.Split(id, ":") if len(parts) != 2 || parts[0] == "" || parts[1] == "" { - return "", "", fmt.Errorf("unexpected format of ID (%q), expected environmentID:applicationID", id) + return "", "", fmt.Errorf("unexpected format of ID (%q), expected EnvironmentID:ApplicationID", id) } return parts[0], parts[1], nil diff --git a/aws/resource_aws_appconfig_hosted_configuration_version.go b/aws/resource_aws_appconfig_hosted_configuration_version.go index f7471fe0331..f9c0507c01e 100644 --- a/aws/resource_aws_appconfig_hosted_configuration_version.go +++ b/aws/resource_aws_appconfig_hosted_configuration_version.go @@ -3,11 +3,14 @@ package aws import ( "fmt" "log" + "regexp" "strconv" "strings" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/appconfig" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) @@ -16,46 +19,45 @@ func resourceAwsAppconfigHostedConfigurationVersion() *schema.Resource { return &schema.Resource{ Create: resourceAwsAppconfigHostedConfigurationVersionCreate, Read: resourceAwsAppconfigHostedConfigurationVersionRead, - Update: resourceAwsAppconfigHostedConfigurationVersionUpdate, Delete: resourceAwsAppconfigHostedConfigurationVersionDelete, Importer: &schema.ResourceImporter{ - State: resourceAwsAppconfigHostedConfigurationVersionImport, + State: schema.ImportStatePassthrough, }, Schema: map[string]*schema.Schema{ "application_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringMatch(regexp.MustCompile(`[a-z0-9]{4,7}`), ""), + }, + "arn": { Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.All( - validation.StringLenBetween(4, 7), - ), + Computed: true, }, "configuration_profile_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.All( - validation.StringLenBetween(4, 7), - ), + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringMatch(regexp.MustCompile(`[a-z0-9]{4,7}`), ""), }, "content": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + Sensitive: true, }, "content_type": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.All( - validation.StringLenBetween(1, 255), - ), + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 255), }, "description": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.All( - validation.StringLenBetween(0, 1024), - ), + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(0, 1024), }, "version_number": { Type: schema.TypeInt, @@ -76,16 +78,19 @@ func resourceAwsAppconfigHostedConfigurationVersionCreate(d *schema.ResourceData ConfigurationProfileId: aws.String(profileID), Content: []byte(d.Get("content").(string)), ContentType: aws.String(d.Get("content_type").(string)), - Description: aws.String(d.Get("description").(string)), } - hcv, err := conn.CreateHostedConfigurationVersion(input) + if v, ok := d.GetOk("description"); ok { + input.Description = aws.String(v.(string)) + } + + output, err := conn.CreateHostedConfigurationVersion(input) + if err != nil { - return fmt.Errorf("Error creating AppConfig HostedConfigurationVersion: %s", err) + return fmt.Errorf("error creating AppConfig HostedConfigurationVersion for Application (%s): %w", appID, err) } - d.SetId(fmt.Sprintf("%s/%s/%d", appID, profileID, aws.Int64Value(hcv.VersionNumber))) - d.Set("version_number", hcv.VersionNumber) + d.SetId(fmt.Sprintf("%s/%s/%d", aws.StringValue(output.ApplicationId), aws.StringValue(output.ConfigurationProfileId), aws.Int64Value(output.VersionNumber))) return resourceAwsAppconfigHostedConfigurationVersionRead(d, meta) } @@ -93,76 +98,93 @@ func resourceAwsAppconfigHostedConfigurationVersionCreate(d *schema.ResourceData func resourceAwsAppconfigHostedConfigurationVersionRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).appconfigconn + appID, confProfID, versionNumber, err := resourceAwsAppconfigHostedConfigurationVersionParseID(d.Id()) + + if err != nil { + return err + } + input := &appconfig.GetHostedConfigurationVersionInput{ - ApplicationId: aws.String(d.Get("application_id").(string)), - ConfigurationProfileId: aws.String(d.Get("configuration_profile_id").(string)), - VersionNumber: aws.Int64(int64(d.Get("version_number").(int))), + ApplicationId: aws.String(appID), + ConfigurationProfileId: aws.String(confProfID), + VersionNumber: aws.Int64(int64(versionNumber)), } output, err := conn.GetHostedConfigurationVersion(input) - if !d.IsNewResource() && isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { - log.Printf("[WARN] Appconfig HostedConfigurationVersion (%s) not found, removing from state", d.Id()) + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, appconfig.ErrCodeResourceNotFoundException) { + log.Printf("[WARN] Appconfig Hosted Configuration Version (%s) not found, removing from state", d.Id()) d.SetId("") return nil } if err != nil { - return fmt.Errorf("error getting AppConfig HostedConfigurationVersion (%s): %s", d.Id(), err) + return fmt.Errorf("error getting AppConfig Hosted Configuration Version (%s): %w", d.Id(), err) } if output == nil { - return fmt.Errorf("error getting AppConfig HostedConfigurationVersion (%s): empty response", d.Id()) + return fmt.Errorf("error getting AppConfig Hosted Configuration Version (%s): empty response", d.Id()) } - d.Set("description", output.Description) + d.Set("application_id", output.ApplicationId) + d.Set("configuration_profile_id", output.ConfigurationProfileId) d.Set("content", string(output.Content)) d.Set("content_type", output.ContentType) + d.Set("description", output.Description) + d.Set("version_number", output.VersionNumber) - return nil -} + arn := arn.ARN{ + AccountID: meta.(*AWSClient).accountid, + Partition: meta.(*AWSClient).partition, + Region: meta.(*AWSClient).region, + Resource: fmt.Sprintf("application/%s/configurationprofile/%s/hostedconfigurationversion/%d", appID, confProfID, versionNumber), + Service: "appconfig", + }.String() -func resourceAwsAppconfigHostedConfigurationVersionUpdate(d *schema.ResourceData, meta interface{}) error { - return resourceAwsAppconfigHostedConfigurationVersionCreate(d, meta) + d.Set("arn", arn) + + return nil } func resourceAwsAppconfigHostedConfigurationVersionDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).appconfigconn + appID, confProfID, versionNumber, err := resourceAwsAppconfigHostedConfigurationVersionParseID(d.Id()) + + if err != nil { + return err + } + input := &appconfig.DeleteHostedConfigurationVersionInput{ - ConfigurationProfileId: aws.String(d.Get("configuration_profile_id").(string)), - ApplicationId: aws.String(d.Get("application_id").(string)), - VersionNumber: aws.Int64(d.Get("version_number").(int64)), + ApplicationId: aws.String(appID), + ConfigurationProfileId: aws.String(confProfID), + VersionNumber: aws.Int64(int64(versionNumber)), } - _, err := conn.DeleteHostedConfigurationVersion(input) + _, err = conn.DeleteHostedConfigurationVersion(input) - if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + if tfawserr.ErrCodeEquals(err, appconfig.ErrCodeResourceNotFoundException) { return nil } if err != nil { - return fmt.Errorf("error deleting Appconfig HostedConfigurationVersion (%s): %s", d.Id(), err) + return fmt.Errorf("error deleting Appconfig Hosted Configuration Version (%s): %w", d.Id(), err) } return nil } -func resourceAwsAppconfigHostedConfigurationVersionImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - parts := strings.Split(d.Id(), "/") - if len(parts) != 3 { - return nil, fmt.Errorf("Wrong format of resource: %s. Please follow 'application-id/configurationprofile-id/version-number'", d.Id()) +func resourceAwsAppconfigHostedConfigurationVersionParseID(id string) (string, string, int, error) { + parts := strings.Split(id, "/") + + if len(parts) != 3 || parts[0] == "" || parts[1] == "" || parts[2] == "" { + return "", "", 0, fmt.Errorf("unexpected format of ID (%q), expected ApplicationID/ConfigurationProfileID/VersionNumber", id) } - verString := parts[2] - verNumber, err := strconv.Atoi(verString) + version, err := strconv.Atoi(parts[2]) if err != nil { - return nil, fmt.Errorf("version-number must be integer: %s: %w", verString, err) + return "", "", 0, fmt.Errorf("error parsing Hosted Configuration Version version_number: %w", err) } - d.Set("application_id", parts[0]) - d.Set("configuration_profile_id", parts[1]) - d.Set("version_number", verNumber) - - return []*schema.ResourceData{d}, nil + return parts[0], parts[1], version, nil } diff --git a/aws/resource_aws_appconfig_hosted_configuration_version_test.go b/aws/resource_aws_appconfig_hosted_configuration_version_test.go index 8215e67cf9a..f08738cfb8d 100644 --- a/aws/resource_aws_appconfig_hosted_configuration_version_test.go +++ b/aws/resource_aws_appconfig_hosted_configuration_version_test.go @@ -2,19 +2,21 @@ package aws import ( "fmt" - "strconv" + "regexp" "testing" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/appconfig" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) func TestAccAWSAppConfigHostedConfigurationVersion_basic(t *testing.T) { - var out appconfig.GetHostedConfigurationVersionOutput + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_appconfig_hosted_configuration_version.test" + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), @@ -22,16 +24,20 @@ func TestAccAWSAppConfigHostedConfigurationVersion_basic(t *testing.T) { CheckDestroy: testAccCheckAppConfigHostedConfigurationVersionDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAppConfigHostedConfigurationVersion(), + Config: testAccAWSAppConfigHostedConfigurationVersion(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigHostedConfigurationVersionExists(resourceName, &out), - testAccCheckAWSAppConfigHostedConfigurationVersionARN(resourceName, &out), - resource.TestCheckResourceAttr(resourceName, "description", "hosted configuration version description"), + testAccCheckAWSAppConfigHostedConfigurationVersionExists(resourceName), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "appconfig", regexp.MustCompile(`application/[a-z0-9]{4,7}/configurationprofile/[a-z0-9]{4,7}/hostedconfigurationversion/[0-9]+`)), + resource.TestCheckResourceAttrPair(resourceName, "application_id", "aws_appconfig_application.test", "id"), + resource.TestCheckResourceAttrPair(resourceName, "configuration_profile_id", "aws_appconfig_configuration_profile.test", "configuration_profile_id"), + resource.TestCheckResourceAttr(resourceName, "content", "{\"foo\":\"bar\"}"), + resource.TestCheckResourceAttr(resourceName, "content_type", "application/json"), + resource.TestCheckResourceAttr(resourceName, "description", rName), + resource.TestCheckResourceAttr(resourceName, "version_number", "1"), ), }, { ResourceName: resourceName, - ImportStateIdFunc: testAccAWSAppConfigHostedConfigurationVersionImportStateIdFunc(resourceName), ImportState: true, ImportStateVerify: true, }, @@ -40,7 +46,7 @@ func TestAccAWSAppConfigHostedConfigurationVersion_basic(t *testing.T) { } func TestAccAWSAppConfigHostedConfigurationVersion_disappears(t *testing.T) { - var out appconfig.GetHostedConfigurationVersionOutput + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_appconfig_hosted_configuration_version.test" resource.ParallelTest(t, resource.TestCase{ @@ -50,10 +56,10 @@ func TestAccAWSAppConfigHostedConfigurationVersion_disappears(t *testing.T) { CheckDestroy: testAccCheckAppConfigHostedConfigurationVersionDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAppConfigHostedConfigurationVersion(), + Config: testAccAWSAppConfigHostedConfigurationVersion(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigHostedConfigurationVersionExists(resourceName, &out), - testAccCheckAWSAppConfigHostedConfigurationVersionDisappears(&out), + testAccCheckAWSAppConfigHostedConfigurationVersionExists(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsAppconfigHostedConfigurationVersion(), resourceName), ), ExpectNonEmptyPlan: true, }, @@ -68,50 +74,38 @@ func testAccCheckAppConfigHostedConfigurationVersionDestroy(s *terraform.State) if rs.Type != "aws_appconfig_hosted_configuration_version" { continue } - versionNumber, err := strconv.Atoi(rs.Primary.Attributes["version_number"]) + + appID, confProfID, versionNumber, err := resourceAwsAppconfigHostedConfigurationVersionParseID(rs.Primary.ID) + if err != nil { - return fmt.Errorf("failed to convert version_number into int (%s): %w", rs.Primary.Attributes["version_number"], err) + return err } input := &appconfig.GetHostedConfigurationVersionInput{ - ApplicationId: aws.String(rs.Primary.Attributes["application_id"]), - ConfigurationProfileId: aws.String(rs.Primary.Attributes["configuration_profile_id"]), + ApplicationId: aws.String(appID), + ConfigurationProfileId: aws.String(confProfID), VersionNumber: aws.Int64(int64(versionNumber)), } output, err := conn.GetHostedConfigurationVersion(input) - if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + if tfawserr.ErrCodeEquals(err, appconfig.ErrCodeResourceNotFoundException) { continue } if err != nil { - return err + return fmt.Errorf("error reading AppConfig Hosted Configuration Version (%s): %w", rs.Primary.ID, err) } if output != nil { - return fmt.Errorf("AppConfig HostedConfigurationVersion (%s) still exists", rs.Primary.ID) + return fmt.Errorf("AppConfig Hosted Configuration Version (%s) still exists", rs.Primary.ID) } } return nil } -func testAccCheckAWSAppConfigHostedConfigurationVersionDisappears(hcv *appconfig.GetHostedConfigurationVersionOutput) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).appconfigconn - - _, err := conn.DeleteHostedConfigurationVersion(&appconfig.DeleteHostedConfigurationVersionInput{ - ApplicationId: hcv.ApplicationId, - ConfigurationProfileId: hcv.ConfigurationProfileId, - VersionNumber: hcv.VersionNumber, - }) - - return err - } -} - -func testAccCheckAWSAppConfigHostedConfigurationVersionExists(resourceName string, hcv *appconfig.GetHostedConfigurationVersionOutput) resource.TestCheckFunc { +func testAccCheckAWSAppConfigHostedConfigurationVersionExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] if !ok { @@ -122,57 +116,46 @@ func testAccCheckAWSAppConfigHostedConfigurationVersionExists(resourceName strin return fmt.Errorf("Resource (%s) ID not set", resourceName) } - conn := testAccProvider.Meta().(*AWSClient).appconfigconn + appID, confProfID, versionNumber, err := resourceAwsAppconfigHostedConfigurationVersionParseID(rs.Primary.ID) - versionNumber, err := strconv.Atoi(rs.Primary.Attributes["version_number"]) if err != nil { - return fmt.Errorf("failed to convert version_number into int (%s): %w", rs.Primary.Attributes["version_number"], err) + return err } + conn := testAccProvider.Meta().(*AWSClient).appconfigconn + output, err := conn.GetHostedConfigurationVersion(&appconfig.GetHostedConfigurationVersionInput{ - ApplicationId: aws.String(rs.Primary.Attributes["application_id"]), - ConfigurationProfileId: aws.String(rs.Primary.Attributes["configuration_profile_id"]), + ApplicationId: aws.String(appID), + ConfigurationProfileId: aws.String(confProfID), VersionNumber: aws.Int64(int64(versionNumber)), }) + if err != nil { - return err + return fmt.Errorf("error reading AppConfig Hosted Configuration Version (%s): %w", rs.Primary.ID, err) } - *hcv = *output + if output == nil { + return fmt.Errorf("AppConfig Hosted Configuration Version (%s) not found", rs.Primary.ID) + } return nil } } -func testAccCheckAWSAppConfigHostedConfigurationVersionARN(resourceName string, hcv *appconfig.GetHostedConfigurationVersionOutput) resource.TestCheckFunc { - return func(s *terraform.State) error { - return testAccCheckResourceAttrRegionalARN(resourceName, "arn", "appconfig", fmt.Sprintf("application/%s/configurationprofile/%s/hostedconfigurationversion/%d", aws.StringValue(hcv.ApplicationId), aws.StringValue(hcv.ConfigurationProfileId), aws.Int64Value(hcv.VersionNumber)))(s) - } -} - -func testAccAWSAppConfigHostedConfigurationVersion() string { - appName := acctest.RandomWithPrefix("tf-acc-test") - profileName := acctest.RandomWithPrefix("tf-acc-test") - return testAccAWSAppConfigApplicationConfigDescription(appName, "test") + testAccAWSAppConfigConfigurationProfile(profileName, "test") + ` +func testAccAWSAppConfigHostedConfigurationVersion(rName string) string { + return composeConfig( + testAccAWSAppConfigConfigurationProfileConfigName(rName), + fmt.Sprintf(` resource "aws_appconfig_hosted_configuration_version" "test" { application_id = aws_appconfig_application.test.id - configuration_profile_id = aws_appconfig_configuration_profile.test.id - description = "hosted configuration version description" + configuration_profile_id = aws_appconfig_configuration_profile.test.configuration_profile_id content_type = "application/json" + content = jsonencode({ - foo = "foo" + foo = "bar" }) -} -` -} - -func testAccAWSAppConfigHostedConfigurationVersionImportStateIdFunc(resourceName string) resource.ImportStateIdFunc { - return func(s *terraform.State) (string, error) { - rs, ok := s.RootModule().Resources[resourceName] - if !ok { - return "", fmt.Errorf("Not Found: %s", resourceName) - } - return rs.Primary.ID, nil - } + description = %q +} +`, rName)) } diff --git a/website/docs/index.html.markdown b/website/docs/index.html.markdown index 018261ceed2..9f38e67fa0f 100644 --- a/website/docs/index.html.markdown +++ b/website/docs/index.html.markdown @@ -254,6 +254,7 @@ for more information about connecting to alternate AWS endpoints or AWS compatib - [`aws_appconfig_application` resource](/docs/providers/aws/r/appconfig_application.html) - [`aws_appconfig_configuration_profile` resource](/docs/providers/aws/r/appconfig_configuration_profile.html) - [`aws_appconfig_environment` resource](/docs/providers/aws/r/appconfig_environment.html) + - [`aws_appconfig_hosted_configuration_version` resource](/docs/providers/aws/r/appconfig_hosted_configuration_version.html) - [`aws_athena_workgroup` resource](/docs/providers/aws/r/athena_workgroup.html) - [`aws_budgets_budget` resource](/docs/providers/aws/r/budgets_budget.html) - [`aws_codedeploy_app` resource](/docs/providers/aws/r/codedeploy_app.html) diff --git a/website/docs/r/appconfig_hosted_configuration_version.html.markdown b/website/docs/r/appconfig_hosted_configuration_version.html.markdown index 1482f5ecb80..a898309c10f 100644 --- a/website/docs/r/appconfig_hosted_configuration_version.html.markdown +++ b/website/docs/r/appconfig_hosted_configuration_version.html.markdown @@ -12,51 +12,41 @@ Provides an AppConfig Hosted Configuration Version resource. ## Example Usage -### AppConfig Hosted Configuration Version - -```hcl -resource "aws_appconfig_hosted_configuration_version" "production" { - application_id = aws_appconfig_application.test.id - configuration_profile_id = aws_appconfig_configuration_profile.test.id - description = "test" +```terraform +resource "aws_appconfig_hosted_configuration_version" "example" { + application_id = aws_appconfig_application.example.id + configuration_profile_id = aws_appconfig_configuration_profile.example.configuration_profile_id + description = "Example Hosted Configuration Version" content_type = "application/json" + content = jsonencode({ - foo = "foo" + foo = "bar" }) } - -resource "aws_appconfig_application" "test" { - name = "test" -} - -resource "aws_appconfig_configuration_profile" "test" { - name = "test" - application_id = aws_appconfig_application.test.id - location_uri = "hosted" -} ``` ## Argument Reference The following arguments are supported: -- `application_id` - (Required) The application id. -- `configuration_profile_id` - (Required) The configuration profile ID. -- `content` - (Required) The content of the configuration or the configuration data. -- `content_type` - (Required) A standard MIME type describing the format of the configuration content. -- `description` - (Optional) A description of the configuration. +* `application_id` - (Required, Forces new resource) The application ID. +* `configuration_profile_id` - (Required, Forces new resource) The configuration profile ID. +* `content` - (Required, Forces new resource) The content of the configuration or the configuration data. +* `content_type` - (Required, Forces new resource) A standard MIME type describing the format of the configuration content. For more information, see [Content-Type](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17). +* `description` - (Optional, Forces new resource) A description of the configuration. ## Attributes Reference In addition to all arguments above, the following attributes are exported: -- `version_number` - hosted configuration version -- `id` - `//` +* `arn` - The Amazon Resource Name (ARN) of the AppConfig hosted configuration version. +* `id` - The AppConfig application ID, configuration profile ID, and version number separated by a slash (`/`). +* `version_number` - The version number of the hosted configuration. ## Import -`aws_appconfig_hosted_configuration_version` can be imported by the Application ID and Configuration Profile ID and Hosted Configuration Version Number, e.g. +AppConfig Hosted Configuration Versions can be imported by using the application ID, configuration profile ID, and version number separated by a slash (`/`), e.g. ``` -$ terraform import aws_appconfig_hosted_configuration_version.test 71abcde/11xxxxx/2 +$ terraform import aws_appconfig_hosted_configuration_version.example 71abcde/11xxxxx/2 ``` From a96caa6c31d68ec18ac23f3e4c90d38af8c8381e Mon Sep 17 00:00:00 2001 From: changelogbot Date: Mon, 12 Jul 2021 14:05:17 +0000 Subject: [PATCH 317/398] Update CHANGELOG.md (Manual Trigger) --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c4e572644d..4b53ab33ee1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,23 @@ ## 3.50.0 (Unreleased) +NOTES: + +* resource/aws_dx_gateway_association_proposal: If an accepted Proposal reaches end-of-life and is removed by AWS do not recreate the resource, instead refreshing Terraform state from the resource's Direct Connect Gateway ID and Associated Gateway ID. ([#19741](https://github.com/hashicorp/terraform-provider-aws/issues/19741)) + FEATURES: +* **New Resource:** `aws_appconfig_application` ([#19307](https://github.com/hashicorp/terraform-provider-aws/issues/19307)) +* **New Resource:** `aws_appconfig_configuration_profile` ([#19320](https://github.com/hashicorp/terraform-provider-aws/issues/19320)) +* **New Resource:** `aws_appconfig_environment` ([#19307](https://github.com/hashicorp/terraform-provider-aws/issues/19307)) * **New Resource:** `aws_config_organization_conformance_pack` ([#17298](https://github.com/hashicorp/terraform-provider-aws/issues/17298)) ENHANCEMENTS: +* resource/aws_cloudwatch_event_target: Add `enable_ecs_managed_tags`, `enable_execute_command`, `placement_constraints`, `propagate_tags`, and `tags` arguments to `ecs_target` block. ([#19975](https://github.com/hashicorp/terraform-provider-aws/issues/19975)) * resource/aws_cognito_user_pool_client: Add the `enable_token_revocation` argument to support targeted sign out ([#20031](https://github.com/hashicorp/terraform-provider-aws/issues/20031)) * resource/aws_fsx_windows_file_system: Add `aliases` argument ([#20054](https://github.com/hashicorp/terraform-provider-aws/issues/20054)) * resource/aws_guardduty_detector: Add `datasources` argument ([#19954](https://github.com/hashicorp/terraform-provider-aws/issues/19954)) +* resource/aws_guardduty_organization_configuration: Add `datasources` argument ([#15241](https://github.com/hashicorp/terraform-provider-aws/issues/15241)) BUG FIXES: From ae6e0e799d6a76ff51313b7b66d48b8e305bf779 Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Thu, 13 May 2021 21:38:03 +0900 Subject: [PATCH 318/398] feat: add appconfig_deployment_strategy --- aws/provider.go | 1 + ...ource_aws_appconfig_deployment_strategy.go | 220 ++++++++++++++++++ 2 files changed, 221 insertions(+) create mode 100644 aws/resource_aws_appconfig_deployment_strategy.go diff --git a/aws/provider.go b/aws/provider.go index f8570bba6aa..724c08aa80d 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -510,6 +510,7 @@ func Provider() *schema.Provider { "aws_appconfig_configuration_profile": resourceAwsAppconfigConfigurationProfile(), "aws_appconfig_environment": resourceAwsAppconfigEnvironment(), "aws_appconfig_hosted_configuration_version": resourceAwsAppconfigHostedConfigurationVersion(), + "aws_appconfig_deployment_strategy": resourceAwsAppconfigDeploymentStrategy(), "aws_appmesh_gateway_route": resourceAwsAppmeshGatewayRoute(), "aws_appmesh_mesh": resourceAwsAppmeshMesh(), "aws_appmesh_route": resourceAwsAppmeshRoute(), diff --git a/aws/resource_aws_appconfig_deployment_strategy.go b/aws/resource_aws_appconfig_deployment_strategy.go new file mode 100644 index 00000000000..089701267cb --- /dev/null +++ b/aws/resource_aws_appconfig_deployment_strategy.go @@ -0,0 +1,220 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" + "github.com/aws/aws-sdk-go/service/appconfig" + "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" +) + +func resourceAwsAppconfigDeploymentStrategy() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsAppconfigDeploymentStrategyCreate, + Read: resourceAwsAppconfigDeploymentStrategyRead, + Update: resourceAwsAppconfigDeploymentStrategyUpdate, + Delete: resourceAwsAppconfigDeploymentStrategyDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 64), + ), + }, + "deployment_duration_in_minutes": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.All( + validation.IntBetween(0, 1440), + ), + }, + "growth_factor": { + Type: schema.TypeFloat, + Required: true, + }, + "replicate_to": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + "NONE", "SSM_DOCUMENT", + }, false), + }, + "description": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.All( + validation.StringLenBetween(0, 1024), + ), + }, + "final_bake_time_in_minutes": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.All( + validation.IntBetween(0, 1440), + ), + }, + "growth_type": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + "EXPONENTIAL", "LINEAR", + }, false), + }, + "tags": tagsSchema(), + "arn": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceAwsAppconfigDeploymentStrategyCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).appconfigconn + + input := &appconfig.CreateDeploymentStrategyInput{ + Name: aws.String(d.Get("name").(string)), + Description: aws.String(d.Get("description").(string)), + DeploymentDurationInMinutes: aws.Int64(int64(d.Get("deployment_duration_in_minutes").(int))), + FinalBakeTimeInMinutes: aws.Int64(int64(d.Get("final_bake_time_in_minutes").(int))), + ReplicateTo: aws.String(d.Get("replicate_to").(string)), + GrowthType: aws.String(d.Get("growth_type").(string)), + GrowthFactor: aws.Float64(d.Get("growth_factor").(float64)), + Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().AppconfigTags(), + } + + strategy, err := conn.CreateDeploymentStrategy(input) + if err != nil { + return fmt.Errorf("Error creating AppConfig DeploymentStrategy: %s", err) + } + + d.SetId(aws.StringValue(strategy.Id)) + + return resourceAwsAppconfigDeploymentStrategyRead(d, meta) +} + +func resourceAwsAppconfigDeploymentStrategyRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).appconfigconn + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + + input := &appconfig.GetDeploymentStrategyInput{ + DeploymentStrategyId: aws.String(d.Id()), + } + + output, err := conn.GetDeploymentStrategy(input) + + if !d.IsNewResource() && isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + log.Printf("[WARN] Appconfig DeploymentStrategy (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error getting AppConfig DeploymentStrategy (%s): %s", d.Id(), err) + } + + if output == nil { + return fmt.Errorf("error getting AppConfig DeploymentStrategy (%s): empty response", d.Id()) + } + + d.Set("name", output.Name) + d.Set("description", output.Description) + d.Set("deployment_duration_in_minutes", output.DeploymentDurationInMinutes) + d.Set("final_bake_time_in_minutes", output.FinalBakeTimeInMinutes) + d.Set("growth_factor", output.GrowthFactor) + d.Set("replicate_to", output.ReplicateTo) + d.Set("growth_type", output.GrowthType) + + strategyARN := arn.ARN{ + AccountID: meta.(*AWSClient).accountid, + Partition: meta.(*AWSClient).partition, + Region: meta.(*AWSClient).region, + Resource: fmt.Sprintf("deploymentstrategy/%s", d.Id()), + Service: "appconfig", + }.String() + d.Set("arn", strategyARN) + + tags, err := keyvaluetags.AppconfigListTags(conn, strategyARN) + if err != nil { + return fmt.Errorf("error getting tags for AppConfig DeploymentStrategy (%s): %s", d.Id(), err) + } + + if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting tags: %s", err) + } + + return nil +} + +func resourceAwsAppconfigDeploymentStrategyUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).appconfigconn + + updateInput := &appconfig.UpdateDeploymentStrategyInput{ + DeploymentStrategyId: aws.String(d.Id()), + } + + if d.HasChange("description") { + updateInput.Description = aws.String(d.Get("description").(string)) + } + + if d.HasChange("growth_type") { + updateInput.GrowthType = aws.String(d.Get("growth_type").(string)) + } + + if d.HasChange("deployment_duration_in_minutes") { + updateInput.DeploymentDurationInMinutes = aws.Int64(int64(d.Get("deployment_duration_in_minutes").(int))) + } + + if d.HasChange("growth_factor") { + updateInput.GrowthFactor = aws.Float64(d.Get("growth_factor").(float64)) + } + + if d.HasChange("final_bake_time_in_minutes") { + updateInput.FinalBakeTimeInMinutes = aws.Int64(int64(d.Get("final_bake_time_in_minutes").(int))) + } + + if d.HasChange("tags") { + o, n := d.GetChange("tags") + if err := keyvaluetags.AppconfigUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return fmt.Errorf("error updating AppConfig DeploymentStrategy (%s) tags: %s", d.Id(), err) + } + } + + _, err := conn.UpdateDeploymentStrategy(updateInput) + if err != nil { + return fmt.Errorf("error updating AppConfig DeploymentStrategy (%s): %s", d.Id(), err) + } + + return resourceAwsAppconfigDeploymentStrategyRead(d, meta) +} + +func resourceAwsAppconfigDeploymentStrategyDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).appconfigconn + + input := &appconfig.DeleteDeploymentStrategyInput{ + DeploymentStrategyId: aws.String(d.Id()), + } + + _, err := conn.DeleteDeploymentStrategy(input) + + if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + return nil + } + + if err != nil { + return fmt.Errorf("error deleting Appconfig DeploymentStrategy (%s): %s", d.Id(), err) + } + + return nil +} From 685851775e685f26a1a3a02bd3a12154f7ca9f8f Mon Sep 17 00:00:00 2001 From: Suzuki Shunsuke Date: Thu, 13 May 2021 22:04:19 +0900 Subject: [PATCH 319/398] docs: add document of appconfig_deployment_strategy --- ...ppconfig_deployment_strategy.html.markdown | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 website/docs/r/appconfig_deployment_strategy.html.markdown diff --git a/website/docs/r/appconfig_deployment_strategy.html.markdown b/website/docs/r/appconfig_deployment_strategy.html.markdown new file mode 100644 index 00000000000..dff2ded4073 --- /dev/null +++ b/website/docs/r/appconfig_deployment_strategy.html.markdown @@ -0,0 +1,58 @@ +--- +subcategory: "AppConfig" +layout: "aws" +page_title: "AWS: aws_appconfig_deployment_strategy" +description: |- + Provides an AppConfig Deployment Strategy resource. +--- + +# Resource: aws_appconfig_deployment_strategy + +Provides an AppConfig Deployment Strategy resource. + +## Example Usage + +### AppConfig Deployment Strategy + +```hcl +resource "aws_appconfig_deployment_strategy" "test" { + name = "test" + description = "test" + deployment_duration_in_minutes = 3 + final_bake_time_in_minutes = 4 + growth_factor = 10 + growth_type = "LINEAR" + replicate_to = "NONE" + tags = { + Env = "Test" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +- `name` - (Required, Forces new resource) A name for the deployment strategy. Must be between 1 and 64 characters in length. +- `description` - (Optional) A description of the deployment strategy. Can be at most 1024 characters. +- `deployment_duration_in_minutes` - (Required) Total amount of time for a deployment to last. +- `final_bake_time_in_minutes` - (Optional) The amount of time AWS AppConfig monitors for alarms before considering the deployment to be complete and no longer eligible for automatic roll back. +- `growth_factor` - (Required) The percentage of targets to receive a deployed configuration during each interval. +- `growth_type` - (Optional) The algorithm used to define how percentage grows over time. +- `replicate_to` - (Required) Save the deployment strategy to a Systems Manager (SSM) document. +- `tags` - (Optional) A map of tags to assign to the resource. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +- `arn` - The Amazon Resource Name (ARN) of the AppConfig Deployment Strategy. +- `id` - The AppConfig Deployment Strategy ID + +## Import + +`aws_appconfig_deployment_strategy` can be imported by the Deployment Strategy ID, e.g. + +``` +$ terraform import aws_appconfig_deployment_strategy.test 11xxxxx +``` From d42388c4a4ba1f49d57aab905c0dbde4052b5963 Mon Sep 17 00:00:00 2001 From: Suzuki Shunsuke Date: Sat, 15 May 2021 07:31:25 +0900 Subject: [PATCH 320/398] test: add tests of aws_appconfig_deployment_strategy --- ..._aws_appconfig_deployment_strategy_test.go | 246 ++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 aws/resource_aws_appconfig_deployment_strategy_test.go diff --git a/aws/resource_aws_appconfig_deployment_strategy_test.go b/aws/resource_aws_appconfig_deployment_strategy_test.go new file mode 100644 index 00000000000..44cdab9665f --- /dev/null +++ b/aws/resource_aws_appconfig_deployment_strategy_test.go @@ -0,0 +1,246 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/appconfig" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccAWSAppConfigDeploymentStrategy_basic(t *testing.T) { + var strategy appconfig.GetDeploymentStrategyOutput + resourceName := "aws_appconfig_deployment_strategy.test" + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigDeploymentStrategyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigDeploymentStrategy(), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigDeploymentStrategyExists(resourceName, &strategy), + testAccCheckAWSAppConfigDeploymentStrategyARN(resourceName, &strategy), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "description", "deployment strategy description"), + ), + }, + { + ResourceName: resourceName, + ImportStateIdFunc: testAccAWSAppConfigDeploymentStrategyImportStateIdFunc(resourceName), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSAppConfigDeploymentStrategy_disappears(t *testing.T) { + var strategy appconfig.GetDeploymentStrategyOutput + resourceName := "aws_appconfig_deployment_strategy.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigDeploymentStrategyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigDeploymentStrategy(), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigDeploymentStrategyExists(resourceName, &strategy), + testAccCheckAWSAppConfigDeploymentStrategyDisappears(&strategy), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAWSAppConfigDeploymentStrategy_Tags(t *testing.T) { + var strategy appconfig.GetDeploymentStrategyOutput + + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_appconfig_deployment_strategy.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigDeploymentStrategyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigDeploymentStrategyTags1(rName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigDeploymentStrategyExists(resourceName, &strategy), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportStateIdFunc: testAccAWSAppConfigDeploymentStrategyImportStateIdFunc(resourceName), + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSAppConfigDeploymentStrategyTags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigDeploymentStrategyExists(resourceName, &strategy), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccAWSAppConfigDeploymentStrategyTags1(rName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigDeploymentStrategyExists(resourceName, &strategy), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + }, + }) +} + +func testAccCheckAppConfigDeploymentStrategyDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).appconfigconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_appconfig_deployment_strategy" { + continue + } + + input := &appconfig.GetDeploymentStrategyInput{ + DeploymentStrategyId: aws.String(rs.Primary.ID), + } + + output, err := conn.GetDeploymentStrategy(input) + + if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + continue + } + + if err != nil { + return err + } + + if output != nil { + return fmt.Errorf("AppConfig DeploymentStrategy (%s) still exists", rs.Primary.ID) + } + } + + return nil +} + +func testAccCheckAWSAppConfigDeploymentStrategyDisappears(strategy *appconfig.GetDeploymentStrategyOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).appconfigconn + + _, err := conn.DeleteDeploymentStrategy(&appconfig.DeleteDeploymentStrategyInput{ + DeploymentStrategyId: strategy.Id, + }) + + return err + } +} + +func testAccCheckAWSAppConfigDeploymentStrategyExists(resourceName string, strategy *appconfig.GetDeploymentStrategyOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Resource not found: %s", resourceName) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("Resource (%s) ID not set", resourceName) + } + + conn := testAccProvider.Meta().(*AWSClient).appconfigconn + + output, err := conn.GetDeploymentStrategy(&appconfig.GetDeploymentStrategyInput{ + DeploymentStrategyId: aws.String(rs.Primary.ID), + }) + if err != nil { + return err + } + + *strategy = *output + + return nil + } +} + +func testAccCheckAWSAppConfigDeploymentStrategyARN(resourceName string, strategy *appconfig.GetDeploymentStrategyOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + return testAccCheckResourceAttrRegionalARN(resourceName, "arn", "appconfig", fmt.Sprintf("deploymentstrategy/%s", aws.StringValue(strategy.Id)))(s) + } +} + +func testAccAWSAppConfigDeploymentStrategy() string { + return fmt.Sprintf(` +resource "aws_appconfig_deployment_strategy" "test" { + name = "%s" + description = "deployment strategy description" + deployment_duration_in_minutes = 3 + final_bake_time_in_minutes = 4 + growth_factor = 10 + growth_type = "LINEAR" + replicate_to = "NONE" + tags = { + Env = "Test" + } +} +`, acctest.RandomWithPrefix("tf-acc-test")) +} + +func testAccAWSAppConfigDeploymentStrategyTags1(rName, tagKey1, tagValue1 string) string { + return fmt.Sprintf(` +resource "aws_appconfig_deployment_strategy" "test" { + name = %[1]q + deployment_duration_in_minutes = 3 + final_bake_time_in_minutes = 4 + growth_factor = 10 + growth_type = "LINEAR" + replicate_to = "NONE" + + tags = { + %[2]q = %[3]q + } +} +`, rName, tagKey1, tagValue1) +} + +func testAccAWSAppConfigDeploymentStrategyTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return testAccAWSAppConfigApplicationTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2) + fmt.Sprintf(` +resource "aws_appconfig_deployment_strategy" "test" { + name = %[1]q + deployment_duration_in_minutes = 3 + final_bake_time_in_minutes = 4 + growth_factor = 10 + growth_type = "LINEAR" + replicate_to = "NONE" + + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } +} +`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +} + +func testAccAWSAppConfigDeploymentStrategyImportStateIdFunc(resourceName string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return "", fmt.Errorf("Not Found: %s", resourceName) + } + + return rs.Primary.ID, nil + } +} From dc46fc5c51087d81ee947a64126bda457890ef96 Mon Sep 17 00:00:00 2001 From: Suzuki Shunsuke Date: Mon, 12 Jul 2021 23:24:10 +0900 Subject: [PATCH 321/398] docs: add CHANGELOG entry --- .changelog/19359.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/19359.txt diff --git a/.changelog/19359.txt b/.changelog/19359.txt new file mode 100644 index 00000000000..d79ead83974 --- /dev/null +++ b/.changelog/19359.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_appconfig_deployment_strategy +``` From 3d12fa85fcfe09ead5d7b33d1920649ef203e7c0 Mon Sep 17 00:00:00 2001 From: Suzuki Shunsuke Date: Mon, 12 Jul 2021 23:28:28 +0900 Subject: [PATCH 322/398] style: sort providers --- aws/provider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/provider.go b/aws/provider.go index 724c08aa80d..bd2642be4db 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -508,9 +508,9 @@ func Provider() *schema.Provider { "aws_appautoscaling_scheduled_action": resourceAwsAppautoscalingScheduledAction(), "aws_appconfig_application": resourceAwsAppconfigApplication(), "aws_appconfig_configuration_profile": resourceAwsAppconfigConfigurationProfile(), + "aws_appconfig_deployment_strategy": resourceAwsAppconfigDeploymentStrategy(), "aws_appconfig_environment": resourceAwsAppconfigEnvironment(), "aws_appconfig_hosted_configuration_version": resourceAwsAppconfigHostedConfigurationVersion(), - "aws_appconfig_deployment_strategy": resourceAwsAppconfigDeploymentStrategy(), "aws_appmesh_gateway_route": resourceAwsAppmeshGatewayRoute(), "aws_appmesh_mesh": resourceAwsAppmeshMesh(), "aws_appmesh_route": resourceAwsAppmeshRoute(), From 326ffda19198003991a981b5e2dc81d3733bee27 Mon Sep 17 00:00:00 2001 From: Suzuki Shunsuke Date: Mon, 12 Jul 2021 23:36:51 +0900 Subject: [PATCH 323/398] style: format test --- aws/resource_aws_appconfig_deployment_strategy_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_appconfig_deployment_strategy_test.go b/aws/resource_aws_appconfig_deployment_strategy_test.go index 44cdab9665f..d34ad401e86 100644 --- a/aws/resource_aws_appconfig_deployment_strategy_test.go +++ b/aws/resource_aws_appconfig_deployment_strategy_test.go @@ -202,7 +202,7 @@ resource "aws_appconfig_deployment_strategy" "test" { func testAccAWSAppConfigDeploymentStrategyTags1(rName, tagKey1, tagValue1 string) string { return fmt.Sprintf(` resource "aws_appconfig_deployment_strategy" "test" { - name = %[1]q + name = %[1]q deployment_duration_in_minutes = 3 final_bake_time_in_minutes = 4 growth_factor = 10 @@ -219,7 +219,7 @@ resource "aws_appconfig_deployment_strategy" "test" { func testAccAWSAppConfigDeploymentStrategyTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { return testAccAWSAppConfigApplicationTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2) + fmt.Sprintf(` resource "aws_appconfig_deployment_strategy" "test" { - name = %[1]q + name = %[1]q deployment_duration_in_minutes = 3 final_bake_time_in_minutes = 4 growth_factor = 10 From 9254447452d7c80b28241f2a70e8dc6d37867ada Mon Sep 17 00:00:00 2001 From: Suzuki Shunsuke Date: Mon, 12 Jul 2021 23:55:38 +0900 Subject: [PATCH 324/398] test: fix the expected length of tags --- aws/resource_aws_appconfig_deployment_strategy_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_appconfig_deployment_strategy_test.go b/aws/resource_aws_appconfig_deployment_strategy_test.go index d34ad401e86..a024b5a7306 100644 --- a/aws/resource_aws_appconfig_deployment_strategy_test.go +++ b/aws/resource_aws_appconfig_deployment_strategy_test.go @@ -25,7 +25,7 @@ func TestAccAWSAppConfigDeploymentStrategy_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckAWSAppConfigDeploymentStrategyExists(resourceName, &strategy), testAccCheckAWSAppConfigDeploymentStrategyARN(resourceName, &strategy), - resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "description", "deployment strategy description"), ), }, From 07b36aa86576ff8ae8ab38193db907d760975af0 Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Mon, 12 Jul 2021 12:42:19 -0400 Subject: [PATCH 325/398] CR updates; add test coverage and default tags support --- ...ource_aws_appconfig_deployment_strategy.go | 192 +++++++++-------- ..._aws_appconfig_deployment_strategy_test.go | 203 ++++++++++++------ website/docs/index.html.markdown | 1 + ...ppconfig_deployment_strategy.html.markdown | 38 ++-- 4 files changed, 261 insertions(+), 173 deletions(-) diff --git a/aws/resource_aws_appconfig_deployment_strategy.go b/aws/resource_aws_appconfig_deployment_strategy.go index 089701267cb..5f4140ecd67 100644 --- a/aws/resource_aws_appconfig_deployment_strategy.go +++ b/aws/resource_aws_appconfig_deployment_strategy.go @@ -7,6 +7,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/appconfig" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "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" @@ -23,80 +24,83 @@ func resourceAwsAppconfigDeploymentStrategy() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "name": { + "arn": { Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.All( - validation.StringLenBetween(1, 64), - ), + Computed: true, }, "deployment_duration_in_minutes": { - Type: schema.TypeInt, - Required: true, - ValidateFunc: validation.All( - validation.IntBetween(0, 1440), - ), - }, - "growth_factor": { - Type: schema.TypeFloat, - Required: true, - }, - "replicate_to": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - "NONE", "SSM_DOCUMENT", - }, false), + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(0, 1440), }, "description": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.All( - validation.StringLenBetween(0, 1024), - ), + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(0, 1024), }, "final_bake_time_in_minutes": { - Type: schema.TypeInt, - Optional: true, - ValidateFunc: validation.All( - validation.IntBetween(0, 1440), - ), + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 1440), + }, + "growth_factor": { + Type: schema.TypeFloat, + Required: true, + ValidateFunc: validation.FloatBetween(1.0, 100.0), }, "growth_type": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice([]string{ - "EXPONENTIAL", "LINEAR", - }, false), + Type: schema.TypeString, + Optional: true, + Default: appconfig.GrowthTypeLinear, + ValidateFunc: validation.StringInSlice(appconfig.GrowthType_Values(), false), }, - "tags": tagsSchema(), - "arn": { - Type: schema.TypeString, - Computed: true, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 64), + }, + "replicate_to": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(appconfig.ReplicateTo_Values(), false), }, + "tags": tagsSchema(), + "tags_all": tagsSchemaComputed(), }, + CustomizeDiff: SetTagsDiff, } } func resourceAwsAppconfigDeploymentStrategyCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).appconfigconn + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig + tags := defaultTagsConfig.MergeTags(keyvaluetags.New(d.Get("tags").(map[string]interface{}))) + + name := d.Get("name").(string) input := &appconfig.CreateDeploymentStrategyInput{ - Name: aws.String(d.Get("name").(string)), - Description: aws.String(d.Get("description").(string)), DeploymentDurationInMinutes: aws.Int64(int64(d.Get("deployment_duration_in_minutes").(int))), - FinalBakeTimeInMinutes: aws.Int64(int64(d.Get("final_bake_time_in_minutes").(int))), - ReplicateTo: aws.String(d.Get("replicate_to").(string)), - GrowthType: aws.String(d.Get("growth_type").(string)), GrowthFactor: aws.Float64(d.Get("growth_factor").(float64)), - Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().AppconfigTags(), + GrowthType: aws.String(d.Get("growth_type").(string)), + Name: aws.String(name), + ReplicateTo: aws.String(d.Get("replicate_to").(string)), + Tags: tags.IgnoreAws().AppconfigTags(), + } + + if v, ok := d.GetOk("description"); ok { + input.Description = aws.String(v.(string)) + } + + if v, ok := d.GetOk("final_bake_time_in_minutes"); ok { + input.FinalBakeTimeInMinutes = aws.Int64(int64(v.(int))) } strategy, err := conn.CreateDeploymentStrategy(input) + if err != nil { - return fmt.Errorf("Error creating AppConfig DeploymentStrategy: %s", err) + return fmt.Errorf("error creating AppConfig Deployment Strategy (%s): %w", name, err) } d.SetId(aws.StringValue(strategy.Id)) @@ -106,6 +110,7 @@ func resourceAwsAppconfigDeploymentStrategyCreate(d *schema.ResourceData, meta i func resourceAwsAppconfigDeploymentStrategyRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).appconfigconn + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig input := &appconfig.GetDeploymentStrategyInput{ @@ -114,44 +119,52 @@ func resourceAwsAppconfigDeploymentStrategyRead(d *schema.ResourceData, meta int output, err := conn.GetDeploymentStrategy(input) - if !d.IsNewResource() && isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { - log.Printf("[WARN] Appconfig DeploymentStrategy (%s) not found, removing from state", d.Id()) + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, appconfig.ErrCodeResourceNotFoundException) { + log.Printf("[WARN] Appconfig Deployment Strategy (%s) not found, removing from state", d.Id()) d.SetId("") return nil } if err != nil { - return fmt.Errorf("error getting AppConfig DeploymentStrategy (%s): %s", d.Id(), err) + return fmt.Errorf("error getting AppConfig Deployment Strategy (%s): %w", d.Id(), err) } if output == nil { - return fmt.Errorf("error getting AppConfig DeploymentStrategy (%s): empty response", d.Id()) + return fmt.Errorf("error getting AppConfig Deployment Strategy (%s): empty response", d.Id()) } - d.Set("name", output.Name) d.Set("description", output.Description) d.Set("deployment_duration_in_minutes", output.DeploymentDurationInMinutes) d.Set("final_bake_time_in_minutes", output.FinalBakeTimeInMinutes) d.Set("growth_factor", output.GrowthFactor) - d.Set("replicate_to", output.ReplicateTo) d.Set("growth_type", output.GrowthType) + d.Set("name", output.Name) + d.Set("replicate_to", output.ReplicateTo) - strategyARN := arn.ARN{ + arn := arn.ARN{ AccountID: meta.(*AWSClient).accountid, Partition: meta.(*AWSClient).partition, Region: meta.(*AWSClient).region, Resource: fmt.Sprintf("deploymentstrategy/%s", d.Id()), Service: "appconfig", }.String() - d.Set("arn", strategyARN) + d.Set("arn", arn) + + tags, err := keyvaluetags.AppconfigListTags(conn, arn) - tags, err := keyvaluetags.AppconfigListTags(conn, strategyARN) if err != nil { - return fmt.Errorf("error getting tags for AppConfig DeploymentStrategy (%s): %s", d.Id(), err) + return fmt.Errorf("error listing tags for AppConfig Deployment Strategy (%s): %w", d.Id(), err) + } + + tags = tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig) + + //lintignore:AWSR002 + if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting tags: %w", err) } - if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { - return fmt.Errorf("error setting tags: %s", err) + if err := d.Set("tags_all", tags.Map()); err != nil { + return fmt.Errorf("error setting tags_all: %w", err) } return nil @@ -160,40 +173,43 @@ func resourceAwsAppconfigDeploymentStrategyRead(d *schema.ResourceData, meta int func resourceAwsAppconfigDeploymentStrategyUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).appconfigconn - updateInput := &appconfig.UpdateDeploymentStrategyInput{ - DeploymentStrategyId: aws.String(d.Id()), - } + if d.HasChangesExcept("tags", "tags_all") { + updateInput := &appconfig.UpdateDeploymentStrategyInput{ + DeploymentStrategyId: aws.String(d.Id()), + } - if d.HasChange("description") { - updateInput.Description = aws.String(d.Get("description").(string)) - } + if d.HasChange("deployment_duration_in_minutes") { + updateInput.DeploymentDurationInMinutes = aws.Int64(int64(d.Get("deployment_duration_in_minutes").(int))) + } - if d.HasChange("growth_type") { - updateInput.GrowthType = aws.String(d.Get("growth_type").(string)) - } + if d.HasChange("description") { + updateInput.Description = aws.String(d.Get("description").(string)) + } - if d.HasChange("deployment_duration_in_minutes") { - updateInput.DeploymentDurationInMinutes = aws.Int64(int64(d.Get("deployment_duration_in_minutes").(int))) - } + if d.HasChange("final_bake_time_in_minutes") { + updateInput.FinalBakeTimeInMinutes = aws.Int64(int64(d.Get("final_bake_time_in_minutes").(int))) + } - if d.HasChange("growth_factor") { - updateInput.GrowthFactor = aws.Float64(d.Get("growth_factor").(float64)) - } + if d.HasChange("growth_factor") { + updateInput.GrowthFactor = aws.Float64(d.Get("growth_factor").(float64)) + } - if d.HasChange("final_bake_time_in_minutes") { - updateInput.FinalBakeTimeInMinutes = aws.Int64(int64(d.Get("final_bake_time_in_minutes").(int))) - } + if d.HasChange("growth_type") { + updateInput.GrowthType = aws.String(d.Get("growth_type").(string)) + } - if d.HasChange("tags") { - o, n := d.GetChange("tags") - if err := keyvaluetags.AppconfigUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { - return fmt.Errorf("error updating AppConfig DeploymentStrategy (%s) tags: %s", d.Id(), err) + _, err := conn.UpdateDeploymentStrategy(updateInput) + + if err != nil { + return fmt.Errorf("error updating AppConfig Deployment Strategy (%s): %w", d.Id(), err) } } - _, err := conn.UpdateDeploymentStrategy(updateInput) - if err != nil { - return fmt.Errorf("error updating AppConfig DeploymentStrategy (%s): %s", d.Id(), err) + if d.HasChange("tags_all") { + o, n := d.GetChange("tags_all") + if err := keyvaluetags.AppconfigUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return fmt.Errorf("error updating AppConfig Deployment Strategy (%s) tags: %w", d.Id(), err) + } } return resourceAwsAppconfigDeploymentStrategyRead(d, meta) @@ -208,12 +224,12 @@ func resourceAwsAppconfigDeploymentStrategyDelete(d *schema.ResourceData, meta i _, err := conn.DeleteDeploymentStrategy(input) - if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + if tfawserr.ErrCodeEquals(err, appconfig.ErrCodeResourceNotFoundException) { return nil } if err != nil { - return fmt.Errorf("error deleting Appconfig DeploymentStrategy (%s): %s", d.Id(), err) + return fmt.Errorf("error deleting Appconfig Deployment Strategy (%s): %w", d.Id(), err) } return nil diff --git a/aws/resource_aws_appconfig_deployment_strategy_test.go b/aws/resource_aws_appconfig_deployment_strategy_test.go index a024b5a7306..053a36d05c9 100644 --- a/aws/resource_aws_appconfig_deployment_strategy_test.go +++ b/aws/resource_aws_appconfig_deployment_strategy_test.go @@ -2,18 +2,21 @@ package aws import ( "fmt" + "regexp" "testing" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/appconfig" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) func TestAccAWSAppConfigDeploymentStrategy_basic(t *testing.T) { - var strategy appconfig.GetDeploymentStrategyOutput + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_appconfig_deployment_strategy.test" + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), @@ -21,26 +24,107 @@ func TestAccAWSAppConfigDeploymentStrategy_basic(t *testing.T) { CheckDestroy: testAccCheckAppConfigDeploymentStrategyDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAppConfigDeploymentStrategy(), + Config: testAccAWSAppConfigDeploymentStrategyConfigName(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigDeploymentStrategyExists(resourceName, &strategy), - testAccCheckAWSAppConfigDeploymentStrategyARN(resourceName, &strategy), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "description", "deployment strategy description"), + testAccCheckAWSAppConfigDeploymentStrategyExists(resourceName), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "appconfig", regexp.MustCompile(`deploymentstrategy/[a-z0-9]{4,7}`)), + resource.TestCheckResourceAttr(resourceName, "deployment_duration_in_minutes", "3"), + resource.TestCheckResourceAttr(resourceName, "growth_factor", "10"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "replicate_to", appconfig.ReplicateToNone), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSAppConfigDeploymentStrategy_updateDescription(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + description := acctest.RandomWithPrefix("tf-acc-test-update") + resourceName := "aws_appconfig_deployment_strategy.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigDeploymentStrategyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigDeploymentStrategyConfigDescription(rName, rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigDeploymentStrategyExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "description", rName), + ), + }, + { + Config: testAccAWSAppConfigDeploymentStrategyConfigDescription(rName, description), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigDeploymentStrategyExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "description", description), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSAppConfigDeploymentStrategy_updateFinalBakeTime(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_appconfig_deployment_strategy.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigDeploymentStrategyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigDeploymentStrategyConfigFinalBakeTime(rName, 60), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigDeploymentStrategyExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "final_bake_time_in_minutes", "60"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSAppConfigDeploymentStrategyConfigFinalBakeTime(rName, 30), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigDeploymentStrategyExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "final_bake_time_in_minutes", "30"), ), }, { ResourceName: resourceName, - ImportStateIdFunc: testAccAWSAppConfigDeploymentStrategyImportStateIdFunc(resourceName), ImportState: true, ImportStateVerify: true, }, + { + // Test FinalBakeTimeInMinutes Removal + Config: testAccAWSAppConfigDeploymentStrategyConfigName(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigDeploymentStrategyExists(resourceName), + ), + }, }, }) } func TestAccAWSAppConfigDeploymentStrategy_disappears(t *testing.T) { - var strategy appconfig.GetDeploymentStrategyOutput + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_appconfig_deployment_strategy.test" resource.ParallelTest(t, resource.TestCase{ @@ -50,10 +134,10 @@ func TestAccAWSAppConfigDeploymentStrategy_disappears(t *testing.T) { CheckDestroy: testAccCheckAppConfigDeploymentStrategyDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAppConfigDeploymentStrategy(), + Config: testAccAWSAppConfigDeploymentStrategyConfigName(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigDeploymentStrategyExists(resourceName, &strategy), - testAccCheckAWSAppConfigDeploymentStrategyDisappears(&strategy), + testAccCheckAWSAppConfigDeploymentStrategyExists(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsAppconfigDeploymentStrategy(), resourceName), ), ExpectNonEmptyPlan: true, }, @@ -62,8 +146,6 @@ func TestAccAWSAppConfigDeploymentStrategy_disappears(t *testing.T) { } func TestAccAWSAppConfigDeploymentStrategy_Tags(t *testing.T) { - var strategy appconfig.GetDeploymentStrategyOutput - rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_appconfig_deployment_strategy.test" @@ -76,21 +158,20 @@ func TestAccAWSAppConfigDeploymentStrategy_Tags(t *testing.T) { { Config: testAccAWSAppConfigDeploymentStrategyTags1(rName, "key1", "value1"), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigDeploymentStrategyExists(resourceName, &strategy), + testAccCheckAWSAppConfigDeploymentStrategyExists(resourceName), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), ), }, { ResourceName: resourceName, - ImportStateIdFunc: testAccAWSAppConfigDeploymentStrategyImportStateIdFunc(resourceName), ImportState: true, ImportStateVerify: true, }, { Config: testAccAWSAppConfigDeploymentStrategyTags2(rName, "key1", "value1updated", "key2", "value2"), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigDeploymentStrategyExists(resourceName, &strategy), + testAccCheckAWSAppConfigDeploymentStrategyExists(resourceName), resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), @@ -99,7 +180,7 @@ func TestAccAWSAppConfigDeploymentStrategy_Tags(t *testing.T) { { Config: testAccAWSAppConfigDeploymentStrategyTags1(rName, "key2", "value2"), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigDeploymentStrategyExists(resourceName, &strategy), + testAccCheckAWSAppConfigDeploymentStrategyExists(resourceName), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), ), @@ -122,35 +203,23 @@ func testAccCheckAppConfigDeploymentStrategyDestroy(s *terraform.State) error { output, err := conn.GetDeploymentStrategy(input) - if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + if tfawserr.ErrCodeEquals(err, appconfig.ErrCodeResourceNotFoundException) { continue } if err != nil { - return err + return fmt.Errorf("error getting Appconfig Deployment Strategy (%s): %w", rs.Primary.ID, err) } if output != nil { - return fmt.Errorf("AppConfig DeploymentStrategy (%s) still exists", rs.Primary.ID) + return fmt.Errorf("AppConfig Deployment Strategy (%s) still exists", rs.Primary.ID) } } return nil } -func testAccCheckAWSAppConfigDeploymentStrategyDisappears(strategy *appconfig.GetDeploymentStrategyOutput) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).appconfigconn - - _, err := conn.DeleteDeploymentStrategy(&appconfig.DeleteDeploymentStrategyInput{ - DeploymentStrategyId: strategy.Id, - }) - - return err - } -} - -func testAccCheckAWSAppConfigDeploymentStrategyExists(resourceName string, strategy *appconfig.GetDeploymentStrategyOutput) resource.TestCheckFunc { +func testAccCheckAWSAppConfigDeploymentStrategyExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] if !ok { @@ -163,40 +232,57 @@ func testAccCheckAWSAppConfigDeploymentStrategyExists(resourceName string, strat conn := testAccProvider.Meta().(*AWSClient).appconfigconn - output, err := conn.GetDeploymentStrategy(&appconfig.GetDeploymentStrategyInput{ + input := &appconfig.GetDeploymentStrategyInput{ DeploymentStrategyId: aws.String(rs.Primary.ID), - }) + } + + output, err := conn.GetDeploymentStrategy(input) + if err != nil { - return err + return fmt.Errorf("error getting Appconfig Deployment Strategy (%s): %w", rs.Primary.ID, err) } - *strategy = *output + if output == nil { + return fmt.Errorf("AppConfig Deployment Strategy (%s) not found", rs.Primary.ID) + } return nil } } -func testAccCheckAWSAppConfigDeploymentStrategyARN(resourceName string, strategy *appconfig.GetDeploymentStrategyOutput) resource.TestCheckFunc { - return func(s *terraform.State) error { - return testAccCheckResourceAttrRegionalARN(resourceName, "arn", "appconfig", fmt.Sprintf("deploymentstrategy/%s", aws.StringValue(strategy.Id)))(s) - } +func testAccAWSAppConfigDeploymentStrategyConfigName(rName string) string { + return fmt.Sprintf(` +resource "aws_appconfig_deployment_strategy" "test" { + name = %[1]q + deployment_duration_in_minutes = 3 + growth_factor = 10 + replicate_to = "NONE" +} +`, rName) } -func testAccAWSAppConfigDeploymentStrategy() string { +func testAccAWSAppConfigDeploymentStrategyConfigDescription(rName, description string) string { return fmt.Sprintf(` resource "aws_appconfig_deployment_strategy" "test" { - name = "%s" - description = "deployment strategy description" + name = %[1]q deployment_duration_in_minutes = 3 - final_bake_time_in_minutes = 4 + description = %[2]q growth_factor = 10 - growth_type = "LINEAR" replicate_to = "NONE" - tags = { - Env = "Test" - } } -`, acctest.RandomWithPrefix("tf-acc-test")) +`, rName, description) +} + +func testAccAWSAppConfigDeploymentStrategyConfigFinalBakeTime(rName string, time int) string { + return fmt.Sprintf(` +resource "aws_appconfig_deployment_strategy" "test" { + name = %[1]q + deployment_duration_in_minutes = 3 + final_bake_time_in_minutes = %[2]d + growth_factor = 10 + replicate_to = "NONE" +} +`, rName, time) } func testAccAWSAppConfigDeploymentStrategyTags1(rName, tagKey1, tagValue1 string) string { @@ -204,9 +290,7 @@ func testAccAWSAppConfigDeploymentStrategyTags1(rName, tagKey1, tagValue1 string resource "aws_appconfig_deployment_strategy" "test" { name = %[1]q deployment_duration_in_minutes = 3 - final_bake_time_in_minutes = 4 growth_factor = 10 - growth_type = "LINEAR" replicate_to = "NONE" tags = { @@ -217,13 +301,11 @@ resource "aws_appconfig_deployment_strategy" "test" { } func testAccAWSAppConfigDeploymentStrategyTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { - return testAccAWSAppConfigApplicationTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2) + fmt.Sprintf(` + return fmt.Sprintf(` resource "aws_appconfig_deployment_strategy" "test" { name = %[1]q deployment_duration_in_minutes = 3 - final_bake_time_in_minutes = 4 growth_factor = 10 - growth_type = "LINEAR" replicate_to = "NONE" tags = { @@ -233,14 +315,3 @@ resource "aws_appconfig_deployment_strategy" "test" { } `, rName, tagKey1, tagValue1, tagKey2, tagValue2) } - -func testAccAWSAppConfigDeploymentStrategyImportStateIdFunc(resourceName string) resource.ImportStateIdFunc { - return func(s *terraform.State) (string, error) { - rs, ok := s.RootModule().Resources[resourceName] - if !ok { - return "", fmt.Errorf("Not Found: %s", resourceName) - } - - return rs.Primary.ID, nil - } -} diff --git a/website/docs/index.html.markdown b/website/docs/index.html.markdown index 9f38e67fa0f..570f08ad738 100644 --- a/website/docs/index.html.markdown +++ b/website/docs/index.html.markdown @@ -253,6 +253,7 @@ for more information about connecting to alternate AWS endpoints or AWS compatib - [`aws_apigatewayv2_stage` resource](/docs/providers/aws/r/apigatewayv2_stage.html) - [`aws_appconfig_application` resource](/docs/providers/aws/r/appconfig_application.html) - [`aws_appconfig_configuration_profile` resource](/docs/providers/aws/r/appconfig_configuration_profile.html) + - [`aws_appconfig_deployment_strategy` resource](/docs/providers/aws/r/appconfig_deployment_strategy.html) - [`aws_appconfig_environment` resource](/docs/providers/aws/r/appconfig_environment.html) - [`aws_appconfig_hosted_configuration_version` resource](/docs/providers/aws/r/appconfig_hosted_configuration_version.html) - [`aws_athena_workgroup` resource](/docs/providers/aws/r/athena_workgroup.html) diff --git a/website/docs/r/appconfig_deployment_strategy.html.markdown b/website/docs/r/appconfig_deployment_strategy.html.markdown index dff2ded4073..139672745bd 100644 --- a/website/docs/r/appconfig_deployment_strategy.html.markdown +++ b/website/docs/r/appconfig_deployment_strategy.html.markdown @@ -12,19 +12,18 @@ Provides an AppConfig Deployment Strategy resource. ## Example Usage -### AppConfig Deployment Strategy - -```hcl -resource "aws_appconfig_deployment_strategy" "test" { - name = "test" - description = "test" +```terraform +resource "aws_appconfig_deployment_strategy" "example" { + name = "example-deployment-strategy-tf" + description = "Example Deployment Strategy" deployment_duration_in_minutes = 3 final_bake_time_in_minutes = 4 growth_factor = 10 growth_type = "LINEAR" replicate_to = "NONE" + tags = { - Env = "Test" + Type = "AppConfig Deployment Strategy" } } ``` @@ -33,26 +32,27 @@ resource "aws_appconfig_deployment_strategy" "test" { The following arguments are supported: -- `name` - (Required, Forces new resource) A name for the deployment strategy. Must be between 1 and 64 characters in length. -- `description` - (Optional) A description of the deployment strategy. Can be at most 1024 characters. -- `deployment_duration_in_minutes` - (Required) Total amount of time for a deployment to last. -- `final_bake_time_in_minutes` - (Optional) The amount of time AWS AppConfig monitors for alarms before considering the deployment to be complete and no longer eligible for automatic roll back. -- `growth_factor` - (Required) The percentage of targets to receive a deployed configuration during each interval. -- `growth_type` - (Optional) The algorithm used to define how percentage grows over time. -- `replicate_to` - (Required) Save the deployment strategy to a Systems Manager (SSM) document. -- `tags` - (Optional) A map of tags to assign to the resource. +* `deployment_duration_in_minutes` - (Required) Total amount of time for a deployment to last. Minimum value of 0, maximum value of 1440. +* `growth_factor` - (Required) The percentage of targets to receive a deployed configuration during each interval. Minimum value of 1.0, maximum value of 100.0. +* `name` - (Required, Forces new resource) A name for the deployment strategy. Must be between 1 and 64 characters in length. +* `replicate_to` - (Required, Forces new resource) Where to save the deployment strategy. Valid values: `NONE` and `SSM_DOCUMENT`. +* `description` - (Optional) A description of the deployment strategy. Can be at most 1024 characters. +* `final_bake_time_in_minutes` - (Optional) The amount of time AWS AppConfig monitors for alarms before considering the deployment to be complete and no longer eligible for automatic roll back. Minimum value of 0, maximum value of 1440. +* `growth_type` - (Optional) The algorithm used to define how percentage grows over time. Valid value: `LINEAR` and `EXPONENTIAL`. Defaults to `LINEAR`. +* `tags` - (Optional) A map of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](/docs/providers/aws/index.html#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. ## Attributes Reference In addition to all arguments above, the following attributes are exported: -- `arn` - The Amazon Resource Name (ARN) of the AppConfig Deployment Strategy. -- `id` - The AppConfig Deployment Strategy ID +* `id` - The AppConfig deployment strategy ID. +* `arn` - The Amazon Resource Name (ARN) of the AppConfig Deployment Strategy. +* `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](/docs/providers/aws/index.html#default_tags-configuration-block). ## Import -`aws_appconfig_deployment_strategy` can be imported by the Deployment Strategy ID, e.g. +AppConfig Deployment Strategies can be imported by using their deployment strategy ID, e.g. ``` -$ terraform import aws_appconfig_deployment_strategy.test 11xxxxx +$ terraform import aws_appconfig_deployment_strategy.example 11xxxxx ``` From 5bb708888ebcddf2884f0bcabb5f21faa8df05a0 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Mon, 12 Jul 2021 14:41:45 -0400 Subject: [PATCH 326/398] r/s3_bucket: Add delete_marker_replication_status argument --- aws/resource_aws_s3_bucket.go | 65 +++++++++++------------------------ 1 file changed, 20 insertions(+), 45 deletions(-) diff --git a/aws/resource_aws_s3_bucket.go b/aws/resource_aws_s3_bucket.go index 839a64a46f0..accb259dd5f 100644 --- a/aws/resource_aws_s3_bucket.go +++ b/aws/resource_aws_s3_bucket.go @@ -524,20 +524,10 @@ func resourceAwsS3Bucket() *schema.Resource { }, }, }, - "delete_marker_replication": { - Type: schema.TypeList, - Optional: true, - MinItems: 1, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "status": { - Type: schema.TypeString, - Optional: true, - Default: s3.DeleteMarkerReplicationStatusDisabled, - }, - }, - }, + "delete_marker_replication_status": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{s3.DeleteMarkerReplicationStatusEnabled}, false), }, }, }, @@ -2125,18 +2115,21 @@ func resourceAwsS3BucketReplicationConfigurationUpdate(s3conn *s3.S3, d *schema. rcRule.Filter.Prefix = aws.String(filter["prefix"].(string)) } + if dmr, ok := rr["delete_marker_replication_status"].(string); ok && dmr != "" { + rcRule.DeleteMarkerReplication = &s3.DeleteMarkerReplication{ + Status: aws.String(dmr), + } + } else { + rcRule.DeleteMarkerReplication = &s3.DeleteMarkerReplication{ + Status: aws.String(s3.DeleteMarkerReplicationStatusDisabled), + } + } + } else { // XML schema V1. rcRule.Prefix = aws.String(rr["prefix"].(string)) } - if d, ok := rr["delete_marker_replication"].([]interface{}); ok && len(d) > 0 && d[0] != nil { - dmr := d[0].(map[string]interface{}) - rcRule.DeleteMarkerReplication = &s3.DeleteMarkerReplication{ - Status: aws.String(dmr["status"].(string)), - } - } - rules = append(rules, rcRule) } @@ -2427,14 +2420,10 @@ func flattenAwsS3BucketReplicationConfiguration(r *s3.ReplicationConfiguration) m["tags"] = keyvaluetags.S3KeyValueTags(a.Tags).IgnoreAws().Map() } t["filter"] = []interface{}{m} - } - if dmr := v.DeleteMarkerReplication; dmr != nil { - m := map[string]interface{}{} - if dmr.Status != nil { - m["status"] = aws.StringValue(v.DeleteMarkerReplication.Status) + if v.DeleteMarkerReplication != nil && v.DeleteMarkerReplication.Status != nil && aws.StringValue(v.DeleteMarkerReplication.Status) == s3.DeleteMarkerReplicationStatusEnabled { + t["delete_marker_replication_status"] = aws.StringValue(v.DeleteMarkerReplication.Status) } - t["delete_marker_replication"] = []interface{}{m} } rules = append(rules, t) @@ -2602,11 +2591,12 @@ func rulesHash(v interface{}) int { if v, ok := m["priority"]; ok { buf.WriteString(fmt.Sprintf("%d-", v.(int))) } - if v, ok := m["delete_marker_replication"].([]interface{}); ok && len(v) > 0 && v[0] != nil { - buf.WriteString(fmt.Sprintf("%d-", deleteMarkerReplicationHash(v[0]))) - } if v, ok := m["filter"].([]interface{}); ok && len(v) > 0 && v[0] != nil { buf.WriteString(fmt.Sprintf("%d-", replicationRuleFilterHash(v[0]))) + + if v, ok := m["delete_marker_replication_status"]; ok && v.(string) == s3.DeleteMarkerReplicationStatusEnabled { + buf.WriteString(fmt.Sprintf("%s-", v.(string))) + } } return hashcode.String(buf.String()) } @@ -2628,21 +2618,6 @@ func replicationRuleFilterHash(v interface{}) int { return hashcode.String(buf.String()) } -func deleteMarkerReplicationHash(v interface{}) int { - var buf bytes.Buffer - m, ok := v.(map[string]interface{}) - - if !ok { - return 0 - } - - if v, ok := m["status"]; ok { - buf.WriteString(fmt.Sprintf("%s-", v.(string))) - } - - return hashcode.String(buf.String()) -} - func destinationHash(v interface{}) int { var buf bytes.Buffer m, ok := v.(map[string]interface{}) From 74b57c254e78e8bfb30d1a1e02c77ba192b42e39 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Mon, 12 Jul 2021 14:42:10 -0400 Subject: [PATCH 327/398] tests/r/s3_bucket: Add delete_marker_replication_status argument --- aws/resource_aws_s3_bucket_test.go | 120 ++++++++++++----------------- 1 file changed, 51 insertions(+), 69 deletions(-) diff --git a/aws/resource_aws_s3_bucket_test.go b/aws/resource_aws_s3_bucket_test.go index cf31272e75f..6fa9c61fb78 100644 --- a/aws/resource_aws_s3_bucket_test.go +++ b/aws/resource_aws_s3_bucket_test.go @@ -164,7 +164,7 @@ func testS3BucketObjectLockEnabled(conn *s3.S3, bucket string) (bool, error) { return aws.StringValue(output.ObjectLockConfiguration.ObjectLockEnabled) == s3.ObjectLockEnabledEnabled, nil } -func TestAccAWSS3Bucket_basic(t *testing.T) { +func TestAccAWSS3Bucket_Basic_basic(t *testing.T) { bucketName := acctest.RandomWithPrefix("tf-test-bucket") region := testAccGetRegion() hostedZoneID, _ := HostedZoneIDForRegion(region) @@ -201,7 +201,7 @@ func TestAccAWSS3Bucket_basic(t *testing.T) { // Support for common Terraform 0.11 pattern // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/7868 -func TestAccAWSS3Bucket_Bucket_EmptyString(t *testing.T) { +func TestAccAWSS3Bucket_Basic_emptyString(t *testing.T) { resourceName := "aws_s3_bucket.test" resource.ParallelTest(t, resource.TestCase{ @@ -227,7 +227,7 @@ func TestAccAWSS3Bucket_Bucket_EmptyString(t *testing.T) { }) } -func TestAccAWSS3Bucket_tagsWithNoSystemTags(t *testing.T) { +func TestAccAWSS3Bucket_Tags_withNoSystemTags(t *testing.T) { resourceName := "aws_s3_bucket.bucket" bucketName := acctest.RandomWithPrefix("tf-test-bucket") @@ -286,7 +286,7 @@ func TestAccAWSS3Bucket_tagsWithNoSystemTags(t *testing.T) { }) } -func TestAccAWSS3Bucket_tagsWithSystemTags(t *testing.T) { +func TestAccAWSS3Bucket_Tags_withSystemTags(t *testing.T) { resourceName := "aws_s3_bucket.bucket" bucketName := acctest.RandomWithPrefix("tf-test-bucket") @@ -371,7 +371,7 @@ func TestAccAWSS3Bucket_tagsWithSystemTags(t *testing.T) { }) } -func TestAccAWSS3Bucket_ignoreTags(t *testing.T) { +func TestAccAWSS3Bucket_Tags_ignoreTags(t *testing.T) { resourceName := "aws_s3_bucket.bucket" bucketName := acctest.RandomWithPrefix("tf-acc-test") @@ -416,7 +416,7 @@ func TestAccAWSS3Bucket_ignoreTags(t *testing.T) { }) } -func TestAccAWSS3Bucket_withTags(t *testing.T) { +func TestAccAWSS3Bucket_Tags_basic(t *testing.T) { rInt := acctest.RandInt() resourceName := "aws_s3_bucket.bucket1" @@ -439,7 +439,7 @@ func TestAccAWSS3Bucket_withTags(t *testing.T) { }) } -func TestAccAWSS3Bucket_namePrefix(t *testing.T) { +func TestAccAWSS3Bucket_Basic_namePrefix(t *testing.T) { resourceName := "aws_s3_bucket.test" resource.ParallelTest(t, resource.TestCase{ @@ -465,7 +465,7 @@ func TestAccAWSS3Bucket_namePrefix(t *testing.T) { }) } -func TestAccAWSS3Bucket_generatedName(t *testing.T) { +func TestAccAWSS3Bucket_Basic_generatedName(t *testing.T) { resourceName := "aws_s3_bucket.test" resource.ParallelTest(t, resource.TestCase{ @@ -490,7 +490,7 @@ func TestAccAWSS3Bucket_generatedName(t *testing.T) { }) } -func TestAccAWSS3Bucket_acceleration(t *testing.T) { +func TestAccAWSS3Bucket_Basic_acceleration(t *testing.T) { bucketName := acctest.RandomWithPrefix("tf-test-bucket") resourceName := "aws_s3_bucket.bucket" @@ -527,7 +527,7 @@ func TestAccAWSS3Bucket_acceleration(t *testing.T) { }) } -func TestAccAWSS3Bucket_RequestPayer(t *testing.T) { +func TestAccAWSS3Bucket_Basic_requestPayer(t *testing.T) { bucketName := acctest.RandomWithPrefix("tf-test-bucket") resourceName := "aws_s3_bucket.bucket" @@ -563,7 +563,7 @@ func TestAccAWSS3Bucket_RequestPayer(t *testing.T) { }) } -func TestAccAWSS3Bucket_Policy(t *testing.T) { +func TestAccAWSS3Bucket_Security_policy(t *testing.T) { bucketName := acctest.RandomWithPrefix("tf-test-bucket") partition := testAccGetPartition() resourceName := "aws_s3_bucket.bucket" @@ -615,7 +615,7 @@ func TestAccAWSS3Bucket_Policy(t *testing.T) { }) } -func TestAccAWSS3Bucket_UpdateAcl(t *testing.T) { +func TestAccAWSS3Bucket_Security_updateACL(t *testing.T) { bucketName := acctest.RandomWithPrefix("tf-test-bucket") resourceName := "aws_s3_bucket.bucket" @@ -649,7 +649,7 @@ func TestAccAWSS3Bucket_UpdateAcl(t *testing.T) { }) } -func TestAccAWSS3Bucket_UpdateGrant(t *testing.T) { +func TestAccAWSS3Bucket_Security_updateGrant(t *testing.T) { bucketName := acctest.RandomWithPrefix("tf-test-bucket") resourceName := "aws_s3_bucket.bucket" @@ -707,7 +707,7 @@ func TestAccAWSS3Bucket_UpdateGrant(t *testing.T) { }) } -func TestAccAWSS3Bucket_AclToGrant(t *testing.T) { +func TestAccAWSS3Bucket_Security_ACLToGrant(t *testing.T) { bucketName := acctest.RandomWithPrefix("tf-test-bucket") resourceName := "aws_s3_bucket.bucket" @@ -737,7 +737,7 @@ func TestAccAWSS3Bucket_AclToGrant(t *testing.T) { }) } -func TestAccAWSS3Bucket_GrantToAcl(t *testing.T) { +func TestAccAWSS3Bucket_Security_GrantToACL(t *testing.T) { bucketName := acctest.RandomWithPrefix("tf-test-bucket") resourceName := "aws_s3_bucket.bucket" @@ -767,7 +767,7 @@ func TestAccAWSS3Bucket_GrantToAcl(t *testing.T) { }) } -func TestAccAWSS3Bucket_Website_Simple(t *testing.T) { +func TestAccAWSS3Bucket_Web_simple(t *testing.T) { bucketName := acctest.RandomWithPrefix("tf-test-bucket") region := testAccGetRegion() resourceName := "aws_s3_bucket.bucket" @@ -812,7 +812,7 @@ func TestAccAWSS3Bucket_Website_Simple(t *testing.T) { }) } -func TestAccAWSS3Bucket_WebsiteRedirect(t *testing.T) { +func TestAccAWSS3Bucket_Web_redirect(t *testing.T) { bucketName := acctest.RandomWithPrefix("tf-test-bucket") region := testAccGetRegion() resourceName := "aws_s3_bucket.bucket" @@ -857,7 +857,7 @@ func TestAccAWSS3Bucket_WebsiteRedirect(t *testing.T) { }) } -func TestAccAWSS3Bucket_WebsiteRoutingRules(t *testing.T) { +func TestAccAWSS3Bucket_Web_routingRules(t *testing.T) { bucketName := acctest.RandomWithPrefix("tf-test-bucket") region := testAccGetRegion() resourceName := "aws_s3_bucket.bucket" @@ -909,7 +909,7 @@ func TestAccAWSS3Bucket_WebsiteRoutingRules(t *testing.T) { }) } -func TestAccAWSS3Bucket_enableDefaultEncryption_whenTypical(t *testing.T) { +func TestAccAWSS3Bucket_Security_enableDefaultEncryptionWhenTypical(t *testing.T) { bucketName := acctest.RandomWithPrefix("tf-test-bucket") resourceName := "aws_s3_bucket.arbitrary" @@ -940,7 +940,7 @@ func TestAccAWSS3Bucket_enableDefaultEncryption_whenTypical(t *testing.T) { }) } -func TestAccAWSS3Bucket_enableDefaultEncryption_whenAES256IsUsed(t *testing.T) { +func TestAccAWSS3Bucket_Security_enableDefaultEncryptionWhenAES256IsUsed(t *testing.T) { bucketName := acctest.RandomWithPrefix("tf-test-bucket") resourceName := "aws_s3_bucket.arbitrary" @@ -971,7 +971,7 @@ func TestAccAWSS3Bucket_enableDefaultEncryption_whenAES256IsUsed(t *testing.T) { }) } -func TestAccAWSS3Bucket_disableDefaultEncryption_whenDefaultEncryptionIsEnabled(t *testing.T) { +func TestAccAWSS3Bucket_Security_disableDefaultEncryptionWhenDefaultEncryptionIsEnabled(t *testing.T) { bucketName := acctest.RandomWithPrefix("tf-test-bucket") resourceName := "aws_s3_bucket.arbitrary" @@ -1004,7 +1004,7 @@ func TestAccAWSS3Bucket_disableDefaultEncryption_whenDefaultEncryptionIsEnabled( }) } -func TestAccAWSS3Bucket_bucketKeyEnabled(t *testing.T) { +func TestAccAWSS3Bucket_Basic_keyEnabled(t *testing.T) { bucketName := acctest.RandomWithPrefix("tf-test-bucket") resourceName := "aws_s3_bucket.arbitrary" @@ -1036,10 +1036,10 @@ func TestAccAWSS3Bucket_bucketKeyEnabled(t *testing.T) { }) } -// Test TestAccAWSS3Bucket_shouldFailNotFound is designed to fail with a "plan +// Test TestAccAWSS3Bucket_Basic_shouldFailNotFound is designed to fail with a "plan // not empty" error in Terraform, to check against regresssions. // See https://github.com/hashicorp/terraform/pull/2925 -func TestAccAWSS3Bucket_shouldFailNotFound(t *testing.T) { +func TestAccAWSS3Bucket_Basic_shouldFailNotFound(t *testing.T) { bucketName := acctest.RandomWithPrefix("tf-test-bucket") resourceName := "aws_s3_bucket.bucket" @@ -1061,7 +1061,7 @@ func TestAccAWSS3Bucket_shouldFailNotFound(t *testing.T) { }) } -func TestAccAWSS3Bucket_Versioning(t *testing.T) { +func TestAccAWSS3Bucket_Manage_versioning(t *testing.T) { bucketName := acctest.RandomWithPrefix("tf-test-bucket") resourceName := "aws_s3_bucket.bucket" @@ -1102,7 +1102,7 @@ func TestAccAWSS3Bucket_Versioning(t *testing.T) { }) } -func TestAccAWSS3Bucket_Cors_Update(t *testing.T) { +func TestAccAWSS3Bucket_Security_corsUpdate(t *testing.T) { bucketName := acctest.RandomWithPrefix("tf-test-bucket") resourceName := "aws_s3_bucket.bucket" @@ -1187,7 +1187,7 @@ func TestAccAWSS3Bucket_Cors_Update(t *testing.T) { }) } -func TestAccAWSS3Bucket_Cors_Delete(t *testing.T) { +func TestAccAWSS3Bucket_Security_corsDelete(t *testing.T) { bucketName := acctest.RandomWithPrefix("tf-test-bucket") resourceName := "aws_s3_bucket.bucket" @@ -1227,7 +1227,7 @@ func TestAccAWSS3Bucket_Cors_Delete(t *testing.T) { }) } -func TestAccAWSS3Bucket_Cors_EmptyOrigin(t *testing.T) { +func TestAccAWSS3Bucket_Security_corsEmptyOrigin(t *testing.T) { bucketName := acctest.RandomWithPrefix("tf-test-bucket") resourceName := "aws_s3_bucket.bucket" @@ -1265,7 +1265,7 @@ func TestAccAWSS3Bucket_Cors_EmptyOrigin(t *testing.T) { }) } -func TestAccAWSS3Bucket_Logging(t *testing.T) { +func TestAccAWSS3Bucket_Security_logging(t *testing.T) { bucketName := acctest.RandomWithPrefix("tf-test-bucket") resourceName := "aws_s3_bucket.bucket" @@ -1292,7 +1292,7 @@ func TestAccAWSS3Bucket_Logging(t *testing.T) { }) } -func TestAccAWSS3Bucket_LifecycleBasic(t *testing.T) { +func TestAccAWSS3Bucket_Manage_lifecycleBasic(t *testing.T) { bucketName := acctest.RandomWithPrefix("tf-test-bucket") resourceName := "aws_s3_bucket.bucket" @@ -1409,7 +1409,7 @@ func TestAccAWSS3Bucket_LifecycleBasic(t *testing.T) { }) } -func TestAccAWSS3Bucket_LifecycleExpireMarkerOnly(t *testing.T) { +func TestAccAWSS3Bucket_Manage_lifecycleExpireMarkerOnly(t *testing.T) { bucketName := acctest.RandomWithPrefix("tf-test-bucket") resourceName := "aws_s3_bucket.bucket" @@ -1447,7 +1447,7 @@ func TestAccAWSS3Bucket_LifecycleExpireMarkerOnly(t *testing.T) { } // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/11420 -func TestAccAWSS3Bucket_LifecycleRule_Expiration_EmptyConfigurationBlock(t *testing.T) { +func TestAccAWSS3Bucket_Manage_lifecycleRuleExpirationEmptyConfigurationBlock(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_s3_bucket.bucket" @@ -1468,7 +1468,7 @@ func TestAccAWSS3Bucket_LifecycleRule_Expiration_EmptyConfigurationBlock(t *test } // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/15138 -func TestAccAWSS3Bucket_LifecycleRule_AbortIncompleteMultipartUploadDays_NoExpiration(t *testing.T) { +func TestAccAWSS3Bucket_Manage_lifecycleRuleAbortIncompleteMultipartUploadDaysNoExpiration(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_s3_bucket.bucket" @@ -1494,7 +1494,7 @@ func TestAccAWSS3Bucket_LifecycleRule_AbortIncompleteMultipartUploadDays_NoExpir }) } -func TestAccAWSS3Bucket_Replication(t *testing.T) { +func TestAccAWSS3Bucket_Replication_basic(t *testing.T) { rInt := acctest.RandInt() alternateRegion := testAccGetAlternateRegion() region := testAccGetRegion() @@ -1612,7 +1612,7 @@ func TestAccAWSS3Bucket_Replication(t *testing.T) { }) } -func TestAccAWSS3Bucket_Replication_MultipleDestinations_EmptyFilter(t *testing.T) { +func TestAccAWSS3Bucket_Replication_multipleDestinationsEmptyFilter(t *testing.T) { rInt := acctest.RandInt() alternateRegion := testAccGetAlternateRegion() region := testAccGetRegion() @@ -1679,7 +1679,7 @@ func TestAccAWSS3Bucket_Replication_MultipleDestinations_EmptyFilter(t *testing. }) } -func TestAccAWSS3Bucket_Replication_MultipleDestinations_NonEmptyFilter(t *testing.T) { +func TestAccAWSS3Bucket_Replication_multipleDestinationsNonEmptyFilter(t *testing.T) { rInt := acctest.RandInt() alternateRegion := testAccGetAlternateRegion() region := testAccGetRegion() @@ -1749,7 +1749,7 @@ func TestAccAWSS3Bucket_Replication_MultipleDestinations_NonEmptyFilter(t *testi }) } -func TestAccAWSS3Bucket_Replication_MultipleDestinations_TwoDestination(t *testing.T) { +func TestAccAWSS3Bucket_Replication_twoDestination(t *testing.T) { // This tests 2 destinations since GovCloud and possibly other non-standard partitions allow a max of 2 rInt := acctest.RandInt() alternateRegion := testAccGetAlternateRegion() @@ -1808,7 +1808,7 @@ func TestAccAWSS3Bucket_Replication_MultipleDestinations_TwoDestination(t *testi }) } -func TestAccAWSS3Bucket_ReplicationConfiguration_Rule_Destination_AccessControlTranslation(t *testing.T) { +func TestAccAWSS3Bucket_Replication_configurationRuleDestinationAccessControlTranslation(t *testing.T) { rInt := acctest.RandInt() region := testAccGetRegion() partition := testAccGetPartition() @@ -1901,7 +1901,7 @@ func TestAccAWSS3Bucket_ReplicationConfiguration_Rule_Destination_AccessControlT } // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/12480 -func TestAccAWSS3Bucket_ReplicationConfiguration_Rule_Destination_AddAccessControlTranslation(t *testing.T) { +func TestAccAWSS3Bucket_Replication_configurationRuleDestinationAddAccessControlTranslation(t *testing.T) { rInt := acctest.RandInt() region := testAccGetRegion() partition := testAccGetPartition() @@ -1983,7 +1983,7 @@ func TestAccAWSS3Bucket_ReplicationConfiguration_Rule_Destination_AddAccessContr } // StorageClass issue: https://github.com/hashicorp/terraform/issues/10909 -func TestAccAWSS3Bucket_ReplicationWithoutStorageClass(t *testing.T) { +func TestAccAWSS3Bucket_Replication_withoutStorageClass(t *testing.T) { rInt := acctest.RandInt() alternateRegion := testAccGetAlternateRegion() region := testAccGetRegion() @@ -2019,7 +2019,7 @@ func TestAccAWSS3Bucket_ReplicationWithoutStorageClass(t *testing.T) { }) } -func TestAccAWSS3Bucket_ReplicationExpectVersioningValidationError(t *testing.T) { +func TestAccAWSS3Bucket_Replication_expectVersioningValidationError(t *testing.T) { rInt := acctest.RandInt() // record the initialized providers so that we can use them to check for the instances in each region @@ -2043,7 +2043,7 @@ func TestAccAWSS3Bucket_ReplicationExpectVersioningValidationError(t *testing.T) } // Prefix issue: https://github.com/hashicorp/terraform-provider-aws/issues/6340 -func TestAccAWSS3Bucket_ReplicationWithoutPrefix(t *testing.T) { +func TestAccAWSS3Bucket_Replication_withoutPrefix(t *testing.T) { rInt := acctest.RandInt() alternateRegion := testAccGetAlternateRegion() region := testAccGetRegion() @@ -2079,7 +2079,7 @@ func TestAccAWSS3Bucket_ReplicationWithoutPrefix(t *testing.T) { }) } -func TestAccAWSS3Bucket_ReplicationSchemaV2(t *testing.T) { +func TestAccAWSS3Bucket_Replication_schemaV2(t *testing.T) { rInt := acctest.RandInt() alternateRegion := testAccGetAlternateRegion() region := testAccGetRegion() @@ -2296,7 +2296,7 @@ func TestAccAWSS3Bucket_ReplicationSchemaV2(t *testing.T) { }) } -func TestAccAWSS3Bucket_SameRegionReplicationSchemaV2(t *testing.T) { +func TestAccAWSS3Bucket_Replication_schemaV2SameRegion(t *testing.T) { resourceName := "aws_s3_bucket.bucket" rName := acctest.RandomWithPrefix("tf-acc-test") destinationResourceName := "aws_s3_bucket.destination" @@ -2331,7 +2331,7 @@ func TestAccAWSS3Bucket_SameRegionReplicationSchemaV2(t *testing.T) { }, Priority: aws.Int64(0), DeleteMarkerReplication: &s3.DeleteMarkerReplication{ - Status: aws.String(s3.DeleteMarkerReplicationStatusDisabled), + Status: aws.String(s3.DeleteMarkerReplicationStatusEnabled), }, }, }, @@ -2350,7 +2350,7 @@ func TestAccAWSS3Bucket_SameRegionReplicationSchemaV2(t *testing.T) { }) } -func TestAccAWSS3Bucket_objectLock(t *testing.T) { +func TestAccAWSS3Bucket_Manage_objectLock(t *testing.T) { bucketName := acctest.RandomWithPrefix("tf-test-bucket") resourceName := "aws_s3_bucket.arbitrary" @@ -2390,7 +2390,7 @@ func TestAccAWSS3Bucket_objectLock(t *testing.T) { }) } -func TestAccAWSS3Bucket_forceDestroy(t *testing.T) { +func TestAccAWSS3Bucket_Basic_forceDestroy(t *testing.T) { resourceName := "aws_s3_bucket.bucket" bucketName := acctest.RandomWithPrefix("tf-test-bucket") @@ -2416,7 +2416,7 @@ func TestAccAWSS3Bucket_forceDestroy(t *testing.T) { // While the aws_s3_bucket_object resource automatically cleans the key // to not contain these extra slashes, out-of-band handling and other AWS // services may create keys with extra slashes (empty "directory" prefixes). -func TestAccAWSS3Bucket_forceDestroyWithEmptyPrefixes(t *testing.T) { +func TestAccAWSS3Bucket_Basic_forceDestroyWithEmptyPrefixes(t *testing.T) { resourceName := "aws_s3_bucket.bucket" bucketName := acctest.RandomWithPrefix("tf-test-bucket") @@ -2437,7 +2437,7 @@ func TestAccAWSS3Bucket_forceDestroyWithEmptyPrefixes(t *testing.T) { }) } -func TestAccAWSS3Bucket_forceDestroyWithObjectLockEnabled(t *testing.T) { +func TestAccAWSS3Bucket_Basic_forceDestroyWithObjectLockEnabled(t *testing.T) { resourceName := "aws_s3_bucket.bucket" bucketName := acctest.RandomWithPrefix("tf-test-bucket") @@ -4574,9 +4574,7 @@ resource "aws_s3_bucket" "bucket" { prefix = "testprefix" } - delete_marker_replication { - status = "Enabled" - } + delete_marker_replication_status = "Enabled" destination { bucket = aws_s3_bucket.destination.arn @@ -4617,8 +4615,6 @@ resource "aws_s3_bucket" "bucket" { prefix = "foo" } - delete_marker_replication {} - destination { bucket = aws_s3_bucket.destination.arn storage_class = "STANDARD" @@ -4650,9 +4646,7 @@ resource "aws_s3_bucket" "bucket" { prefix = "foo" } - delete_marker_replication { - status = "Enabled" - } + delete_marker_replication_status = "Enabled" destination { bucket = aws_s3_bucket.destination.arn @@ -4689,10 +4683,6 @@ resource "aws_s3_bucket" "bucket" { } } - delete_marker_replication { - status = "Disabled" - } - destination { bucket = aws_s3_bucket.destination.arn storage_class = "STANDARD" @@ -4731,10 +4721,6 @@ resource "aws_s3_bucket" "bucket" { } } - delete_marker_replication { - status = "Disabled" - } - destination { bucket = aws_s3_bucket.destination.arn storage_class = "STANDARD" @@ -4770,10 +4756,6 @@ resource "aws_s3_bucket" "bucket" { } } - delete_marker_replication { - status = "Disabled" - } - destination { bucket = aws_s3_bucket.destination.arn storage_class = "STANDARD" From fa38dd5bb3dee9919454770284191646af2f75ab Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Mon, 12 Jul 2021 14:42:28 -0400 Subject: [PATCH 328/398] docs/r/s3_bucket: Add delete_marker_replication_status argument --- website/docs/r/s3_bucket.html.markdown | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/website/docs/r/s3_bucket.html.markdown b/website/docs/r/s3_bucket.html.markdown index cf54e1f364c..203237f3a23 100644 --- a/website/docs/r/s3_bucket.html.markdown +++ b/website/docs/r/s3_bucket.html.markdown @@ -429,14 +429,14 @@ The `replication_configuration` object supports the following: The `rules` object supports the following: +* `delete_marker_replication_status` - (Optional) Whether delete markers are replicated. The only valid value is `Enabled`. To disable, omit this argument. This argument is only valid with V2 replication configurations (i.e., when `filter` is used). +* `destination` - (Required) Specifies the destination for the rule (documented below). +* `filter` - (Optional) Filter that identifies subset of objects to which the replication rule applies (documented below). * `id` - (Optional) Unique identifier for the rule. Must be less than or equal to 255 characters in length. +* `prefix` - (Optional) Object keyname prefix identifying one or more objects to which the rule applies. Must be less than or equal to 1024 characters in length. * `priority` - (Optional) The priority associated with the rule. -* `destination` - (Required) Specifies the destination for the rule (documented below). * `source_selection_criteria` - (Optional) Specifies special object selection criteria (documented below). -* `prefix` - (Optional) Object keyname prefix identifying one or more objects to which the rule applies. Must be less than or equal to 1024 characters in length. * `status` - (Required) The status of the rule. Either `Enabled` or `Disabled`. The rule is ignored if status is not Enabled. -* `filter` - (Optional) Filter that identifies subset of objects to which the replication rule applies (documented below). -* `delete_marker_replication` - (Optional) Specifies whether delete markers are replicated (documented below). ~> **NOTE on `prefix` and `filter`:** Amazon S3's latest version of the replication configuration is V2, which includes the `filter` attribute for replication rules. With the `filter` attribute, you can specify object filters based on the object key prefix, tags, or both to scope the objects that the rule applies to. @@ -445,10 +445,6 @@ Replication configuration V1 supports filtering based on only the `prefix` attri * For a specific rule, `prefix` conflicts with `filter` * If any rule has `filter` specified then they all must * `priority` is optional (with a default value of `0`) but must be unique between multiple rules -* If a rule has `filter` then it **must** have a `delete_marker_replication` object -* If a rule has `prefix` then it **must not** have a `delete_marker_replication` object - -~> **NOTE:** Delete markers are always replicated when using `prefix` in a rule and is not configurable. The default behavior when using `filter` can be achieved by providing an empty configuration block `delete_marker_replication {}`. ~> **NOTE:** Replication to multiple destination buckets requires that `priority` is specified in the `rules` object. If the corresponding rule requires no filter, an empty configuration block `filter {}` must be specified. @@ -476,10 +472,6 @@ The `filter` object supports the following: * `tags` - (Optional) A map of tags that identifies subset of objects to which the rule applies. The rule applies only to objects having all the tags in its tagset. -The `delete_marker_replication` object supports the following: - -* `status` - (Optional) The delete marker replication status. Either `Enabled` or `Disabled`. Default `Disabled`. If `tags` is set in the `filter` object, then `status` must be set to `Disabled`. - The `server_side_encryption_configuration` object supports the following: * `rule` - (required) A single object for server-side encryption by default configuration. (documented below) From 9cb7da5ef598f00b46a18e3afe86a051869dd7ff Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Mon, 12 Jul 2021 14:44:12 -0400 Subject: [PATCH 329/398] r/s3_bucket: Update changelog --- .changelog/19323.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changelog/19323.txt b/.changelog/19323.txt index 5dac0534726..a4855edd4c6 100644 --- a/.changelog/19323.txt +++ b/.changelog/19323.txt @@ -1,3 +1,3 @@ ```release-note:enhancement -resource/aws_s3_bucket: Add the delete_marker_replication option to s3 bucket replication_configuration v2 rules +resource/aws_s3_bucket: Add the delete_marker_replication_status argument for V2 replication configurations ``` From 7520f9ab9750bfc3cdb33f8bb20edd29fa368a4f Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Mon, 12 Jul 2021 14:45:28 -0400 Subject: [PATCH 330/398] r/s3_bucket: Remove errant newline --- aws/resource_aws_s3_bucket.go | 1 - 1 file changed, 1 deletion(-) diff --git a/aws/resource_aws_s3_bucket.go b/aws/resource_aws_s3_bucket.go index accb259dd5f..54af265517f 100644 --- a/aws/resource_aws_s3_bucket.go +++ b/aws/resource_aws_s3_bucket.go @@ -2124,7 +2124,6 @@ func resourceAwsS3BucketReplicationConfigurationUpdate(s3conn *s3.S3, d *schema. Status: aws.String(s3.DeleteMarkerReplicationStatusDisabled), } } - } else { // XML schema V1. rcRule.Prefix = aws.String(rr["prefix"].(string)) From af79ce02e08456330cd89bc78340ed64276c7739 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Mon, 12 Jul 2021 14:47:32 -0400 Subject: [PATCH 331/398] r/s3_bucket: Linty McLinterface --- aws/resource_aws_s3_bucket_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_s3_bucket_test.go b/aws/resource_aws_s3_bucket_test.go index 6fa9c61fb78..3897510930c 100644 --- a/aws/resource_aws_s3_bucket_test.go +++ b/aws/resource_aws_s3_bucket_test.go @@ -4646,7 +4646,7 @@ resource "aws_s3_bucket" "bucket" { prefix = "foo" } - delete_marker_replication_status = "Enabled" + delete_marker_replication_status = "Enabled" destination { bucket = aws_s3_bucket.destination.arn From 1aa38493a74bcdf3009957c487b9894e1c3b9cb6 Mon Sep 17 00:00:00 2001 From: Eric Searcy Date: Mon, 12 Jul 2021 12:37:22 -0700 Subject: [PATCH 332/398] Remove ECR docs policy reference to IAM ECR policy is JSON and has an attribute name of "policy" but otherwise has nothing to do with the IAM policies that the second sentence of the attribute description links to. --- website/docs/r/ecr_lifecycle_policy.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/ecr_lifecycle_policy.html.markdown b/website/docs/r/ecr_lifecycle_policy.html.markdown index 35412f0ad81..75da0372254 100644 --- a/website/docs/r/ecr_lifecycle_policy.html.markdown +++ b/website/docs/r/ecr_lifecycle_policy.html.markdown @@ -85,7 +85,7 @@ EOF The following arguments are supported: * `repository` - (Required) Name of the repository to apply the policy. -* `policy` - (Required) The policy document. This is a JSON formatted string. See more details about [Policy Parameters](http://docs.aws.amazon.com/AmazonECR/latest/userguide/LifecyclePolicies.html#lifecycle_policy_parameters) in the official AWS docs. For more information about building IAM policy documents with Terraform, see the [AWS IAM Policy Document Guide](https://learn.hashicorp.com/terraform/aws/iam-policy). +* `policy` - (Required) The policy document. This is a JSON formatted string. See more details about [Policy Parameters](http://docs.aws.amazon.com/AmazonECR/latest/userguide/LifecyclePolicies.html#lifecycle_policy_parameters) in the official AWS docs. ## Attributes Reference From ab6facf62b30c72425453d445844d5e1b296bc23 Mon Sep 17 00:00:00 2001 From: Bartosz Janda Date: Sat, 29 May 2021 18:10:16 +0200 Subject: [PATCH 333/398] Add encrypted SES SMTP password --- .changelog/19579.txt | 3 +++ aws/resource_aws_iam_access_key.go | 24 +++++++++++++---- aws/resource_aws_iam_access_key_test.go | 29 ++++++++++++++++----- website/docs/r/iam_access_key.html.markdown | 3 ++- 4 files changed, 47 insertions(+), 12 deletions(-) create mode 100644 .changelog/19579.txt diff --git a/.changelog/19579.txt b/.changelog/19579.txt new file mode 100644 index 00000000000..909b9e825e0 --- /dev/null +++ b/.changelog/19579.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_iam_access_key: Add encrypted SES SMTP password +``` \ No newline at end of file diff --git a/aws/resource_aws_iam_access_key.go b/aws/resource_aws_iam_access_key.go index 0b396760eb5..560904020ef 100644 --- a/aws/resource_aws_iam_access_key.go +++ b/aws/resource_aws_iam_access_key.go @@ -91,6 +91,10 @@ func resourceAwsIamAccessKey() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "encrypted_ses_smtp_password_v4": { + Type: schema.TypeString, + Computed: true, + }, }, } } @@ -117,6 +121,11 @@ func resourceAwsIamAccessKeyCreate(d *schema.ResourceData, meta interface{}) err return fmt.Errorf("CreateAccessKey response did not contain a Secret Access Key as expected") } + sesSMTPPasswordV4, err := sesSmtpPasswordFromSecretKeySigV4(createResp.AccessKey.SecretAccessKey, meta.(*AWSClient).region) + if err != nil { + return fmt.Errorf("error getting SES SigV4 SMTP Password from Secret Access Key: %s", err) + } + if v, ok := d.GetOk("pgp_key"); ok { pgpKey := v.(string) encryptionKey, err := encryption.RetrieveGPGKey(pgpKey) @@ -130,17 +139,22 @@ func resourceAwsIamAccessKeyCreate(d *schema.ResourceData, meta interface{}) err d.Set("key_fingerprint", fingerprint) d.Set("encrypted_secret", encrypted) + + _, encrypted, err = encryption.EncryptValue(encryptionKey, sesSMTPPasswordV4, "SES SMTP password") + if err != nil { + return err + } + + d.Set("encrypted_ses_smtp_password_v4", encrypted) } else { if err := d.Set("secret", createResp.AccessKey.SecretAccessKey); err != nil { return err } - } - sesSMTPPasswordV4, err := sesSmtpPasswordFromSecretKeySigV4(createResp.AccessKey.SecretAccessKey, meta.(*AWSClient).region) - if err != nil { - return fmt.Errorf("error getting SES SigV4 SMTP Password from Secret Access Key: %s", err) + if err := d.Set("ses_smtp_password_v4", sesSMTPPasswordV4); err != nil { + return err + } } - d.Set("ses_smtp_password_v4", sesSMTPPasswordV4) if v, ok := d.GetOk("status"); ok && v.(string) == iam.StatusTypeInactive { input := &iam.UpdateAccessKeyInput{ diff --git a/aws/resource_aws_iam_access_key_test.go b/aws/resource_aws_iam_access_key_test.go index f22bf919cc2..875ad79a69a 100644 --- a/aws/resource_aws_iam_access_key_test.go +++ b/aws/resource_aws_iam_access_key_test.go @@ -33,13 +33,17 @@ func TestAccAWSAccessKey_basic(t *testing.T) { testAccCheckAWSAccessKeyAttributes(&conf, "Active"), testAccCheckResourceAttrRfc3339("aws_iam_access_key.a_key", "create_date"), resource.TestCheckResourceAttrSet("aws_iam_access_key.a_key", "secret"), + resource.TestCheckNoResourceAttr("aws_iam_access_key.a_key", "encrypted_secret"), + resource.TestCheckNoResourceAttr("aws_iam_access_key.a_key", "key_fingerprint"), + resource.TestCheckResourceAttrSet("aws_iam_access_key.a_key", "ses_smtp_password_v4"), + resource.TestCheckNoResourceAttr("aws_iam_access_key.a_key", "encrypted_ses_smtp_password_v4"), ), }, { ResourceName: "aws_iam_access_key.a_key", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"encrypted_secret", "key_fingerprint", "pgp_key", "secret", "ses_smtp_password_v4"}, + ImportStateVerifyIgnore: []string{"encrypted_secret", "key_fingerprint", "pgp_key", "secret", "ses_smtp_password_v4", "encrypted_ses_smtp_password_v4"}, }, }, }) @@ -67,13 +71,17 @@ func TestAccAWSAccessKey_encrypted(t *testing.T) { "aws_iam_access_key.a_key", "encrypted_secret"), resource.TestCheckResourceAttrSet( "aws_iam_access_key.a_key", "key_fingerprint"), + resource.TestCheckNoResourceAttr( + "aws_iam_access_key.a_key", "ses_smtp_password_v4"), + resource.TestCheckResourceAttrSet( + "aws_iam_access_key.a_key", "encrypted_ses_smtp_password_v4"), ), }, { ResourceName: "aws_iam_access_key.a_key", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"encrypted_secret", "key_fingerprint", "pgp_key", "secret", "ses_smtp_password_v4"}, + ImportStateVerifyIgnore: []string{"encrypted_secret", "key_fingerprint", "pgp_key", "secret", "ses_smtp_password_v4", "encrypted_ses_smtp_password_v4"}, }, }, }) @@ -100,7 +108,7 @@ func TestAccAWSAccessKey_Status(t *testing.T) { ResourceName: "aws_iam_access_key.a_key", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"encrypted_secret", "key_fingerprint", "pgp_key", "secret", "ses_smtp_password_v4"}, + ImportStateVerifyIgnore: []string{"encrypted_secret", "key_fingerprint", "pgp_key", "secret", "ses_smtp_password_v4", "encrypted_ses_smtp_password_v4"}, }, { Config: testAccAWSAccessKeyConfig_Status(rName, iam.StatusTypeActive), @@ -205,14 +213,23 @@ func testDecryptSecretKeyAndTest(nAccessKey, key string) resource.TestCheckFunc return fmt.Errorf("Not found: %s", nAccessKey) } - password, ok := keyResource.Primary.Attributes["encrypted_secret"] + secret, ok := keyResource.Primary.Attributes["encrypted_secret"] + if !ok { + return errors.New("No secret in state") + } + + password, ok := keyResource.Primary.Attributes["encrypted_ses_smtp_password_v4"] if !ok { return errors.New("No password in state") } - // We can't verify that the decrypted password is correct, because we don't + // We can't verify that the decrypted secret or password is correct, because we don't // have it. We can verify that decrypting it does not error - _, err := pgpkeys.DecryptBytes(password, key) + _, err := pgpkeys.DecryptBytes(secret, key) + if err != nil { + return fmt.Errorf("Error decrypting secret: %s", err) + } + _, err = pgpkeys.DecryptBytes(password, key) if err != nil { return fmt.Errorf("Error decrypting password: %s", err) } diff --git a/website/docs/r/iam_access_key.html.markdown b/website/docs/r/iam_access_key.html.markdown index 221d074640f..42da5509d17 100644 --- a/website/docs/r/iam_access_key.html.markdown +++ b/website/docs/r/iam_access_key.html.markdown @@ -85,6 +85,7 @@ In addition to all arguments above, the following attributes are exported: * `secret` - The secret access key. This attribute is not available for imported resources. Note that this will be written to the state file. If you use this, please protect your backend state file judiciously. Alternatively, you may supply a `pgp_key` instead, which will prevent the secret from being stored in plaintext, at the cost of preventing the use of the secret key in automation. * `encrypted_secret` - The encrypted secret, base64 encoded, if `pgp_key` was specified. This attribute is not available for imported resources. The encrypted secret may be decrypted using the command line, for example: `terraform output -raw encrypted_secret | base64 --decode | keybase pgp decrypt`. * `ses_smtp_password_v4` - The secret access key converted into an SES SMTP password by applying [AWS's documented Sigv4 conversion algorithm](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/smtp-credentials.html#smtp-credentials-convert). This attribute is not available for imported resources. As SigV4 is region specific, valid Provider regions are `ap-south-1`, `ap-southeast-2`, `eu-central-1`, `eu-west-1`, `us-east-1` and `us-west-2`. See current [AWS SES regions](https://docs.aws.amazon.com/general/latest/gr/rande.html#ses_region). +* `encrypted_ses_smtp_password_v4` - The encrypted SES SMTP password, base64 encoded, if `pgp_key` was specified. This attribute is not available for imported resources. The encrypted password may be decrypted using the command line, for example: `terraform output -raw encrypted_ses_smtp_password_v4 | base64 --decode | keybase pgp decrypt`. ## Import @@ -94,4 +95,4 @@ IAM Access Keys can be imported using the identifier, e.g. $ terraform import aws_iam_access_key.example AKIA1234567890 ``` -Resource attributes such as `encrypted_secret`, `key_fingerprint`, `pgp_key`, `secret`, and `ses_smtp_password_v4` are not available for imported resources as this information cannot be read from the IAM API. +Resource attributes such as `encrypted_secret`, `key_fingerprint`, `pgp_key`, `secret`, `ses_smtp_password_v4`, and `encrypted_ses_smtp_password_v4` are not available for imported resources as this information cannot be read from the IAM API. From 4dbfa0d54a768f5167aa92e682d55a1a73870aa4 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Mon, 12 Jul 2021 16:47:19 -0400 Subject: [PATCH 334/398] r/iam_access_key: Clean up arg order --- aws/resource_aws_iam_access_key.go | 51 ++++++++++++++---------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/aws/resource_aws_iam_access_key.go b/aws/resource_aws_iam_access_key.go index 560904020ef..98623dfbc9c 100644 --- a/aws/resource_aws_iam_access_key.go +++ b/aws/resource_aws_iam_access_key.go @@ -50,19 +50,26 @@ func resourceAwsIamAccessKey() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "user": { + "create_date": { Type: schema.TypeString, - Required: true, - ForceNew: true, + Computed: true, }, - "status": { + "encrypted_secret": { + Type: schema.TypeString, + Computed: true, + }, + "encrypted_ses_smtp_password_v4": { Type: schema.TypeString, + Computed: true, + }, + "key_fingerprint": { + Type: schema.TypeString, + Computed: true, + }, + "pgp_key": { + Type: schema.TypeString, + ForceNew: true, Optional: true, - Default: "Active", - ValidateFunc: validation.StringInSlice([]string{ - iam.StatusTypeActive, - iam.StatusTypeInactive, - }, false), }, "secret": { Type: schema.TypeString, @@ -74,26 +81,16 @@ func resourceAwsIamAccessKey() *schema.Resource { Computed: true, Sensitive: true, }, - "pgp_key": { - Type: schema.TypeString, - ForceNew: true, - Optional: true, - }, - "create_date": { - Type: schema.TypeString, - Computed: true, - }, - "encrypted_secret": { - Type: schema.TypeString, - Computed: true, - }, - "key_fingerprint": { - Type: schema.TypeString, - Computed: true, + "status": { + Type: schema.TypeString, + Optional: true, + Default: iam.StatusTypeActive, + ValidateFunc: validation.StringInSlice(iam.StatusType_Values(), false), }, - "encrypted_ses_smtp_password_v4": { + "user": { Type: schema.TypeString, - Computed: true, + Required: true, + ForceNew: true, }, }, } From 5f898383f470dfa85d72d4049cb44fbcf14a57c3 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Mon, 12 Jul 2021 16:48:08 -0400 Subject: [PATCH 335/398] tests/r/iam_access_key: Standardize tests --- aws/resource_aws_iam_access_key_test.go | 88 ++++++++++++------------- 1 file changed, 43 insertions(+), 45 deletions(-) diff --git a/aws/resource_aws_iam_access_key_test.go b/aws/resource_aws_iam_access_key_test.go index 875ad79a69a..54e5120163e 100644 --- a/aws/resource_aws_iam_access_key_test.go +++ b/aws/resource_aws_iam_access_key_test.go @@ -18,7 +18,8 @@ import ( func TestAccAWSAccessKey_basic(t *testing.T) { var conf iam.AccessKeyMetadata - rName := fmt.Sprintf("test-user-%d", acctest.RandInt()) + resourceName := "aws_iam_access_key.test" + rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -29,18 +30,18 @@ func TestAccAWSAccessKey_basic(t *testing.T) { { Config: testAccAWSAccessKeyConfig(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAccessKeyExists("aws_iam_access_key.a_key", &conf), + testAccCheckAWSAccessKeyExists(resourceName, &conf), testAccCheckAWSAccessKeyAttributes(&conf, "Active"), - testAccCheckResourceAttrRfc3339("aws_iam_access_key.a_key", "create_date"), - resource.TestCheckResourceAttrSet("aws_iam_access_key.a_key", "secret"), - resource.TestCheckNoResourceAttr("aws_iam_access_key.a_key", "encrypted_secret"), - resource.TestCheckNoResourceAttr("aws_iam_access_key.a_key", "key_fingerprint"), - resource.TestCheckResourceAttrSet("aws_iam_access_key.a_key", "ses_smtp_password_v4"), - resource.TestCheckNoResourceAttr("aws_iam_access_key.a_key", "encrypted_ses_smtp_password_v4"), + testAccCheckResourceAttrRfc3339(resourceName, "create_date"), + resource.TestCheckResourceAttrSet(resourceName, "secret"), + resource.TestCheckNoResourceAttr(resourceName, "encrypted_secret"), + resource.TestCheckNoResourceAttr(resourceName, "key_fingerprint"), + resource.TestCheckResourceAttrSet(resourceName, "ses_smtp_password_v4"), + resource.TestCheckNoResourceAttr(resourceName, "encrypted_ses_smtp_password_v4"), ), }, { - ResourceName: "aws_iam_access_key.a_key", + ResourceName: resourceName, ImportState: true, ImportStateVerify: true, ImportStateVerifyIgnore: []string{"encrypted_secret", "key_fingerprint", "pgp_key", "secret", "ses_smtp_password_v4", "encrypted_ses_smtp_password_v4"}, @@ -51,7 +52,8 @@ func TestAccAWSAccessKey_basic(t *testing.T) { func TestAccAWSAccessKey_encrypted(t *testing.T) { var conf iam.AccessKeyMetadata - rName := fmt.Sprintf("test-user-%d", acctest.RandInt()) + resourceName := "aws_iam_access_key.test" + rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -62,23 +64,18 @@ func TestAccAWSAccessKey_encrypted(t *testing.T) { { Config: testAccAWSAccessKeyConfig_encrypted(rName, testPubKey1), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAccessKeyExists("aws_iam_access_key.a_key", &conf), + testAccCheckAWSAccessKeyExists(resourceName, &conf), testAccCheckAWSAccessKeyAttributes(&conf, "Active"), - testDecryptSecretKeyAndTest("aws_iam_access_key.a_key", testPrivKey1), - resource.TestCheckNoResourceAttr( - "aws_iam_access_key.a_key", "secret"), - resource.TestCheckResourceAttrSet( - "aws_iam_access_key.a_key", "encrypted_secret"), - resource.TestCheckResourceAttrSet( - "aws_iam_access_key.a_key", "key_fingerprint"), - resource.TestCheckNoResourceAttr( - "aws_iam_access_key.a_key", "ses_smtp_password_v4"), - resource.TestCheckResourceAttrSet( - "aws_iam_access_key.a_key", "encrypted_ses_smtp_password_v4"), + testDecryptSecretKeyAndTest(resourceName, testPrivKey1), + resource.TestCheckNoResourceAttr(resourceName, "secret"), + resource.TestCheckResourceAttrSet(resourceName, "encrypted_secret"), + resource.TestCheckResourceAttrSet(resourceName, "key_fingerprint"), + resource.TestCheckNoResourceAttr(resourceName, "ses_smtp_password_v4"), + resource.TestCheckResourceAttrSet(resourceName, "encrypted_ses_smtp_password_v4"), ), }, { - ResourceName: "aws_iam_access_key.a_key", + ResourceName: resourceName, ImportState: true, ImportStateVerify: true, ImportStateVerifyIgnore: []string{"encrypted_secret", "key_fingerprint", "pgp_key", "secret", "ses_smtp_password_v4", "encrypted_ses_smtp_password_v4"}, @@ -87,9 +84,10 @@ func TestAccAWSAccessKey_encrypted(t *testing.T) { }) } -func TestAccAWSAccessKey_Status(t *testing.T) { +func TestAccAWSAccessKey_status(t *testing.T) { var conf iam.AccessKeyMetadata - rName := fmt.Sprintf("test-user-%d", acctest.RandInt()) + resourceName := "aws_iam_access_key.test" + rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -100,12 +98,12 @@ func TestAccAWSAccessKey_Status(t *testing.T) { { Config: testAccAWSAccessKeyConfig_Status(rName, iam.StatusTypeInactive), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAccessKeyExists("aws_iam_access_key.a_key", &conf), - resource.TestCheckResourceAttr("aws_iam_access_key.a_key", "status", iam.StatusTypeInactive), + testAccCheckAWSAccessKeyExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "status", iam.StatusTypeInactive), ), }, { - ResourceName: "aws_iam_access_key.a_key", + ResourceName: resourceName, ImportState: true, ImportStateVerify: true, ImportStateVerifyIgnore: []string{"encrypted_secret", "key_fingerprint", "pgp_key", "secret", "ses_smtp_password_v4", "encrypted_ses_smtp_password_v4"}, @@ -113,15 +111,15 @@ func TestAccAWSAccessKey_Status(t *testing.T) { { Config: testAccAWSAccessKeyConfig_Status(rName, iam.StatusTypeActive), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAccessKeyExists("aws_iam_access_key.a_key", &conf), - resource.TestCheckResourceAttr("aws_iam_access_key.a_key", "status", iam.StatusTypeActive), + testAccCheckAWSAccessKeyExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "status", iam.StatusTypeActive), ), }, { Config: testAccAWSAccessKeyConfig_Status(rName, iam.StatusTypeInactive), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAccessKeyExists("aws_iam_access_key.a_key", &conf), - resource.TestCheckResourceAttr("aws_iam_access_key.a_key", "status", iam.StatusTypeInactive), + testAccCheckAWSAccessKeyExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "status", iam.StatusTypeInactive), ), }, }, @@ -194,7 +192,7 @@ func testAccCheckAWSAccessKeyExists(n string, res *iam.AccessKeyMetadata) resour func testAccCheckAWSAccessKeyAttributes(accessKeyMetadata *iam.AccessKeyMetadata, status string) resource.TestCheckFunc { return func(s *terraform.State) error { - if !strings.Contains(*accessKeyMetadata.UserName, "test-user") { + if !strings.Contains(*accessKeyMetadata.UserName, "tf-acc-test") { return fmt.Errorf("Bad username: %s", *accessKeyMetadata.UserName) } @@ -240,27 +238,27 @@ func testDecryptSecretKeyAndTest(nAccessKey, key string) resource.TestCheckFunc func testAccAWSAccessKeyConfig(rName string) string { return fmt.Sprintf(` -resource "aws_iam_user" "a_user" { - name = "%s" +resource "aws_iam_user" "test" { + name = %[1]q } -resource "aws_iam_access_key" "a_key" { - user = aws_iam_user.a_user.name +resource "aws_iam_access_key" "test" { + user = aws_iam_user.test.name } `, rName) } func testAccAWSAccessKeyConfig_encrypted(rName, key string) string { return fmt.Sprintf(` -resource "aws_iam_user" "a_user" { - name = "%s" +resource "aws_iam_user" "test" { + name = %[1]q } -resource "aws_iam_access_key" "a_key" { - user = aws_iam_user.a_user.name +resource "aws_iam_access_key" "test" { + user = aws_iam_user.test.name pgp_key = < Date: Mon, 12 Jul 2021 16:48:38 -0400 Subject: [PATCH 336/398] docs/r/iam_access_key: Clean up docs --- website/docs/r/iam_access_key.html.markdown | 22 +++++++++------------ 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/website/docs/r/iam_access_key.html.markdown b/website/docs/r/iam_access_key.html.markdown index 42da5509d17..05c10cd618f 100644 --- a/website/docs/r/iam_access_key.html.markdown +++ b/website/docs/r/iam_access_key.html.markdown @@ -67,25 +67,21 @@ output "aws_iam_smtp_password_v4" { The following arguments are supported: -* `user` - (Required) The IAM user to associate with this access key. -* `pgp_key` - (Optional) Either a base-64 encoded PGP public key, or a - keybase username in the form `keybase:some_person_that_exists`, for use - in the `encrypted_secret` output attribute. -* `status` - (Optional) The access key status to apply. Defaults to `Active`. -Valid values are `Active` and `Inactive`. +* `pgp_key` - (Optional) Either a base-64 encoded PGP public key, or a keybase username in the form `keybase:some_person_that_exists`, for use in the `encrypted_secret` output attribute. +* `status` - (Optional) Access key status to apply. Defaults to `Active`. Valid values are `Active` and `Inactive`. +* `user` - (Required) IAM user to associate with this access key. ## Attributes Reference In addition to all arguments above, the following attributes are exported: * `create_date` - Date and time in [RFC3339 format](https://tools.ietf.org/html/rfc3339#section-5.8) that the access key was created. -* `id` - The access key ID. -* `user` - The IAM user associated with this access key. -* `key_fingerprint` - The fingerprint of the PGP key used to encrypt the secret. This attribute is not available for imported resources. -* `secret` - The secret access key. This attribute is not available for imported resources. Note that this will be written to the state file. If you use this, please protect your backend state file judiciously. Alternatively, you may supply a `pgp_key` instead, which will prevent the secret from being stored in plaintext, at the cost of preventing the use of the secret key in automation. -* `encrypted_secret` - The encrypted secret, base64 encoded, if `pgp_key` was specified. This attribute is not available for imported resources. The encrypted secret may be decrypted using the command line, for example: `terraform output -raw encrypted_secret | base64 --decode | keybase pgp decrypt`. -* `ses_smtp_password_v4` - The secret access key converted into an SES SMTP password by applying [AWS's documented Sigv4 conversion algorithm](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/smtp-credentials.html#smtp-credentials-convert). This attribute is not available for imported resources. As SigV4 is region specific, valid Provider regions are `ap-south-1`, `ap-southeast-2`, `eu-central-1`, `eu-west-1`, `us-east-1` and `us-west-2`. See current [AWS SES regions](https://docs.aws.amazon.com/general/latest/gr/rande.html#ses_region). -* `encrypted_ses_smtp_password_v4` - The encrypted SES SMTP password, base64 encoded, if `pgp_key` was specified. This attribute is not available for imported resources. The encrypted password may be decrypted using the command line, for example: `terraform output -raw encrypted_ses_smtp_password_v4 | base64 --decode | keybase pgp decrypt`. +* `encrypted_secret` - Encrypted secret, base64 encoded, if `pgp_key` was specified. This attribute is not available for imported resources. The encrypted secret may be decrypted using the command line, for example: `terraform output -raw encrypted_secret | base64 --decode | keybase pgp decrypt`. +* `encrypted_ses_smtp_password_v4` - Encrypted SES SMTP password, base64 encoded, if `pgp_key` was specified. This attribute is not available for imported resources. The encrypted password may be decrypted using the command line, for example: `terraform output -raw encrypted_ses_smtp_password_v4 | base64 --decode | keybase pgp decrypt`. +* `id` - Access key ID. +* `key_fingerprint` - Fingerprint of the PGP key used to encrypt the secret. This attribute is not available for imported resources. +* `secret` - Secret access key. This attribute is not available for imported resources. Note that this will be written to the state file. If you use this, please protect your backend state file judiciously. Alternatively, you may supply a `pgp_key` instead, which will prevent the secret from being stored in plaintext, at the cost of preventing the use of the secret key in automation. +* `ses_smtp_password_v4` - Secret access key converted into an SES SMTP password by applying [AWS's documented Sigv4 conversion algorithm](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/smtp-credentials.html#smtp-credentials-convert). This attribute is not available for imported resources. As SigV4 is region specific, valid Provider regions are `ap-south-1`, `ap-southeast-2`, `eu-central-1`, `eu-west-1`, `us-east-1` and `us-west-2`. See current [AWS SES regions](https://docs.aws.amazon.com/general/latest/gr/rande.html#ses_region). ## Import From bf37d1478dbbb29bd93a71926993db09fa294a42 Mon Sep 17 00:00:00 2001 From: Angel Alonso Date: Mon, 17 May 2021 19:28:26 +0200 Subject: [PATCH 337/398] Implemented Scope-down statements on WAFv2 Web ACL Managed Rules --- aws/resource_aws_wafv2_web_acl.go | 21 ++++++++++---- aws/resource_aws_wafv2_web_acl_test.go | 32 +++++++++++++++------- website/docs/r/wafv2_web_acl.html.markdown | 6 ++++ 3 files changed, 43 insertions(+), 16 deletions(-) diff --git a/aws/resource_aws_wafv2_web_acl.go b/aws/resource_aws_wafv2_web_acl.go index ee635a360c9..7fc61107382 100644 --- a/aws/resource_aws_wafv2_web_acl.go +++ b/aws/resource_aws_wafv2_web_acl.go @@ -359,7 +359,7 @@ func wafv2WebACLRootStatementSchema(level int) *schema.Schema { "byte_match_statement": wafv2ByteMatchStatementSchema(), "geo_match_statement": wafv2GeoMatchStatementSchema(), "ip_set_reference_statement": wafv2IpSetReferenceStatementSchema(), - "managed_rule_group_statement": wafv2ManagedRuleGroupStatementSchema(), + "managed_rule_group_statement": wafv2ManagedRuleGroupStatementSchema(level), "not_statement": wafv2StatementSchema(level), "or_statement": wafv2StatementSchema(level), "rate_based_statement": wafv2RateBasedStatementSchema(level), @@ -373,7 +373,7 @@ func wafv2WebACLRootStatementSchema(level int) *schema.Schema { } } -func wafv2ManagedRuleGroupStatementSchema() *schema.Schema { +func wafv2ManagedRuleGroupStatementSchema(level int) *schema.Schema { return &schema.Schema{ Type: schema.TypeList, Optional: true, @@ -391,6 +391,7 @@ func wafv2ManagedRuleGroupStatementSchema() *schema.Schema { Required: true, ValidateFunc: validation.StringLenBetween(1, 128), }, + "scope_down_statement": wafv2ScopeDownStatementSchema(level - 1), }, }, } @@ -626,11 +627,18 @@ func expandWafv2ManagedRuleGroupStatement(l []interface{}) *wafv2.ManagedRuleGro } m := l[0].(map[string]interface{}) - return &wafv2.ManagedRuleGroupStatement{ + r := &wafv2.ManagedRuleGroupStatement{ ExcludedRules: expandWafv2ExcludedRules(m["excluded_rule"].([]interface{})), Name: aws.String(m["name"].(string)), VendorName: aws.String(m["vendor_name"].(string)), } + + s := m["scope_down_statement"].([]interface{}) + if len(s) > 0 && s[0] != nil { + r.ScopeDownStatement = expandWafv2Statement(s[0].(map[string]interface{})) + } + + return r } func expandWafv2RateBasedStatement(l []interface{}) *wafv2.RateBasedStatement { @@ -824,9 +832,10 @@ func flattenWafv2ManagedRuleGroupStatement(r *wafv2.ManagedRuleGroupStatement) i } m := map[string]interface{}{ - "excluded_rule": flattenWafv2ExcludedRules(r.ExcludedRules), - "name": aws.StringValue(r.Name), - "vendor_name": aws.StringValue(r.VendorName), + "excluded_rule": flattenWafv2ExcludedRules(r.ExcludedRules), + "name": aws.StringValue(r.Name), + "vendor_name": aws.StringValue(r.VendorName), + "scope_down_statement": nil, } return []interface{}{m} diff --git a/aws/resource_aws_wafv2_web_acl_test.go b/aws/resource_aws_wafv2_web_acl_test.go index aa59aa5e35e..84ba4161547 100644 --- a/aws/resource_aws_wafv2_web_acl_test.go +++ b/aws/resource_aws_wafv2_web_acl_test.go @@ -566,10 +566,11 @@ func TestAccAwsWafv2WebACL_ManagedRuleGroupStatement(t *testing.T) { "override_action.0.count.#": "0", "override_action.0.none.#": "1", "statement.#": "1", - "statement.0.managed_rule_group_statement.#": "1", - "statement.0.managed_rule_group_statement.0.name": "AWSManagedRulesCommonRuleSet", - "statement.0.managed_rule_group_statement.0.vendor_name": "AWS", - "statement.0.managed_rule_group_statement.0.excluded_rule.#": "0", + "statement.0.managed_rule_group_statement.#": "1", + "statement.0.managed_rule_group_statement.0.name": "AWSManagedRulesCommonRuleSet", + "statement.0.managed_rule_group_statement.0.vendor_name": "AWS", + "statement.0.managed_rule_group_statement.0.excluded_rule.#": "0", + "statement.0.managed_rule_group_statement.0.scope_down_statement.#": "0", }), ), }, @@ -587,12 +588,17 @@ func TestAccAwsWafv2WebACL_ManagedRuleGroupStatement(t *testing.T) { "override_action.0.count.#": "1", "override_action.0.none.#": "0", "statement.#": "1", - "statement.0.managed_rule_group_statement.#": "1", - "statement.0.managed_rule_group_statement.0.name": "AWSManagedRulesCommonRuleSet", - "statement.0.managed_rule_group_statement.0.vendor_name": "AWS", - "statement.0.managed_rule_group_statement.0.excluded_rule.#": "2", - "statement.0.managed_rule_group_statement.0.excluded_rule.0.name": "SizeRestrictions_QUERYSTRING", - "statement.0.managed_rule_group_statement.0.excluded_rule.1.name": "NoUserAgent_HEADER", + "statement.0.managed_rule_group_statement.#": "1", + "statement.0.managed_rule_group_statement.0.name": "AWSManagedRulesCommonRuleSet", + "statement.0.managed_rule_group_statement.0.vendor_name": "AWS", + "statement.0.managed_rule_group_statement.0.excluded_rule.#": "2", + "statement.0.managed_rule_group_statement.0.excluded_rule.0.name": "SizeRestrictions_QUERYSTRING", + "statement.0.managed_rule_group_statement.0.excluded_rule.1.name": "NoUserAgent_HEADER", + "statement.0.managed_rule_group_statement.0.scope_down_statement.#": "1", + "statement.0.managed_rule_group_statement.0.scope_down_statement.0.geo_match_statement.#": "1", + "statement.0.managed_rule_group_statement.0.scope_down_statement.0.geo_match_statement.0.country_codes.#": "2", + "statement.0.managed_rule_group_statement.0.scope_down_statement.0.geo_match_statement.0.country_codes.0": "US", + "statement.0.managed_rule_group_statement.0.scope_down_statement.0.geo_match_statement.0.country_codes.1": "NL", }), ), }, @@ -2115,6 +2121,12 @@ resource "aws_wafv2_web_acl" "test" { excluded_rule { name = "NoUserAgent_HEADER" } + + scope_down_statement { + geo_match_statement { + country_codes = ["US", "NL"] + } + } } } diff --git a/website/docs/r/wafv2_web_acl.html.markdown b/website/docs/r/wafv2_web_acl.html.markdown index 14640f4fc27..d7917c0c7d3 100644 --- a/website/docs/r/wafv2_web_acl.html.markdown +++ b/website/docs/r/wafv2_web_acl.html.markdown @@ -47,6 +47,12 @@ resource "aws_wafv2_web_acl" "example" { excluded_rule { name = "NoUserAgent_HEADER" } + + scope_down_statement { + geo_match_statement { + country_codes = ["US", "NL"] + } + } } } From 65b43e6dbc1fb65d98da87a32fdd583c8e44b25b Mon Sep 17 00:00:00 2001 From: Angel Alonso Date: Mon, 17 May 2021 19:48:02 +0200 Subject: [PATCH 338/398] Added changelog --- .changelog/19407.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/19407.txt diff --git a/.changelog/19407.txt b/.changelog/19407.txt new file mode 100644 index 00000000000..47e0a8ea76c --- /dev/null +++ b/.changelog/19407.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_wafv2_web_acl: Support `scope_down_statement` on `managed_rule_group_statement` +``` \ No newline at end of file From 3bbbf300dc7d7c1d3bac5cc8a66c3d00d349c581 Mon Sep 17 00:00:00 2001 From: Angel Alonso Date: Fri, 18 Jun 2021 10:45:56 +0200 Subject: [PATCH 339/398] Added scope_down_statement to flattened managed_rule_group_statement Co-authored-by: Jack Shubatt --- aws/resource_aws_wafv2_web_acl.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aws/resource_aws_wafv2_web_acl.go b/aws/resource_aws_wafv2_web_acl.go index 7fc61107382..18c71038b57 100644 --- a/aws/resource_aws_wafv2_web_acl.go +++ b/aws/resource_aws_wafv2_web_acl.go @@ -838,6 +838,9 @@ func flattenWafv2ManagedRuleGroupStatement(r *wafv2.ManagedRuleGroupStatement) i "scope_down_statement": nil, } +if r.ScopeDownStatement != nil { + m["scope_down_statement"] = []interface{}{flattenWafv2Statement(r.ScopeDownStatement)} +} return []interface{}{m} } From 1768615f53692e3fd9d222715103d2155bd0e5be Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Wed, 10 Feb 2021 12:16:11 -0800 Subject: [PATCH 340/398] Updates finder.SecurityGroupByID() to return resource.NotFoundError --- aws/internal/service/ec2/finder/finder.go | 17 ++++++++++++++--- aws/internal/service/ec2/waiter/status.go | 7 +------ aws/resource_aws_default_security_group.go | 17 +++++++++-------- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/aws/internal/service/ec2/finder/finder.go b/aws/internal/service/ec2/finder/finder.go index 81d99aff35b..87b8987c635 100644 --- a/aws/internal/service/ec2/finder/finder.go +++ b/aws/internal/service/ec2/finder/finder.go @@ -408,17 +408,28 @@ func RouteByPrefixListIDDestination(conn *ec2.EC2, routeTableID, prefixListID st } // SecurityGroupByID looks up a security group by ID. When not found, returns nil and potentially an API error. +// SecurityGroupByID looks up a security group by ID. Returns a resource.NotFoundError if not found. func SecurityGroupByID(conn *ec2.EC2, id string) (*ec2.SecurityGroup, error) { - req := &ec2.DescribeSecurityGroupsInput{ + input := &ec2.DescribeSecurityGroupsInput{ GroupIds: aws.StringSlice([]string{id}), } - result, err := conn.DescribeSecurityGroups(req) + result, err := conn.DescribeSecurityGroups(input) + if tfawserr.ErrCodeEquals(err, tfec2.InvalidSecurityGroupIDNotFound) || + tfawserr.ErrCodeEquals(err, tfec2.InvalidGroupNotFound) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } if err != nil { return nil, err } if result == nil || len(result.SecurityGroups) == 0 || result.SecurityGroups[0] == nil { - return nil, nil + return nil, &resource.NotFoundError{ + Message: "Empty result", + LastRequest: input, + } } return result.SecurityGroups[0], nil diff --git a/aws/internal/service/ec2/waiter/status.go b/aws/internal/service/ec2/waiter/status.go index 68265ef0f40..6547d450c0a 100644 --- a/aws/internal/service/ec2/waiter/status.go +++ b/aws/internal/service/ec2/waiter/status.go @@ -314,18 +314,13 @@ const ( func SecurityGroupStatus(conn *ec2.EC2, id string) resource.StateRefreshFunc { return func() (interface{}, string, error) { group, err := finder.SecurityGroupByID(conn, id) - if tfawserr.ErrCodeEquals(err, tfec2.InvalidSecurityGroupIDNotFound) || - tfawserr.ErrCodeEquals(err, tfec2.InvalidGroupNotFound) { + if tfresource.NotFound(err) { return nil, SecurityGroupStatusNotFound, nil } if err != nil { return nil, SecurityGroupStatusUnknown, err } - if group == nil { - return nil, SecurityGroupStatusNotFound, nil - } - return group, SecurityGroupStatusCreated, nil } } diff --git a/aws/resource_aws_default_security_group.go b/aws/resource_aws_default_security_group.go index e9cc15c11a1..97be5dc68d3 100644 --- a/aws/resource_aws_default_security_group.go +++ b/aws/resource_aws_default_security_group.go @@ -14,6 +14,7 @@ import ( "github.com/terraform-providers/terraform-provider-aws/aws/internal/hashcode" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) const DefaultSecurityGroupName = "default" @@ -267,14 +268,14 @@ func resourceAwsDefaultSecurityGroupRead(d *schema.ResourceData, meta interface{ ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig group, err := finder.SecurityGroupByID(conn, d.Id()) - if err != nil { - return err - } - if group == nil { + if tfresource.NotFound(err) { log.Printf("[WARN] Security group (%s) not found, removing from state", d.Id()) d.SetId("") return nil } + if err != nil { + return err + } remoteIngressRules := resourceAwsSecurityGroupIPPermGather(d.Id(), group.IpPermissions, group.OwnerId) remoteEgressRules := resourceAwsSecurityGroupIPPermGather(d.Id(), group.IpPermissionsEgress, group.OwnerId) @@ -327,14 +328,14 @@ func resourceAwsDefaultSecurityGroupUpdate(d *schema.ResourceData, meta interfac conn := meta.(*AWSClient).ec2conn group, err := finder.SecurityGroupByID(conn, d.Id()) - if err != nil { - return err - } - if group == nil { + if tfresource.NotFound(err) { log.Printf("[WARN] Security group (%s) not found, removing from state", d.Id()) d.SetId("") return nil } + if err != nil { + return err + } err = resourceAwsSecurityGroupUpdateRules(d, "ingress", meta, group) if err != nil { From c7a95dd4895eff02418244def810dbd7b688f783 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Thu, 11 Feb 2021 00:20:28 -0800 Subject: [PATCH 341/398] Adds EmptyResult and TooManyResults errors compatible with NotFoundError and expands use of Security Group finder --- aws/data_source_aws_security_group.go | 19 +-- aws/internal/service/ec2/finder/finder.go | 27 ++- aws/internal/tfresource/not_found_error.go | 79 +++++++++ .../tfresource/not_found_error_test.go | 156 ++++++++++++++++++ aws/resource_aws_elb.go | 57 +------ 5 files changed, 271 insertions(+), 67 deletions(-) create mode 100644 aws/internal/tfresource/not_found_error.go create mode 100644 aws/internal/tfresource/not_found_error_test.go diff --git a/aws/data_source_aws_security_group.go b/aws/data_source_aws_security_group.go index bc9e346a985..1bcb54a8980 100644 --- a/aws/data_source_aws_security_group.go +++ b/aws/data_source_aws_security_group.go @@ -1,14 +1,16 @@ package aws import ( + "errors" "fmt" - "log" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/ec2" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func dataSourceAwsSecurityGroup() *schema.Resource { @@ -76,19 +78,16 @@ func dataSourceAwsSecurityGroupRead(d *schema.ResourceData, meta interface{}) er req.Filters = nil } - log.Printf("[DEBUG] Reading Security Group: %s", req) - resp, err := conn.DescribeSecurityGroups(req) - if err != nil { - return err - } - if resp == nil || len(resp.SecurityGroups) == 0 { + sg, err := finder.SecurityGroup(conn, req) + if errors.Is(err, tfresource.ErrEmptyResult) { return fmt.Errorf("no matching SecurityGroup found") } - if len(resp.SecurityGroups) > 1 { + if errors.Is(err, tfresource.ErrTooManyResults) { return fmt.Errorf("multiple Security Groups matched; use additional constraints to reduce matches to a single Security Group") } - - sg := resp.SecurityGroups[0] + if err != nil { + return err + } d.SetId(aws.StringValue(sg.GroupId)) d.Set("name", sg.GroupName) diff --git a/aws/internal/service/ec2/finder/finder.go b/aws/internal/service/ec2/finder/finder.go index 87b8987c635..14ac59610ea 100644 --- a/aws/internal/service/ec2/finder/finder.go +++ b/aws/internal/service/ec2/finder/finder.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" tfnet "github.com/terraform-providers/terraform-provider-aws/aws/internal/net" tfec2 "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) // CarrierGatewayByID returns the carrier gateway corresponding to the specified identifier. @@ -413,6 +414,23 @@ func SecurityGroupByID(conn *ec2.EC2, id string) (*ec2.SecurityGroup, error) { input := &ec2.DescribeSecurityGroupsInput{ GroupIds: aws.StringSlice([]string{id}), } + return SecurityGroup(conn, input) +} + +// SecurityGroupByNameAndVpcID looks up a security group by name and VPC ID. Returns a resource.NotFoundError if not found. +func SecurityGroupByNameAndVpcID(conn *ec2.EC2, name, vpcID string) (*ec2.SecurityGroup, error) { + input := &ec2.DescribeSecurityGroupsInput{ + Filters: tfec2.BuildAttributeFilterList( + map[string]string{ + "group-name": name, + "vpc-id": vpcID, + }, + ), + } + return SecurityGroup(conn, input) +} + +func SecurityGroup(conn *ec2.EC2, input *ec2.DescribeSecurityGroupsInput) (*ec2.SecurityGroup, error) { result, err := conn.DescribeSecurityGroups(input) if tfawserr.ErrCodeEquals(err, tfec2.InvalidSecurityGroupIDNotFound) || tfawserr.ErrCodeEquals(err, tfec2.InvalidGroupNotFound) { @@ -426,10 +444,11 @@ func SecurityGroupByID(conn *ec2.EC2, id string) (*ec2.SecurityGroup, error) { } if result == nil || len(result.SecurityGroups) == 0 || result.SecurityGroups[0] == nil { - return nil, &resource.NotFoundError{ - Message: "Empty result", - LastRequest: input, - } + return nil, tfresource.NewEmptyResultError(input) + } + + if len(result.SecurityGroups) > 1 { + return nil, tfresource.NewTooManyResultsError(len(result.SecurityGroups), input) } return result.SecurityGroups[0], nil diff --git a/aws/internal/tfresource/not_found_error.go b/aws/internal/tfresource/not_found_error.go new file mode 100644 index 00000000000..d57ee8ca688 --- /dev/null +++ b/aws/internal/tfresource/not_found_error.go @@ -0,0 +1,79 @@ +package tfresource + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +type EmptyResultError struct { + LastRequest interface{} +} + +var ErrEmptyResult = &EmptyResultError{} + +func NewEmptyResultError(lastRequest interface{}) error { + return &EmptyResultError{ + LastRequest: lastRequest, + } +} + +func (e *EmptyResultError) Error() string { + return "empty result" +} + +func (e *EmptyResultError) Is(err error) bool { + _, ok := err.(*EmptyResultError) + return ok +} + +func (e *EmptyResultError) As(target interface{}) bool { + t, ok := target.(**resource.NotFoundError) + if !ok { + return false + } + + *t = &resource.NotFoundError{ + Message: e.Error(), + LastRequest: e.LastRequest, + } + + return true +} + +type TooManyResultsError struct { + Count int + LastRequest interface{} +} + +var ErrTooManyResults = &TooManyResultsError{} + +func NewTooManyResultsError(count int, lastRequest interface{}) error { + return &TooManyResultsError{ + Count: count, + LastRequest: lastRequest, + } +} + +func (e *TooManyResultsError) Error() string { + return fmt.Sprintf("too many results: wanted 1, got %d", e.Count) +} + +func (e *TooManyResultsError) Is(err error) bool { + _, ok := err.(*TooManyResultsError) + return ok +} + +func (e *TooManyResultsError) As(target interface{}) bool { + t, ok := target.(**resource.NotFoundError) + if !ok { + return false + } + + *t = &resource.NotFoundError{ + Message: e.Error(), + LastRequest: e.LastRequest, + } + + return true +} diff --git a/aws/internal/tfresource/not_found_error_test.go b/aws/internal/tfresource/not_found_error_test.go new file mode 100644 index 00000000000..a96f7b522ae --- /dev/null +++ b/aws/internal/tfresource/not_found_error_test.go @@ -0,0 +1,156 @@ +package tfresource + +import ( + "errors" + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestEmptyResultErrorAsNotFoundError(t *testing.T) { + lastRequest := 123 + err := NewEmptyResultError(lastRequest) + + var nfe *resource.NotFoundError + ok := errors.As(err, &nfe) + + if !ok { + t.Fatal("expected errors.As() to return true") + } + if nfe.Message != "empty result" { + t.Errorf(`expected Message to be "empty result", got %q`, nfe.Message) + } + if nfe.LastRequest != lastRequest { + t.Errorf("unexpected value for LastRequest") + } +} + +func TestEmptyResultErrorIs(t *testing.T) { + testCases := []struct { + name string + err error + expected bool + }{ + { + name: "compare to nil", + err: nil, + }, + { + name: "other error", + err: errors.New("test"), + }, + { + name: "EmptyResultError with LastRequest", + err: &EmptyResultError{ + LastRequest: 123, + }, + expected: true, + }, + { + name: "ErrEmptyResult", + err: ErrEmptyResult, + expected: true, + }, + { + name: "wrapped other error", + err: fmt.Errorf("test: %w", errors.New("test")), + }, + { + name: "wrapped EmptyResultError with LastRequest", + err: fmt.Errorf("test: %w", &EmptyResultError{ + LastRequest: 123, + }), + expected: true, + }, + { + name: "wrapped ErrEmptyResult", + err: fmt.Errorf("test: %w", ErrEmptyResult), + expected: true, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + err := &EmptyResultError{} + ok := errors.Is(testCase.err, err) + if ok != testCase.expected { + t.Errorf("got %t, expected %t", ok, testCase.expected) + } + }) + } +} + +func TestTooManyResultsErrorAsNotFoundError(t *testing.T) { + count := 2 + lastRequest := 123 + err := NewTooManyResultsError(count, lastRequest) + + var nfe *resource.NotFoundError + ok := errors.As(err, &nfe) + + if !ok { + t.Fatal("expected errors.As() to return true") + } + if expected := fmt.Sprintf("too many results: wanted 1, got %d", count); nfe.Message != expected { + t.Errorf(`expected Message to be %q, got %q`, expected, nfe.Message) + } + if nfe.LastRequest != lastRequest { + t.Errorf("unexpected value for LastRequest") + } +} + +func TestTooManyResultsErrorIs(t *testing.T) { + testCases := []struct { + name string + err error + expected bool + }{ + { + name: "compare to nil", + err: nil, + }, + { + name: "other error", + err: errors.New("test"), + }, + { + name: "TooManyResultsError with LastRequest", + err: &TooManyResultsError{ + LastRequest: 123, + }, + expected: true, + }, + { + name: "ErrTooManyResults", + err: ErrTooManyResults, + expected: true, + }, + { + name: "wrapped other error", + err: fmt.Errorf("test: %w", errors.New("test")), + }, + { + name: "wrapped TooManyResultsError with LastRequest", + err: fmt.Errorf("test: %w", &TooManyResultsError{ + LastRequest: 123, + }), + expected: true, + }, + { + name: "wrapped ErrTooManyResults", + err: fmt.Errorf("test: %w", ErrTooManyResults), + expected: true, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + err := &TooManyResultsError{} + ok := errors.Is(testCase.err, err) + if ok != testCase.expected { + t.Errorf("got %t, expected %t", ok, testCase.expected) + } + }) + } +} diff --git a/aws/resource_aws_elb.go b/aws/resource_aws_elb.go index 463665bb0fa..665ef14276f 100644 --- a/aws/resource_aws_elb.go +++ b/aws/resource_aws_elb.go @@ -19,6 +19,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/terraform-providers/terraform-provider-aws/aws/internal/hashcode" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2/finder" ) func resourceAwsElb() *schema.Resource { @@ -405,14 +406,12 @@ func flattenAwsELbResource(d *schema.ResourceData, ec2conn *ec2.EC2, elbconn *el d.Set("source_security_group", group) // Manually look up the ELB Security Group ID, since it's not provided - var elbVpc string if lb.VPCId != nil { - elbVpc = *lb.VPCId - sgId, err := sourceSGIdByName(ec2conn, *lb.SourceSecurityGroup.GroupName, elbVpc) + sg, err := finder.SecurityGroupByNameAndVpcID(ec2conn, aws.StringValue(lb.SourceSecurityGroup.GroupName), aws.StringValue(lb.VPCId)) if err != nil { - return fmt.Errorf("Error looking up ELB Security Group ID: %s", err) + return fmt.Errorf("Error looking up ELB Security Group ID: %w", err) } else { - d.Set("source_security_group_id", sgId) + d.Set("source_security_group_id", aws.StringValue(sg.GroupId)) } } } @@ -828,54 +827,6 @@ func isLoadBalancerNotFound(err error) bool { return ok && elberr.Code() == elb.ErrCodeAccessPointNotFoundException } -func sourceSGIdByName(conn *ec2.EC2, sg, vpcId string) (string, error) { - var filters []*ec2.Filter - var sgFilterName, sgFilterVPCID *ec2.Filter - sgFilterName = &ec2.Filter{ - Name: aws.String("group-name"), - Values: []*string{aws.String(sg)}, - } - - if vpcId != "" { - sgFilterVPCID = &ec2.Filter{ - Name: aws.String("vpc-id"), - Values: []*string{aws.String(vpcId)}, - } - } - - filters = append(filters, sgFilterName) - - if sgFilterVPCID != nil { - filters = append(filters, sgFilterVPCID) - } - - req := &ec2.DescribeSecurityGroupsInput{ - Filters: filters, - } - resp, err := conn.DescribeSecurityGroups(req) - if err != nil { - if ec2err, ok := err.(awserr.Error); ok { - if ec2err.Code() == "InvalidSecurityGroupID.NotFound" || - ec2err.Code() == "InvalidGroup.NotFound" { - resp = nil - err = nil - } - } - - if err != nil { - log.Printf("Error on ELB SG look up: %s", err) - return "", err - } - } - - if resp == nil || len(resp.SecurityGroups) == 0 { - return "", fmt.Errorf("No security groups found for name %s and vpc id %s", sg, vpcId) - } - - group := resp.SecurityGroups[0] - return *group.GroupId, nil -} - func validateAccessLogsInterval(v interface{}, k string) (ws []string, errors []error) { value := v.(int) From f512ab8a1315283d72886299060e1542a37880f8 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Fri, 12 Feb 2021 16:54:03 -0800 Subject: [PATCH 342/398] Moves error files --- aws/internal/tfresource/{not_found_error.go => finder_errors.go} | 0 .../tfresource/{not_found_error_test.go => finder_errors_test.go} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename aws/internal/tfresource/{not_found_error.go => finder_errors.go} (100%) rename aws/internal/tfresource/{not_found_error_test.go => finder_errors_test.go} (100%) diff --git a/aws/internal/tfresource/not_found_error.go b/aws/internal/tfresource/finder_errors.go similarity index 100% rename from aws/internal/tfresource/not_found_error.go rename to aws/internal/tfresource/finder_errors.go diff --git a/aws/internal/tfresource/not_found_error_test.go b/aws/internal/tfresource/finder_errors_test.go similarity index 100% rename from aws/internal/tfresource/not_found_error_test.go rename to aws/internal/tfresource/finder_errors_test.go From 38be8f9eb94b8c6ebbaddc1164f11990c5f370a9 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Thu, 25 Mar 2021 17:53:14 -0700 Subject: [PATCH 343/398] Removes new error types --- aws/data_source_aws_security_group.go | 16 +- aws/internal/service/ec2/finder/finder.go | 11 +- aws/internal/tfresource/finder_errors.go | 79 --------- aws/internal/tfresource/finder_errors_test.go | 156 ------------------ 4 files changed, 18 insertions(+), 244 deletions(-) delete mode 100644 aws/internal/tfresource/finder_errors.go delete mode 100644 aws/internal/tfresource/finder_errors_test.go diff --git a/aws/data_source_aws_security_group.go b/aws/data_source_aws_security_group.go index 1bcb54a8980..311bcfbb2d5 100644 --- a/aws/data_source_aws_security_group.go +++ b/aws/data_source_aws_security_group.go @@ -3,14 +3,15 @@ package aws import ( "errors" "fmt" + "strings" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2/finder" - "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func dataSourceAwsSecurityGroup() *schema.Resource { @@ -79,11 +80,14 @@ func dataSourceAwsSecurityGroupRead(d *schema.ResourceData, meta interface{}) er } sg, err := finder.SecurityGroup(conn, req) - if errors.Is(err, tfresource.ErrEmptyResult) { - return fmt.Errorf("no matching SecurityGroup found") - } - if errors.Is(err, tfresource.ErrTooManyResults) { - return fmt.Errorf("multiple Security Groups matched; use additional constraints to reduce matches to a single Security Group") + var nfe *resource.NotFoundError + if errors.As(err, &nfe) { + if nfe.Message == "empty result" { + return fmt.Errorf("no matching SecurityGroup found") + } + if strings.HasPrefix(nfe.Message, "too many results:") { + return fmt.Errorf("multiple Security Groups matched; use additional constraints to reduce matches to a single Security Group") + } } if err != nil { return err diff --git a/aws/internal/service/ec2/finder/finder.go b/aws/internal/service/ec2/finder/finder.go index 14ac59610ea..cbf273af144 100644 --- a/aws/internal/service/ec2/finder/finder.go +++ b/aws/internal/service/ec2/finder/finder.go @@ -9,7 +9,6 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" tfnet "github.com/terraform-providers/terraform-provider-aws/aws/internal/net" tfec2 "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2" - "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) // CarrierGatewayByID returns the carrier gateway corresponding to the specified identifier. @@ -444,11 +443,17 @@ func SecurityGroup(conn *ec2.EC2, input *ec2.DescribeSecurityGroupsInput) (*ec2. } if result == nil || len(result.SecurityGroups) == 0 || result.SecurityGroups[0] == nil { - return nil, tfresource.NewEmptyResultError(input) + return nil, &resource.NotFoundError{ + Message: "empty result", + LastRequest: input, + } } if len(result.SecurityGroups) > 1 { - return nil, tfresource.NewTooManyResultsError(len(result.SecurityGroups), input) + return nil, &resource.NotFoundError{ + Message: fmt.Sprintf("too many results: wanted 1, got %d", len(result.SecurityGroups)), + LastRequest: input, + } } return result.SecurityGroups[0], nil diff --git a/aws/internal/tfresource/finder_errors.go b/aws/internal/tfresource/finder_errors.go deleted file mode 100644 index d57ee8ca688..00000000000 --- a/aws/internal/tfresource/finder_errors.go +++ /dev/null @@ -1,79 +0,0 @@ -package tfresource - -import ( - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" -) - -type EmptyResultError struct { - LastRequest interface{} -} - -var ErrEmptyResult = &EmptyResultError{} - -func NewEmptyResultError(lastRequest interface{}) error { - return &EmptyResultError{ - LastRequest: lastRequest, - } -} - -func (e *EmptyResultError) Error() string { - return "empty result" -} - -func (e *EmptyResultError) Is(err error) bool { - _, ok := err.(*EmptyResultError) - return ok -} - -func (e *EmptyResultError) As(target interface{}) bool { - t, ok := target.(**resource.NotFoundError) - if !ok { - return false - } - - *t = &resource.NotFoundError{ - Message: e.Error(), - LastRequest: e.LastRequest, - } - - return true -} - -type TooManyResultsError struct { - Count int - LastRequest interface{} -} - -var ErrTooManyResults = &TooManyResultsError{} - -func NewTooManyResultsError(count int, lastRequest interface{}) error { - return &TooManyResultsError{ - Count: count, - LastRequest: lastRequest, - } -} - -func (e *TooManyResultsError) Error() string { - return fmt.Sprintf("too many results: wanted 1, got %d", e.Count) -} - -func (e *TooManyResultsError) Is(err error) bool { - _, ok := err.(*TooManyResultsError) - return ok -} - -func (e *TooManyResultsError) As(target interface{}) bool { - t, ok := target.(**resource.NotFoundError) - if !ok { - return false - } - - *t = &resource.NotFoundError{ - Message: e.Error(), - LastRequest: e.LastRequest, - } - - return true -} diff --git a/aws/internal/tfresource/finder_errors_test.go b/aws/internal/tfresource/finder_errors_test.go deleted file mode 100644 index a96f7b522ae..00000000000 --- a/aws/internal/tfresource/finder_errors_test.go +++ /dev/null @@ -1,156 +0,0 @@ -package tfresource - -import ( - "errors" - "fmt" - "testing" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" -) - -func TestEmptyResultErrorAsNotFoundError(t *testing.T) { - lastRequest := 123 - err := NewEmptyResultError(lastRequest) - - var nfe *resource.NotFoundError - ok := errors.As(err, &nfe) - - if !ok { - t.Fatal("expected errors.As() to return true") - } - if nfe.Message != "empty result" { - t.Errorf(`expected Message to be "empty result", got %q`, nfe.Message) - } - if nfe.LastRequest != lastRequest { - t.Errorf("unexpected value for LastRequest") - } -} - -func TestEmptyResultErrorIs(t *testing.T) { - testCases := []struct { - name string - err error - expected bool - }{ - { - name: "compare to nil", - err: nil, - }, - { - name: "other error", - err: errors.New("test"), - }, - { - name: "EmptyResultError with LastRequest", - err: &EmptyResultError{ - LastRequest: 123, - }, - expected: true, - }, - { - name: "ErrEmptyResult", - err: ErrEmptyResult, - expected: true, - }, - { - name: "wrapped other error", - err: fmt.Errorf("test: %w", errors.New("test")), - }, - { - name: "wrapped EmptyResultError with LastRequest", - err: fmt.Errorf("test: %w", &EmptyResultError{ - LastRequest: 123, - }), - expected: true, - }, - { - name: "wrapped ErrEmptyResult", - err: fmt.Errorf("test: %w", ErrEmptyResult), - expected: true, - }, - } - - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { - err := &EmptyResultError{} - ok := errors.Is(testCase.err, err) - if ok != testCase.expected { - t.Errorf("got %t, expected %t", ok, testCase.expected) - } - }) - } -} - -func TestTooManyResultsErrorAsNotFoundError(t *testing.T) { - count := 2 - lastRequest := 123 - err := NewTooManyResultsError(count, lastRequest) - - var nfe *resource.NotFoundError - ok := errors.As(err, &nfe) - - if !ok { - t.Fatal("expected errors.As() to return true") - } - if expected := fmt.Sprintf("too many results: wanted 1, got %d", count); nfe.Message != expected { - t.Errorf(`expected Message to be %q, got %q`, expected, nfe.Message) - } - if nfe.LastRequest != lastRequest { - t.Errorf("unexpected value for LastRequest") - } -} - -func TestTooManyResultsErrorIs(t *testing.T) { - testCases := []struct { - name string - err error - expected bool - }{ - { - name: "compare to nil", - err: nil, - }, - { - name: "other error", - err: errors.New("test"), - }, - { - name: "TooManyResultsError with LastRequest", - err: &TooManyResultsError{ - LastRequest: 123, - }, - expected: true, - }, - { - name: "ErrTooManyResults", - err: ErrTooManyResults, - expected: true, - }, - { - name: "wrapped other error", - err: fmt.Errorf("test: %w", errors.New("test")), - }, - { - name: "wrapped TooManyResultsError with LastRequest", - err: fmt.Errorf("test: %w", &TooManyResultsError{ - LastRequest: 123, - }), - expected: true, - }, - { - name: "wrapped ErrTooManyResults", - err: fmt.Errorf("test: %w", ErrTooManyResults), - expected: true, - }, - } - - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { - err := &TooManyResultsError{} - ok := errors.Is(testCase.err, err) - if ok != testCase.expected { - t.Errorf("got %t, expected %t", ok, testCase.expected) - } - }) - } -} From f7a03408934ec47063312269f2cb515d97559945 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Thu, 25 Mar 2021 20:00:41 -0700 Subject: [PATCH 344/398] Replace calls to `DescribeSecurityGroups` with single result with `finder.SecurityGroup...()` --- aws/data_source_aws_elb.go | 5 +- ...esource_aws_default_security_group_test.go | 31 ++-- aws/resource_aws_elb.go | 2 +- aws/resource_aws_emr_cluster_test.go | 68 +++----- ...s_globalaccelerator_endpoint_group_test.go | 23 +-- aws/resource_aws_security_group.go | 149 ++++------------- aws/resource_aws_security_group_rule.go | 98 +++--------- aws/resource_aws_security_group_rule_test.go | 54 ++----- aws/resource_aws_security_group_test.go | 150 ++++++------------ 9 files changed, 162 insertions(+), 418 deletions(-) diff --git a/aws/data_source_aws_elb.go b/aws/data_source_aws_elb.go index dd53bdc9bda..23ccc709072 100644 --- a/aws/data_source_aws_elb.go +++ b/aws/data_source_aws_elb.go @@ -9,6 +9,7 @@ import ( "github.com/aws/aws-sdk-go/service/elb" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2/finder" ) func dataSourceAwsElb() *schema.Resource { @@ -261,11 +262,11 @@ func dataSourceAwsElbRead(d *schema.ResourceData, meta interface{}) error { var elbVpc string if lb.VPCId != nil { elbVpc = aws.StringValue(lb.VPCId) - sgId, err := sourceSGIdByName(ec2conn, aws.StringValue(lb.SourceSecurityGroup.GroupName), elbVpc) + sg, err := finder.SecurityGroupByNameAndVpcID(ec2conn, aws.StringValue(lb.SourceSecurityGroup.GroupName), elbVpc) if err != nil { return fmt.Errorf("error looking up ELB Security Group ID: %w", err) } else { - d.Set("source_security_group_id", sgId) + d.Set("source_security_group_id", sg.GroupId) } } } diff --git a/aws/resource_aws_default_security_group_test.go b/aws/resource_aws_default_security_group_test.go index 0792d0654b4..30bfd9eb01a 100644 --- a/aws/resource_aws_default_security_group_test.go +++ b/aws/resource_aws_default_security_group_test.go @@ -8,6 +8,7 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2/finder" ) func TestAccAWSDefaultSecurityGroup_Vpc_basic(t *testing.T) { @@ -179,24 +180,19 @@ func testAccCheckAWSDefaultSecurityGroupExists(n string, group *ec2.SecurityGrou } if rs.Primary.ID == "" { - return fmt.Errorf("No Security Group is set") + return fmt.Errorf("No EC2 Default Security Group ID is set") } conn := testAccProvider.Meta().(*AWSClient).ec2conn - req := &ec2.DescribeSecurityGroupsInput{ - GroupIds: []*string{aws.String(rs.Primary.ID)}, - } - resp, err := conn.DescribeSecurityGroups(req) + + sg, err := finder.SecurityGroupByID(conn, rs.Primary.ID) if err != nil { return err } - if len(resp.SecurityGroups) > 0 && *resp.SecurityGroups[0].GroupId == rs.Primary.ID { - *group = *resp.SecurityGroups[0] - return nil - } + *group = *sg - return fmt.Errorf("Security Group not found") + return nil } } @@ -213,21 +209,12 @@ func testAccCheckAWSDefaultSecurityGroupEc2ClassicExists(n string, group *ec2.Se conn := testAccProviderEc2Classic.Meta().(*AWSClient).ec2conn - input := &ec2.DescribeSecurityGroupsInput{ - GroupIds: []*string{aws.String(rs.Primary.ID)}, - } - - resp, err := conn.DescribeSecurityGroups(input) - + sg, err := finder.SecurityGroupByID(conn, rs.Primary.ID) if err != nil { - return fmt.Errorf("error describing EC2 Default Security Group (%s): %w", rs.Primary.ID, err) - } - - if len(resp.SecurityGroups) == 0 || aws.StringValue(resp.SecurityGroups[0].GroupId) != rs.Primary.ID { - return fmt.Errorf("EC2 Default Security Group (%s) not found", rs.Primary.ID) + return err } - *group = *resp.SecurityGroups[0] + *group = *sg return nil } diff --git a/aws/resource_aws_elb.go b/aws/resource_aws_elb.go index 665ef14276f..06cd1507d0d 100644 --- a/aws/resource_aws_elb.go +++ b/aws/resource_aws_elb.go @@ -411,7 +411,7 @@ func flattenAwsELbResource(d *schema.ResourceData, ec2conn *ec2.EC2, elbconn *el if err != nil { return fmt.Errorf("Error looking up ELB Security Group ID: %w", err) } else { - d.Set("source_security_group_id", aws.StringValue(sg.GroupId)) + d.Set("source_security_group_id", sg.GroupId) } } } diff --git a/aws/resource_aws_emr_cluster_test.go b/aws/resource_aws_emr_cluster_test.go index 11e4891d379..a10cca67782 100644 --- a/aws/resource_aws_emr_cluster_test.go +++ b/aws/resource_aws_emr_cluster_test.go @@ -8,12 +8,12 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/emr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2/finder" ) func init() { @@ -26,7 +26,7 @@ func init() { func testSweepEmrClusters(region string) error { client, err := sharedClientForRegion(region) if err != nil { - return fmt.Errorf("error getting client: %s", err) + return fmt.Errorf("error getting client: %w", err) } conn := client.(*AWSClient).emrconn @@ -71,7 +71,7 @@ func testSweepEmrClusters(region string) error { log.Printf("[WARN] Skipping EMR Cluster sweep for %s: %s", region, err) return nil } - return fmt.Errorf("error retrieving EMR Clusters: %s", err) + return fmt.Errorf("error retrieving EMR Clusters: %w", err) } return nil @@ -1495,25 +1495,24 @@ func testAccCheckAWSEmrDestroy(s *terraform.State) error { continue } - params := &emr.DescribeClusterInput{ + input := &emr.DescribeClusterInput{ ClusterId: aws.String(rs.Primary.ID), } - describe, err := conn.DescribeCluster(params) - - if err == nil { - if describe.Cluster != nil && - *describe.Cluster.Status.State == "WAITING" { - return fmt.Errorf("EMR Cluster still exists") - } + output, err := conn.DescribeCluster(input) + if err != nil { + return err } - providerErr, ok := err.(awserr.Error) - if !ok { - return err + // if output.Cluster != nil && + // *output.Cluster.Status.State == "WAITING" { + // return fmt.Errorf("EMR Cluster still exists") + // } + if output.Cluster == nil || output.Cluster.Status == nil || aws.StringValue(output.Cluster.Status.State) == emr.ClusterStateTerminated { + continue } - log.Printf("[ERROR] %v", providerErr) + return fmt.Errorf("EMR Cluster still exists") } return nil @@ -1533,7 +1532,7 @@ func testAccCheckAWSEmrClusterExists(n string, v *emr.Cluster) resource.TestChec ClusterId: aws.String(rs.Primary.ID), }) if err != nil { - return fmt.Errorf("EMR error: %v", err) + return fmt.Errorf("EMR error: %w", err) } if describe.Cluster == nil || *describe.Cluster.Id != rs.Primary.ID { @@ -1604,7 +1603,7 @@ func testAccCheckAWSEmrClusterDisappears(cluster *emr.Cluster) resource.TestChec } if err != nil { - return fmt.Errorf("error waiting for EMR Cluster (%s) Instances to drain: %s", id, err) + return fmt.Errorf("error waiting for EMR Cluster (%s) Instances to drain: %w", id, err) } return nil @@ -1639,10 +1638,10 @@ func testAccEmrDeleteManagedSecurityGroups(conn *ec2.EC2, vpc *ec2.Vpc) error { } for groupName := range managedSecurityGroups { - securityGroup, err := testAccEmrDescribeManagedSecurityGroup(conn, vpc, groupName) + securityGroup, err := finder.SecurityGroupByNameAndVpcID(conn, groupName, aws.StringValue(vpc.VpcId)) if err != nil { - return fmt.Errorf("error describing EMR Managed Security Group (%s): %s", groupName, err) + return fmt.Errorf("error describing EMR Managed Security Group (%s): %w", groupName, err) } managedSecurityGroups[groupName] = securityGroup @@ -1658,7 +1657,7 @@ func testAccEmrDeleteManagedSecurityGroups(conn *ec2.EC2, vpc *ec2.Vpc) error { err := testAccEmrRevokeManagedSecurityGroup(conn, securityGroup) if err != nil { - return fmt.Errorf("error revoking EMR Managed Security Group (%s): %s", groupName, err) + return fmt.Errorf("error revoking EMR Managed Security Group (%s): %w", groupName, err) } } @@ -1670,40 +1669,13 @@ func testAccEmrDeleteManagedSecurityGroups(conn *ec2.EC2, vpc *ec2.Vpc) error { err := testAccEmrDeleteManagedSecurityGroup(conn, securityGroup) if err != nil { - return fmt.Errorf("error deleting EMR Managed Security Group (%s): %s", groupName, err) + return fmt.Errorf("error deleting EMR Managed Security Group (%s): %w", groupName, err) } } return nil } -func testAccEmrDescribeManagedSecurityGroup(conn *ec2.EC2, vpc *ec2.Vpc, securityGroupName string) (*ec2.SecurityGroup, error) { - input := &ec2.DescribeSecurityGroupsInput{ - Filters: []*ec2.Filter{ - { - Name: aws.String("group-name"), - Values: aws.StringSlice([]string{securityGroupName}), - }, - { - Name: aws.String("vpc-id"), - Values: []*string{vpc.VpcId}, - }, - }, - } - - output, err := conn.DescribeSecurityGroups(input) - - if err != nil { - return nil, err - } - - if output == nil || len(output.SecurityGroups) != 1 { - return nil, nil - } - - return output.SecurityGroups[0], nil -} - func testAccEmrRevokeManagedSecurityGroup(conn *ec2.EC2, securityGroup *ec2.SecurityGroup) error { input := &ec2.RevokeSecurityGroupIngressInput{ GroupId: securityGroup.GroupId, diff --git a/aws/resource_aws_globalaccelerator_endpoint_group_test.go b/aws/resource_aws_globalaccelerator_endpoint_group_test.go index 0dda503b349..aac2d49854b 100644 --- a/aws/resource_aws_globalaccelerator_endpoint_group_test.go +++ b/aws/resource_aws_globalaccelerator_endpoint_group_test.go @@ -1,6 +1,7 @@ package aws import ( + "errors" "fmt" "regexp" "testing" @@ -12,6 +13,7 @@ import ( "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/terraform" + ec2finder "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2/finder" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/globalaccelerator/finder" "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) @@ -469,27 +471,18 @@ func testAccCheckGlobalAcceleratorEndpointGroupDeleteGlobalAcceleratorSecurityGr return func(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).ec2conn - input := &ec2.DescribeSecurityGroupsInput{ - Filters: buildEC2AttributeFilterList( - map[string]string{ - "group-name": "GlobalAccelerator", - "vpc-id": aws.StringValue(vpc.VpcId), - }, - ), + sg, err := ec2finder.SecurityGroupByNameAndVpcID(conn, "GlobalAccelerator", aws.StringValue(vpc.VpcId)) + var nfe *resource.NotFoundError + if errors.As(err, &nfe) { + // Already gone. + return nil } - - output, err := conn.DescribeSecurityGroups(input) if err != nil { return err } - if len(output.SecurityGroups) == 0 { - // Already gone. - return nil - } - _, err = conn.DeleteSecurityGroup(&ec2.DeleteSecurityGroupInput{ - GroupId: output.SecurityGroups[0].GroupId, + GroupId: sg.GroupId, }) if err != nil { return err diff --git a/aws/resource_aws_security_group.go b/aws/resource_aws_security_group.go index 31a08ea60b3..ffb90175518 100644 --- a/aws/resource_aws_security_group.go +++ b/aws/resource_aws_security_group.go @@ -2,6 +2,7 @@ package aws import ( "bytes" + "errors" "fmt" "log" "sort" @@ -11,7 +12,6 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/ec2" "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -20,6 +20,8 @@ import ( "github.com/terraform-providers/terraform-provider-aws/aws/internal/hashcode" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" "github.com/terraform-providers/terraform-provider-aws/aws/internal/naming" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2/waiter" ) func resourceAwsSecurityGroup() *schema.Resource { @@ -262,11 +264,10 @@ func resourceAwsSecurityGroupCreate(d *schema.ResourceData, meta interface{}) er securityGroupOpts.GroupName = aws.String(groupName) var err error - log.Printf( - "[DEBUG] Security Group create configuration: %#v", securityGroupOpts) + log.Printf("[DEBUG] Security Group create configuration: %s", securityGroupOpts) createResp, err := conn.CreateSecurityGroup(securityGroupOpts) if err != nil { - return fmt.Errorf("Error creating Security Group: %s", err) + return fmt.Errorf("Error creating Security Group: %w", err) } d.SetId(aws.StringValue(createResp.GroupId)) @@ -274,16 +275,15 @@ func resourceAwsSecurityGroupCreate(d *schema.ResourceData, meta interface{}) er log.Printf("[INFO] Security Group ID: %s", d.Id()) // Wait for the security group to truly exist - resp, err := waitForSgToExist(conn, d.Id(), d.Timeout(schema.TimeoutCreate)) + group, err := waiter.SecurityGroupCreated(conn, d.Id(), d.Timeout(schema.TimeoutCreate)) if err != nil { return fmt.Errorf( - "Error waiting for Security Group (%s) to become available: %s", + "Error waiting for Security Group (%s) to become available: %w", d.Id(), err) } // AWS defaults all Security Groups to have an ALLOW ALL egress rule. Here we // revoke that rule, so users don't unknowingly have/use it. - group := resp.(*ec2.SecurityGroup) if group.VpcId != nil && *group.VpcId != "" { log.Printf("[DEBUG] Revoking default egress rule for Security Group for %s", d.Id()) @@ -304,9 +304,7 @@ func resourceAwsSecurityGroupCreate(d *schema.ResourceData, meta interface{}) er } if _, err = conn.RevokeSecurityGroupEgress(req); err != nil { - return fmt.Errorf( - "Error revoking default egress rule for Security Group (%s): %s", - d.Id(), err) + return fmt.Errorf("Error revoking default egress rule for Security Group (%s): %w", d.Id(), err) } log.Printf("[DEBUG] Revoking default IPv6 egress rule for Security Group for %s", d.Id()) @@ -330,10 +328,8 @@ func resourceAwsSecurityGroupCreate(d *schema.ResourceData, meta interface{}) er if err != nil { //If we have a NotFound or InvalidParameterValue, then we are trying to remove the default IPv6 egress of a non-IPv6 //enabled SG - if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() != "InvalidPermission.NotFound" && !isAWSErr(err, "InvalidParameterValue", "remote-ipv6-range") { - return fmt.Errorf( - "Error revoking default IPv6 egress rule for Security Group (%s): %s", - d.Id(), err) + if !tfawserr.ErrCodeEquals(err, "InvalidPermission.NotFound") && !tfawserr.ErrMessageContains(err, "InvalidParameterValue", "remote-ipv6-range") { + return fmt.Errorf("Error revoking default IPv6 egress rule for Security Group (%s): %w", d.Id(), err) } } @@ -347,25 +343,16 @@ func resourceAwsSecurityGroupRead(d *schema.ResourceData, meta interface{}) erro defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig - var sgRaw interface{} - var err error - if d.IsNewResource() { - sgRaw, err = waitForSgToExist(conn, d.Id(), d.Timeout(schema.TimeoutRead)) - } else { - sgRaw, _, err = SGStateRefreshFunc(conn, d.Id())() - } - - if err != nil { - return err - } - - if sgRaw == nil { + sg, err := finder.SecurityGroupByID(conn, d.Id()) + var nfe *resource.NotFoundError + if errors.As(err, &nfe) { log.Printf("[WARN] Security group (%s) not found, removing from state", d.Id()) d.SetId("") return nil } - - sg := sgRaw.(*ec2.SecurityGroup) + if err != nil { + return fmt.Errorf("error reading Security Group (%s): %w", d.Id(), err) + } remoteIngressRules := resourceAwsSecurityGroupIPPermGather(d.Id(), sg.IpPermissions, sg.OwnerId) remoteEgressRules := resourceAwsSecurityGroupIPPermGather(d.Id(), sg.IpPermissionsEgress, sg.OwnerId) @@ -394,11 +381,11 @@ func resourceAwsSecurityGroupRead(d *schema.ResourceData, meta interface{}) erro d.Set("vpc_id", sg.VpcId) if err := d.Set("ingress", ingressRules); err != nil { - log.Printf("[WARN] Error setting Ingress rule set for (%s): %s", d.Id(), err) + return fmt.Errorf("error setting ingress: %w", err) } if err := d.Set("egress", egressRules); err != nil { - log.Printf("[WARN] Error setting Egress rule set for (%s): %s", d.Id(), err) + return fmt.Errorf("error setting egress: %w", err) } tags := keyvaluetags.Ec2KeyValueTags(sg.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig) @@ -418,25 +405,11 @@ func resourceAwsSecurityGroupRead(d *schema.ResourceData, meta interface{}) erro func resourceAwsSecurityGroupUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ec2conn - var sgRaw interface{} - var err error - if d.IsNewResource() { - sgRaw, err = waitForSgToExist(conn, d.Id(), d.Timeout(schema.TimeoutRead)) - } else { - sgRaw, _, err = SGStateRefreshFunc(conn, d.Id())() - } - + group, err := finder.SecurityGroupByID(conn, d.Id()) if err != nil { - return err - } - if sgRaw == nil { - log.Printf("[WARN] Security group (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil + return fmt.Errorf("error updating Security Group (%s): %w", d.Id(), err) } - group := sgRaw.(*ec2.SecurityGroup) - err = resourceAwsSecurityGroupUpdateRules(d, "ingress", meta, group) if err != nil { return err @@ -453,7 +426,7 @@ func resourceAwsSecurityGroupUpdate(d *schema.ResourceData, meta interface{}) er o, n := d.GetChange("tags_all") if err := keyvaluetags.Ec2UpdateTags(conn, d.Id(), o, n); err != nil { - return fmt.Errorf("error updating EC2 Security Group (%s) tags: %s", d.Id(), err) + return fmt.Errorf("error updating EC2 Security Group (%s) tags: %w", d.Id(), err) } } @@ -466,7 +439,7 @@ func resourceAwsSecurityGroupDelete(d *schema.ResourceData, meta interface{}) er log.Printf("[DEBUG] Security Group destroy: %v", d.Id()) if err := deleteLingeringLambdaENIs(conn, "group-id", d.Id(), d.Timeout(schema.TimeoutDelete)); err != nil { - return fmt.Errorf("error deleting Lambda ENIs using Security Group (%s): %s", d.Id(), err) + return fmt.Errorf("error deleting Lambda ENIs using Security Group (%s): %w", d.Id(), err) } // conditionally revoke rules first before attempting to delete the group @@ -509,37 +482,31 @@ func resourceAwsSecurityGroupDelete(d *schema.ResourceData, meta interface{}) er } } if err != nil { - return fmt.Errorf("Error deleting security group: %s", err) + return fmt.Errorf("Error deleting security group: %w", err) } return nil } // Revoke all ingress/egress rules that a Security Group has func forceRevokeSecurityGroupRules(conn *ec2.EC2, d *schema.ResourceData) error { - sgRaw, _, err := SGStateRefreshFunc(conn, d.Id())() + group, err := finder.SecurityGroupByID(conn, d.Id()) if err != nil { return err } - if sgRaw == nil { - return nil - } - group := sgRaw.(*ec2.SecurityGroup) if len(group.IpPermissions) > 0 { req := &ec2.RevokeSecurityGroupIngressInput{ GroupId: group.GroupId, IpPermissions: group.IpPermissions, } - if group.VpcId == nil || *group.VpcId == "" { + if aws.StringValue(group.VpcId) == "" { req.GroupId = nil req.GroupName = group.GroupName } _, err = conn.RevokeSecurityGroupIngress(req) if err != nil { - return fmt.Errorf( - "Error revoking security group %s rules: %s", - *group.GroupId, err) + return fmt.Errorf("error revoking Security Group (%s) rules: %w", aws.StringValue(group.GroupId), err) } } @@ -551,9 +518,7 @@ func forceRevokeSecurityGroupRules(conn *ec2.EC2, d *schema.ResourceData) error _, err = conn.RevokeSecurityGroupEgress(req) if err != nil { - return fmt.Errorf( - "Error revoking security group %s rules: %s", - *group.GroupId, err) + return fmt.Errorf("error revoking Security Group (%s) rules: %w", aws.StringValue(group.GroupId), err) } } @@ -775,9 +740,7 @@ func resourceAwsSecurityGroupUpdateRules( } if err != nil { - return fmt.Errorf( - "Error revoking security group %s rules: %s", - ruleset, err) + return fmt.Errorf("error revoking Security Group (%s) rules: %w", ruleset, err) } } @@ -805,9 +768,7 @@ func resourceAwsSecurityGroupUpdateRules( } if err != nil { - return fmt.Errorf( - "Error authorizing security group %s rules: %s", - ruleset, err) + return fmt.Errorf("error authorizing Security Group (%s) rules: %w", ruleset, err) } } } @@ -815,50 +776,6 @@ func resourceAwsSecurityGroupUpdateRules( return nil } -// SGStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch -// a security group. -func SGStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { - return func() (interface{}, string, error) { - req := &ec2.DescribeSecurityGroupsInput{ - GroupIds: []*string{aws.String(id)}, - } - resp, err := conn.DescribeSecurityGroups(req) - if err != nil { - if ec2err, ok := err.(awserr.Error); ok { - if ec2err.Code() == "InvalidSecurityGroupID.NotFound" || - ec2err.Code() == "InvalidGroup.NotFound" { - resp = nil - err = nil - } - } - - if err != nil { - log.Printf("Error on SGStateRefresh: %s", err) - return nil, "", err - } - } - - if resp == nil { - return nil, "", nil - } - - group := resp.SecurityGroups[0] - return group, "exists", nil - } -} - -func waitForSgToExist(conn *ec2.EC2, id string, timeout time.Duration) (interface{}, error) { - log.Printf("[DEBUG] Waiting for Security Group (%s) to exist", id) - stateConf := &resource.StateChangeConf{ - Pending: []string{""}, - Target: []string{"exists"}, - Refresh: SGStateRefreshFunc(conn, id), - Timeout: timeout, - } - - return stateConf.WaitForState() -} - // matchRules receives the group id, type of rules, and the local / remote maps // of rules. We iterate through the local set of rules trying to find a matching // remote rule, which may be structured differently because of how AWS @@ -1443,7 +1360,7 @@ func deleteLingeringLambdaENIs(conn *ec2.EC2, filterName, resourceId string, tim }) if err != nil { - return fmt.Errorf("error describing ENIs: %s", err) + return fmt.Errorf("error describing ENIs: %w", err) } for _, eni := range resp.NetworkInterfaces { @@ -1475,7 +1392,7 @@ func deleteLingeringLambdaENIs(conn *ec2.EC2, filterName, resourceId string, tim } if err != nil { - return fmt.Errorf("error waiting for Lambda V2N ENI (%s) to become available for detachment: %s", eniId, err) + return fmt.Errorf("error waiting for Lambda V2N ENI (%s) to become available for detachment: %w", eniId, err) } eni = eniRaw.(*ec2.NetworkInterface) @@ -1484,13 +1401,13 @@ func deleteLingeringLambdaENIs(conn *ec2.EC2, filterName, resourceId string, tim err = detachNetworkInterface(conn, eni, timeout) if err != nil { - return fmt.Errorf("error detaching Lambda ENI (%s): %s", eniId, err) + return fmt.Errorf("error detaching Lambda ENI (%s): %w", eniId, err) } err = deleteNetworkInterface(conn, eniId) if err != nil { - return fmt.Errorf("error deleting Lambda ENI (%s): %s", eniId, err) + return fmt.Errorf("error deleting Lambda ENI (%s): %w", eniId, err) } } diff --git a/aws/resource_aws_security_group_rule.go b/aws/resource_aws_security_group_rule.go index c9cdc388813..7f3bfdf8c4d 100644 --- a/aws/resource_aws_security_group_rule.go +++ b/aws/resource_aws_security_group_rule.go @@ -10,12 +10,14 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/ec2" + "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/hashcode" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func resourceAwsSecurityGroupRule() *schema.Resource { @@ -155,7 +157,7 @@ func resourceAwsSecurityGroupRuleCreate(d *schema.ResourceData, meta interface{} awsMutexKV.Lock(sg_id) defer awsMutexKV.Unlock(sg_id) - sg, err := findResourceSecurityGroup(conn, sg_id) + sg, err := finder.SecurityGroupByID(conn, sg_id) if err != nil { return err } @@ -209,20 +211,15 @@ func resourceAwsSecurityGroupRuleCreate(d *schema.ResourceData, meta interface{} return fmt.Errorf("Security Group Rule must be type 'ingress' or type 'egress'") } - if autherr != nil { - if awsErr, ok := autherr.(awserr.Error); ok { - if awsErr.Code() == "InvalidPermission.Duplicate" { - return fmt.Errorf(`[WARN] A duplicate Security Group rule was found on (%s). This may be + if tfawserr.ErrCodeEquals(autherr, "InvalidPermission.Duplicate") { + return fmt.Errorf(`[WARN] A duplicate Security Group rule was found on (%s). This may be a side effect of a now-fixed Terraform issue causing two security groups with identical attributes but different source_security_group_ids to overwrite each other in the state. See https://github.com/hashicorp/terraform/pull/2376 for more -information and instructions for recovery. Error message: %s`, sg_id, awsErr.Message()) - } - } - - return fmt.Errorf( - "Error authorizing security group rule type %s: %s", - ruleType, autherr) +information and instructions for recovery. Error: %w`, sg_id, autherr) + } + if autherr != nil { + return fmt.Errorf("Error authorizing security group rule type %s: %w", ruleType, autherr) } var rules []*ec2.IpPermission @@ -230,7 +227,7 @@ information and instructions for recovery. Error message: %s`, sg_id, awsErr.Mes log.Printf("[DEBUG] Computed group rule ID %s", id) err = resource.Retry(5*time.Minute, func() *resource.RetryError { - sg, err := findResourceSecurityGroup(conn, sg_id) + sg, err := finder.SecurityGroupByID(conn, sg_id) if err != nil { log.Printf("[DEBUG] Error finding Security Group (%s) for Rule (%s): %s", sg_id, id, err) @@ -255,9 +252,9 @@ information and instructions for recovery. Error message: %s`, sg_id, awsErr.Mes return nil }) if isResourceTimeoutError(err) { - sg, err := findResourceSecurityGroup(conn, sg_id) + sg, err := finder.SecurityGroupByID(conn, sg_id) if err != nil { - return fmt.Errorf("Error finding security group: %s", err) + return fmt.Errorf("Error finding security group: %w", err) } switch ruleType { @@ -269,7 +266,7 @@ information and instructions for recovery. Error message: %s`, sg_id, awsErr.Mes rule := findRuleMatch(perm, rules, isVPC) if rule == nil { - return fmt.Errorf("Error finding matching security group rule: %s", err) + return fmt.Errorf("Error finding matching security group rule: %w", err) } } if err != nil { @@ -283,17 +280,17 @@ information and instructions for recovery. Error message: %s`, sg_id, awsErr.Mes func resourceAwsSecurityGroupRuleRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ec2conn sg_id := d.Get("security_group_id").(string) - sg, err := findResourceSecurityGroup(conn, sg_id) - if _, notFound := err.(securityGroupNotFound); notFound { - // The security group containing this rule no longer exists. + sg, err := finder.SecurityGroupByID(conn, sg_id) + if tfresource.NotFound(err) { + log.Printf("[WARN] Security Group (%s) not found, removing Rule (%s) from state", sg_id, d.Id()) d.SetId("") return nil } if err != nil { - return fmt.Errorf("Error finding security group (%s) for rule (%s): %s", sg_id, d.Id(), err) + return fmt.Errorf("error finding Security Group (%s) for Rule (%s): %w", sg_id, d.Id(), err) } - isVPC := sg.VpcId != nil && *sg.VpcId != "" + isVPC := aws.StringValue(sg.VpcId) != "" var rule *ec2.IpPermission var rules []*ec2.IpPermission @@ -363,7 +360,7 @@ func resourceAwsSecurityGroupRuleDelete(d *schema.ResourceData, meta interface{} awsMutexKV.Lock(sg_id) defer awsMutexKV.Unlock(sg_id) - sg, err := findResourceSecurityGroup(conn, sg_id) + sg, err := finder.SecurityGroupByID(conn, sg_id) if err != nil { return err } @@ -385,14 +382,11 @@ func resourceAwsSecurityGroupRuleDelete(d *schema.ResourceData, meta interface{} _, err = conn.RevokeSecurityGroupIngress(req) if err != nil { - return fmt.Errorf( - "Error revoking security group %s rules: %s", - sg_id, err) + return fmt.Errorf("Error revoking security group %s rules: %w", sg_id, err) } case "egress": - log.Printf("[DEBUG] Revoking security group %#v %s rule: %#v", - sg_id, "egress", perm) + log.Printf("[DEBUG] Revoking security group %#v %s rule: %#v", sg_id, "egress", perm) req := &ec2.RevokeSecurityGroupEgressInput{ GroupId: sg.GroupId, IpPermissions: []*ec2.IpPermission{perm}, @@ -401,49 +395,13 @@ func resourceAwsSecurityGroupRuleDelete(d *schema.ResourceData, meta interface{} _, err = conn.RevokeSecurityGroupEgress(req) if err != nil { - return fmt.Errorf( - "Error revoking security group %s rules: %s", - sg_id, err) + return fmt.Errorf("Error revoking security group %s rules: %w", sg_id, err) } } return nil } -func findResourceSecurityGroup(conn *ec2.EC2, id string) (*ec2.SecurityGroup, error) { - req := &ec2.DescribeSecurityGroupsInput{ - GroupIds: []*string{aws.String(id)}, - } - resp, err := conn.DescribeSecurityGroups(req) - if err, ok := err.(awserr.Error); ok && err.Code() == "InvalidGroup.NotFound" { - return nil, securityGroupNotFound{id, nil} - } - if err != nil { - return nil, err - } - if resp == nil { - return nil, securityGroupNotFound{id, nil} - } - if len(resp.SecurityGroups) != 1 || resp.SecurityGroups[0] == nil { - return nil, securityGroupNotFound{id, resp.SecurityGroups} - } - - return resp.SecurityGroups[0], nil -} - -type securityGroupNotFound struct { - id string - securityGroups []*ec2.SecurityGroup -} - -func (err securityGroupNotFound) Error() string { - if err.securityGroups == nil { - return fmt.Sprintf("No security group with ID %q", err.id) - } - return fmt.Sprintf("Expected to find one security group with ID %q, got: %#v", - err.id, err.securityGroups) -} - // ByGroupPair implements sort.Interface for []*ec2.UserIDGroupPairs based on // GroupID or GroupName field (only one should be set). type ByGroupPair []*ec2.UserIdGroupPair @@ -902,7 +860,7 @@ func resourceSecurityGroupRuleDescriptionUpdate(conn *ec2.EC2, d *schema.Resourc awsMutexKV.Lock(sg_id) defer awsMutexKV.Unlock(sg_id) - sg, err := findResourceSecurityGroup(conn, sg_id) + sg, err := finder.SecurityGroupByID(conn, sg_id) if err != nil { return err } @@ -922,9 +880,7 @@ func resourceSecurityGroupRuleDescriptionUpdate(conn *ec2.EC2, d *schema.Resourc _, err = conn.UpdateSecurityGroupRuleDescriptionsIngress(req) if err != nil { - return fmt.Errorf( - "Error updating security group %s rule description: %s", - sg_id, err) + return fmt.Errorf("Error updating security group %s rule description: %w", sg_id, err) } case "egress": req := &ec2.UpdateSecurityGroupRuleDescriptionsEgressInput{ @@ -935,9 +891,7 @@ func resourceSecurityGroupRuleDescriptionUpdate(conn *ec2.EC2, d *schema.Resourc _, err = conn.UpdateSecurityGroupRuleDescriptionsEgress(req) if err != nil { - return fmt.Errorf( - "Error updating security group %s rule description: %s", - sg_id, err) + return fmt.Errorf("Error updating security group %s rule description: %w", sg_id, err) } } diff --git a/aws/resource_aws_security_group_rule_test.go b/aws/resource_aws_security_group_rule_test.go index cecb8378ebf..92f8bcab819 100644 --- a/aws/resource_aws_security_group_rule_test.go +++ b/aws/resource_aws_security_group_rule_test.go @@ -10,11 +10,12 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/ec2" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func TestIpPermissionIDHash(t *testing.T) { @@ -663,14 +664,11 @@ func TestAccAWSSecurityGroupRule_PrefixListEgress(t *testing.T) { prefixListsOutput, err := conn.DescribePrefixLists(prefixListInput) if err != nil { - _, ok := err.(awserr.Error) - if !ok { - return fmt.Errorf("Error reading VPC Endpoint prefix list: %s", err.Error()) - } + return fmt.Errorf("error reading VPC Endpoint prefix list: %w", err) } if len(prefixListsOutput.PrefixLists) != 1 { - return fmt.Errorf("There are multiple prefix lists associated with the service name '%s'. Unexpected", prefixListsOutput) + return fmt.Errorf("unexpected multiple prefix lists associated with the service: %s", prefixListsOutput) } p = ec2.IpPermission{ @@ -1066,14 +1064,11 @@ func TestAccAWSSecurityGroupRule_MultiDescription(t *testing.T) { prefixListsOutput, err := conn.DescribePrefixLists(prefixListInput) if err != nil { - _, ok := err.(awserr.Error) - if !ok { - return fmt.Errorf("Error reading VPC Endpoint prefix list: %s", err.Error()) - } + return fmt.Errorf("error reading VPC Endpoint prefix list: %w", err) } if len(prefixListsOutput.PrefixLists) != 1 { - return fmt.Errorf("There are multiple prefix lists associated with the service name '%s'. Unexpected", prefixListsOutput) + return fmt.Errorf("unexpected multiple prefix lists associated with the service: %s", prefixListsOutput) } rule4 = ec2.IpPermission{ @@ -1188,27 +1183,15 @@ func testAccCheckAWSSecurityGroupRuleDestroy(s *terraform.State) error { continue } - // Retrieve our group - req := &ec2.DescribeSecurityGroupsInput{ - GroupIds: []*string{aws.String(rs.Primary.ID)}, - } - resp, err := conn.DescribeSecurityGroups(req) - if err == nil { - if len(resp.SecurityGroups) > 0 && *resp.SecurityGroups[0].GroupId == rs.Primary.ID { - return fmt.Errorf("Security Group (%s) still exists.", rs.Primary.ID) - } - - return nil - } - - ec2err, ok := err.(awserr.Error) - if !ok { - return err + _, err := finder.SecurityGroupByID(conn, rs.Primary.ID) + if tfresource.NotFound(err) { + continue } - // Confirm error code is what we want - if ec2err.Code() != "InvalidGroup.NotFound" { + if err != nil { return err } + + return fmt.Errorf("Security Group (%s) still exists.", rs.Primary.ID) } return nil @@ -1226,20 +1209,15 @@ func testAccCheckAWSSecurityGroupRuleExists(n string, group *ec2.SecurityGroup) } conn := testAccProvider.Meta().(*AWSClient).ec2conn - req := &ec2.DescribeSecurityGroupsInput{ - GroupIds: []*string{aws.String(rs.Primary.ID)}, - } - resp, err := conn.DescribeSecurityGroups(req) + + sg, err := finder.SecurityGroupByID(conn, rs.Primary.ID) if err != nil { return err } - if len(resp.SecurityGroups) > 0 && *resp.SecurityGroups[0].GroupId == rs.Primary.ID { - *group = *resp.SecurityGroups[0] - return nil - } + *group = *sg - return fmt.Errorf("Security Group not found") + return nil } } diff --git a/aws/resource_aws_security_group_test.go b/aws/resource_aws_security_group_test.go index b0a97c8a0c4..e85547ba1da 100644 --- a/aws/resource_aws_security_group_test.go +++ b/aws/resource_aws_security_group_test.go @@ -12,14 +12,14 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/ec2" - "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "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/terraform" "github.com/terraform-providers/terraform-provider-aws/aws/internal/naming" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) // add sweeper to delete known test sgs @@ -36,7 +36,7 @@ func init() { func testSweepSecurityGroups(region string) error { client, err := sharedClientForRegion(region) if err != nil { - return fmt.Errorf("error getting client: %s", err) + return fmt.Errorf("error getting client: %w", err) } conn := client.(*AWSClient).ec2conn @@ -82,7 +82,7 @@ func testSweepSecurityGroups(region string) error { } if err != nil { - return fmt.Errorf("Error retrieving EC2 Security Groups: %s", err) + return fmt.Errorf("Error retrieving EC2 Security Groups: %w", err) } err = conn.DescribeSecurityGroupsPages(input, func(page *ec2.DescribeSecurityGroupsOutput, lastPage bool) bool { @@ -118,7 +118,7 @@ func testSweepSecurityGroups(region string) error { }) if err != nil { - return fmt.Errorf("Error retrieving EC2 Security Groups: %s", err) + return fmt.Errorf("Error retrieving EC2 Security Groups: %w", err) } return nil @@ -2098,15 +2098,11 @@ func testAddRuleCycle(primary, secondary *ec2.SecurityGroup) resource.TestCheckF var err error _, err = conn.AuthorizeSecurityGroupEgress(req1) if err != nil { - return fmt.Errorf( - "Error authorizing primary security group %s rules: %s", *primary.GroupId, - err) + return fmt.Errorf("Error authorizing primary security group %s rules: %w", aws.StringValue(primary.GroupId), err) } _, err = conn.AuthorizeSecurityGroupEgress(req2) if err != nil { - return fmt.Errorf( - "Error authorizing secondary security group %s rules: %s", *secondary.GroupId, - err) + return fmt.Errorf("Error authorizing secondary security group %s rules: %w", aws.StringValue(secondary.GroupId), err) } return nil } @@ -2133,9 +2129,7 @@ func testRemoveRuleCycle(primary, secondary *ec2.SecurityGroup) resource.TestChe } if _, err = conn.RevokeSecurityGroupIngress(req); err != nil { - return fmt.Errorf( - "Error revoking default ingress rule for Security Group in testRemoveCycle (%s): %s", - *primary.GroupId, err) + return fmt.Errorf("Error revoking default ingress rule for Security Group in testRemoveCycle (%s): %w", aws.StringValue(primary.GroupId), err) } } @@ -2146,9 +2140,7 @@ func testRemoveRuleCycle(primary, secondary *ec2.SecurityGroup) resource.TestChe } if _, err = conn.RevokeSecurityGroupEgress(req); err != nil { - return fmt.Errorf( - "Error revoking default egress rule for Security Group in testRemoveCycle (%s): %s", - *sg.GroupId, err) + return fmt.Errorf("Error revoking default egress rule for Security Group in testRemoveCycle (%s): %w", aws.StringValue(sg.GroupId), err) } } } @@ -2164,27 +2156,15 @@ func testAccCheckAWSSecurityGroupDestroy(s *terraform.State) error { continue } - // Retrieve our group - req := &ec2.DescribeSecurityGroupsInput{ - GroupIds: []*string{aws.String(rs.Primary.ID)}, - } - resp, err := conn.DescribeSecurityGroups(req) - if err == nil { - if len(resp.SecurityGroups) > 0 && *resp.SecurityGroups[0].GroupId == rs.Primary.ID { - return fmt.Errorf("Security Group (%s) still exists.", rs.Primary.ID) - } - - return nil - } - - ec2err, ok := err.(awserr.Error) - if !ok { - return err + _, err := finder.SecurityGroupByID(conn, rs.Primary.ID) + if tfresource.NotFound(err) { + continue } - // Confirm error code is what we want - if ec2err.Code() != "InvalidGroup.NotFound" { + if err != nil { return err } + + return fmt.Errorf("Security Group (%s) still exists.", rs.Primary.ID) } return nil @@ -2198,25 +2178,15 @@ func testAccCheckAWSSecurityGroupEc2ClassicDestroy(s *terraform.State) error { continue } - input := &ec2.DescribeSecurityGroupsInput{ - GroupIds: []*string{aws.String(rs.Primary.ID)}, - } - - output, err := conn.DescribeSecurityGroups(input) - - if tfawserr.ErrCodeEquals(err, "InvalidGroup.NotFound") { + _, err := finder.SecurityGroupByID(conn, rs.Primary.ID) + if tfresource.NotFound(err) { continue } - if err != nil { - return fmt.Errorf("error describing EC2 Security Group (%s): %w", rs.Primary.ID, err) + return err } - for _, sg := range output.SecurityGroups { - if aws.StringValue(sg.GroupId) == rs.Primary.ID { - return fmt.Errorf("EC2 Security Group (%s) still exists", rs.Primary.ID) - } - } + return fmt.Errorf("Security Group (%s) still exists.", rs.Primary.ID) } return nil @@ -2234,20 +2204,18 @@ func testAccCheckAWSSecurityGroupExists(n string, group *ec2.SecurityGroup) reso } conn := testAccProvider.Meta().(*AWSClient).ec2conn - req := &ec2.DescribeSecurityGroupsInput{ - GroupIds: []*string{aws.String(rs.Primary.ID)}, + + sg, err := finder.SecurityGroupByID(conn, rs.Primary.ID) + if tfresource.NotFound(err) { + return fmt.Errorf("Security Group (%s) not found: %w", rs.Primary.ID, err) } - resp, err := conn.DescribeSecurityGroups(req) if err != nil { return err } - if len(resp.SecurityGroups) > 0 && *resp.SecurityGroups[0].GroupId == rs.Primary.ID { - *group = *resp.SecurityGroups[0] - return nil - } + *group = *sg - return fmt.Errorf("Security Group not found") + return nil } } @@ -2264,24 +2232,17 @@ func testAccCheckAWSSecurityGroupEc2ClassicExists(n string, group *ec2.SecurityG conn := testAccProviderEc2Classic.Meta().(*AWSClient).ec2conn - input := &ec2.DescribeSecurityGroupsInput{ - GroupIds: []*string{aws.String(rs.Primary.ID)}, + sg, err := finder.SecurityGroupByID(conn, rs.Primary.ID) + if tfresource.NotFound(err) { + return fmt.Errorf("Security Group (%s) not found: %w", rs.Primary.ID, err) } - - output, err := conn.DescribeSecurityGroups(input) - if err != nil { - return fmt.Errorf("error describing EC2 Security Group (%s): %w", rs.Primary.ID, err) + return err } - for _, sg := range output.SecurityGroups { - if aws.StringValue(sg.GroupId) == rs.Primary.ID { - *group = *sg - return nil - } - } + *group = *sg - return fmt.Errorf("EC2 Security Group (%s) not found", rs.Primary.ID) + return nil } } @@ -2533,20 +2494,17 @@ func testAccCheckAWSSecurityGroupExistsWithoutDefault(n string) resource.TestChe } conn := testAccProvider.Meta().(*AWSClient).ec2conn - req := &ec2.DescribeSecurityGroupsInput{ - GroupIds: []*string{aws.String(rs.Primary.ID)}, + + group, err := finder.SecurityGroupByID(conn, rs.Primary.ID) + if tfresource.NotFound(err) { + return fmt.Errorf("Security Group (%s) not found: %w", rs.Primary.ID, err) } - resp, err := conn.DescribeSecurityGroups(req) if err != nil { return err } - if len(resp.SecurityGroups) > 0 && *resp.SecurityGroups[0].GroupId == rs.Primary.ID { - group := *resp.SecurityGroups[0] - - if len(group.IpPermissionsEgress) != 1 { - return fmt.Errorf("Security Group should have only 1 egress rule, got %d", len(group.IpPermissionsEgress)) - } + if len(group.IpPermissionsEgress) != 1 { + return fmt.Errorf("Security Group should have only 1 egress rule, got %d", len(group.IpPermissionsEgress)) } return nil @@ -2657,26 +2615,18 @@ func TestAccAWSSecurityGroup_ruleLimitCidrBlockExceededAppend(t *testing.T) { t.Fatalf("PreConfig check failed: %s", err) } - id := *group.GroupId + id := aws.StringValue(group.GroupId) conn := testAccProvider.Meta().(*AWSClient).ec2conn - req := &ec2.DescribeSecurityGroupsInput{ - GroupIds: []*string{aws.String(id)}, + + match, err := finder.SecurityGroupByID(conn, id) + if tfresource.NotFound(err) { + t.Fatalf("PreConfig check failed: Security Group (%s) not found: %s", id, err) } - resp, err := conn.DescribeSecurityGroups(req) if err != nil { t.Fatalf("PreConfig check failed: %s", err) } - var match *ec2.SecurityGroup - if len(resp.SecurityGroups) > 0 && *resp.SecurityGroups[0].GroupId == id { - match = resp.SecurityGroups[0] - } - - if match == nil { - t.Fatalf("PreConfig check failed: security group %s not found", id) - } - if cidrCount := len(match.IpPermissionsEgress[0].IpRanges); cidrCount != ruleLimit { t.Fatalf("PreConfig check failed: rule does not have previous IP ranges, has %d", cidrCount) } @@ -2823,23 +2773,15 @@ func testAccCheckAWSSecurityGroupRuleCount(group *ec2.SecurityGroup, expectedIng func testSecurityGroupRuleCount(id string, expectedIngressCount, expectedEgressCount int) error { conn := testAccProvider.Meta().(*AWSClient).ec2conn - req := &ec2.DescribeSecurityGroupsInput{ - GroupIds: []*string{aws.String(id)}, + + group, err := finder.SecurityGroupByID(conn, id) + if tfresource.NotFound(err) { + return fmt.Errorf("Security Group (%s) not found: %w", id, err) } - resp, err := conn.DescribeSecurityGroups(req) if err != nil { return err } - var group *ec2.SecurityGroup - if len(resp.SecurityGroups) > 0 && *resp.SecurityGroups[0].GroupId == id { - group = resp.SecurityGroups[0] - } - - if group == nil { - return fmt.Errorf("Security group %s not found", id) - } - if actual := len(group.IpPermissions); actual != expectedIngressCount { return fmt.Errorf("Security group ingress rule count %d does not match %d", actual, expectedIngressCount) } From 715821ab517d068f7b9f6a0dda242d2b314c3c03 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Tue, 27 Apr 2021 16:51:09 -0700 Subject: [PATCH 345/398] Updates function comments --- aws/internal/service/ec2/finder/finder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/internal/service/ec2/finder/finder.go b/aws/internal/service/ec2/finder/finder.go index cbf273af144..ec9f498a232 100644 --- a/aws/internal/service/ec2/finder/finder.go +++ b/aws/internal/service/ec2/finder/finder.go @@ -407,7 +407,6 @@ func RouteByPrefixListIDDestination(conn *ec2.EC2, routeTableID, prefixListID st return nil, &resource.NotFoundError{} } -// SecurityGroupByID looks up a security group by ID. When not found, returns nil and potentially an API error. // SecurityGroupByID looks up a security group by ID. Returns a resource.NotFoundError if not found. func SecurityGroupByID(conn *ec2.EC2, id string) (*ec2.SecurityGroup, error) { input := &ec2.DescribeSecurityGroupsInput{ @@ -429,6 +428,7 @@ func SecurityGroupByNameAndVpcID(conn *ec2.EC2, name, vpcID string) (*ec2.Securi return SecurityGroup(conn, input) } +// SecurityGroup looks up a security group using an ec2.DescribeSecurityGroupsInput. Returns a resource.NotFoundError if not found. func SecurityGroup(conn *ec2.EC2, input *ec2.DescribeSecurityGroupsInput) (*ec2.SecurityGroup, error) { result, err := conn.DescribeSecurityGroups(input) if tfawserr.ErrCodeEquals(err, tfec2.InvalidSecurityGroupIDNotFound) || From b698e692963bde8b7c01ef827899ab6b6695b75c Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 12 Jul 2021 14:20:44 -0400 Subject: [PATCH 346/398] Correct error message for 'TestAccAWSSecurityGroup_rulesDropOnError'. --- aws/resource_aws_security_group_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_security_group_test.go b/aws/resource_aws_security_group_test.go index e85547ba1da..f3f97df2226 100644 --- a/aws/resource_aws_security_group_test.go +++ b/aws/resource_aws_security_group_test.go @@ -2753,7 +2753,7 @@ func TestAccAWSSecurityGroup_rulesDropOnError(t *testing.T) { // Add a bad rule to trigger API error { Config: testAccAWSSecurityGroupConfig_rulesDropOnError_AddBadRule, - ExpectError: regexp.MustCompile("InvalidGroupId.Malformed"), + ExpectError: regexp.MustCompile("InvalidGroup.NotFound"), }, // All originally added rules must survive. This will return non-empty plan if anything changed. { From 88d36d052269e6cfa71e36e83222630ae28b7192 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Mon, 12 Jul 2021 20:56:32 -0400 Subject: [PATCH 347/398] docs/r/wafv2_web_acl: Add to docs --- website/docs/r/wafv2_web_acl.html.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/website/docs/r/wafv2_web_acl.html.markdown b/website/docs/r/wafv2_web_acl.html.markdown index d7917c0c7d3..e7ea1dc9177 100644 --- a/website/docs/r/wafv2_web_acl.html.markdown +++ b/website/docs/r/wafv2_web_acl.html.markdown @@ -416,6 +416,7 @@ The `managed_rule_group_statement` block supports the following arguments: * `excluded_rule` - (Optional) The `rules` whose actions are set to `COUNT` by the web ACL, regardless of the action that is set on the rule. See [Excluded Rule](#excluded-rule) below for details. * `name` - (Required) The name of the managed rule group. +* `scope_down_statement` - Narrows the scope of the statement to matching web requests. This can be any nestable statement, and you can nest statements at any level below this scope-down statement. See [Statement](#statement) above for details. * `vendor_name` - (Required) The name of the managed rule group vendor. ### NOT Statement From 3516ed7aaecbc91439446e27201a36036a372514 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Mon, 12 Jul 2021 20:57:05 -0400 Subject: [PATCH 348/398] tests/r/wafv2_web_acl: Clean up tests --- aws/resource_aws_wafv2_web_acl_test.go | 206 ++++++++++++------------- 1 file changed, 103 insertions(+), 103 deletions(-) diff --git a/aws/resource_aws_wafv2_web_acl_test.go b/aws/resource_aws_wafv2_web_acl_test.go index 84ba4161547..dff17df08fb 100644 --- a/aws/resource_aws_wafv2_web_acl_test.go +++ b/aws/resource_aws_wafv2_web_acl_test.go @@ -129,7 +129,7 @@ func TestAccAwsWafv2WebACL_basic(t *testing.T) { }) } -func TestAccAwsWafv2WebACL_updateRule(t *testing.T) { +func TestAccAwsWafv2WebACL_Update_rule(t *testing.T) { var v wafv2.WebACL webACLName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_wafv2_web_acl.test" @@ -260,7 +260,7 @@ func TestAccAwsWafv2WebACL_updateRule(t *testing.T) { }) } -func TestAccAwsWafv2WebACL_UpdateRuleProperties(t *testing.T) { +func TestAccAwsWafv2WebACL_Update_ruleProperties(t *testing.T) { var v wafv2.WebACL webACLName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_wafv2_web_acl.test" @@ -466,7 +466,7 @@ func TestAccAwsWafv2WebACL_UpdateRuleProperties(t *testing.T) { }) } -func TestAccAwsWafv2WebACL_ChangeNameForceNew(t *testing.T) { +func TestAccAwsWafv2WebACL_Update_nameForceNew(t *testing.T) { var before, after wafv2.WebACL webACLName := acctest.RandomWithPrefix("tf-acc-test") ruleGroupNewName := acctest.RandomWithPrefix("tf-acc-test") @@ -518,7 +518,7 @@ func TestAccAwsWafv2WebACL_ChangeNameForceNew(t *testing.T) { }) } -func TestAccAwsWafv2WebACL_Disappears(t *testing.T) { +func TestAccAwsWafv2WebACL_disappears(t *testing.T) { var v wafv2.WebACL webACLName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_wafv2_web_acl.test" @@ -541,7 +541,7 @@ func TestAccAwsWafv2WebACL_Disappears(t *testing.T) { }) } -func TestAccAwsWafv2WebACL_ManagedRuleGroupStatement(t *testing.T) { +func TestAccAwsWafv2WebACL_managedRuleGroup(t *testing.T) { var v wafv2.WebACL webACLName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_wafv2_web_acl.test" @@ -575,7 +575,7 @@ func TestAccAwsWafv2WebACL_ManagedRuleGroupStatement(t *testing.T) { ), }, { - Config: testAccAwsWafv2WebACLConfig_ManagedRuleGroupStatement_Update(webACLName), + Config: testAccAwsWafv2WebACLConfig_ManagedRuleGroupStatement_update(webACLName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsWafv2WebACLExists(resourceName, &v), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), @@ -612,7 +612,7 @@ func TestAccAwsWafv2WebACL_ManagedRuleGroupStatement(t *testing.T) { }) } -func TestAccAwsWafv2WebACL_Minimal(t *testing.T) { +func TestAccAwsWafv2WebACL_minimal(t *testing.T) { var v wafv2.WebACL webACLName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_wafv2_web_acl.test" @@ -645,7 +645,7 @@ func TestAccAwsWafv2WebACL_Minimal(t *testing.T) { }) } -func TestAccAwsWafv2WebACL_RateBasedStatement(t *testing.T) { +func TestAccAwsWafv2WebACL_RateBased_basic(t *testing.T) { var v wafv2.WebACL webACLName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_wafv2_web_acl.test" @@ -679,7 +679,7 @@ func TestAccAwsWafv2WebACL_RateBasedStatement(t *testing.T) { ), }, { - Config: testAccAwsWafv2WebACLConfig_RateBasedStatement_Update(webACLName), + Config: testAccAwsWafv2WebACLConfig_RateBasedStatement_update(webACLName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsWafv2WebACLExists(resourceName, &v), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), @@ -714,7 +714,7 @@ func TestAccAwsWafv2WebACL_RateBasedStatement(t *testing.T) { }) } -func TestAccAwsWafv2WebACL_GeoMatchStatement(t *testing.T) { +func TestAccAwsWafv2WebACL_GeoMatch_basic(t *testing.T) { var v wafv2.WebACL webACLName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_wafv2_web_acl.test" @@ -806,7 +806,7 @@ func TestAccAwsWafv2WebACL_GeoMatchStatement(t *testing.T) { }) } -func TestAccAwsWafv2WebACL_GeoMatchStatement_ForwardedIPConfig(t *testing.T) { +func TestAccAwsWafv2WebACL_GeoMatch_forwardedIPConfig(t *testing.T) { var v wafv2.WebACL webACLName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_wafv2_web_acl.test" @@ -818,7 +818,7 @@ func TestAccAwsWafv2WebACL_GeoMatchStatement_ForwardedIPConfig(t *testing.T) { CheckDestroy: testAccCheckAwsWafv2WebACLDestroy, Steps: []resource.TestStep{ { - Config: testAccAwsWafv2WebACLConfig_GeoMatchStatement_ForwardedIPConfig(webACLName, "MATCH", "X-Forwarded-For"), + Config: testAccAwsWafv2WebACLConfig_GeoMatchStatement_forwardedIPConfig(webACLName, "MATCH", "X-Forwarded-For"), Check: resource.ComposeTestCheckFunc( testAccCheckAwsWafv2WebACLExists(resourceName, &v), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), @@ -840,7 +840,7 @@ func TestAccAwsWafv2WebACL_GeoMatchStatement_ForwardedIPConfig(t *testing.T) { ), }, { - Config: testAccAwsWafv2WebACLConfig_GeoMatchStatement_ForwardedIPConfig(webACLName, "NO_MATCH", "Updated"), + Config: testAccAwsWafv2WebACLConfig_GeoMatchStatement_forwardedIPConfig(webACLName, "NO_MATCH", "Updated"), Check: resource.ComposeTestCheckFunc( testAccCheckAwsWafv2WebACLExists(resourceName, &v), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), @@ -871,7 +871,7 @@ func TestAccAwsWafv2WebACL_GeoMatchStatement_ForwardedIPConfig(t *testing.T) { }) } -func TestAccAwsWafv2WebACL_IPSetReferenceStatement(t *testing.T) { +func TestAccAwsWafv2WebACL_IPSetReference_basic(t *testing.T) { var v wafv2.WebACL webACLName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_wafv2_web_acl.test" @@ -883,7 +883,7 @@ func TestAccAwsWafv2WebACL_IPSetReferenceStatement(t *testing.T) { CheckDestroy: testAccCheckAwsWafv2WebACLDestroy, Steps: []resource.TestStep{ { - Config: testAccAwsWafv2WebACLConfig_IPSetReferenceStatement(webACLName), + Config: testAccAwsWafv2WebACLConfig_IPSetReference(webACLName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsWafv2WebACLExists(resourceName, &v), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), @@ -917,7 +917,7 @@ func TestAccAwsWafv2WebACL_IPSetReferenceStatement(t *testing.T) { }) } -func TestAccAwsWafv2WebACL_IPSetReferenceStatement_IPSetForwardedIPConfig(t *testing.T) { +func TestAccAwsWafv2WebACL_IPSetReference_forwardedIPConfig(t *testing.T) { var v wafv2.WebACL webACLName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_wafv2_web_acl.test" @@ -929,7 +929,7 @@ func TestAccAwsWafv2WebACL_IPSetReferenceStatement_IPSetForwardedIPConfig(t *tes CheckDestroy: testAccCheckAwsWafv2WebACLDestroy, Steps: []resource.TestStep{ { - Config: testAccAwsWafv2WebACLConfig_IPSetReferenceStatement_IPSetForwardedIPConfig(webACLName, "MATCH", "X-Forwarded-For", "FIRST"), + Config: testAccAwsWafv2WebACLConfig_IPSetReference_forwardedIPConfig(webACLName, "MATCH", "X-Forwarded-For", "FIRST"), Check: resource.ComposeTestCheckFunc( testAccCheckAwsWafv2WebACLExists(resourceName, &v), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), @@ -951,7 +951,7 @@ func TestAccAwsWafv2WebACL_IPSetReferenceStatement_IPSetForwardedIPConfig(t *tes ), }, { - Config: testAccAwsWafv2WebACLConfig_IPSetReferenceStatement_IPSetForwardedIPConfig(webACLName, "NO_MATCH", "X-Forwarded-For", "LAST"), + Config: testAccAwsWafv2WebACLConfig_IPSetReference_forwardedIPConfig(webACLName, "NO_MATCH", "X-Forwarded-For", "LAST"), Check: resource.ComposeTestCheckFunc( testAccCheckAwsWafv2WebACLExists(resourceName, &v), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), @@ -973,7 +973,7 @@ func TestAccAwsWafv2WebACL_IPSetReferenceStatement_IPSetForwardedIPConfig(t *tes ), }, { - Config: testAccAwsWafv2WebACLConfig_IPSetReferenceStatement_IPSetForwardedIPConfig(webACLName, "MATCH", "Updated", "ANY"), + Config: testAccAwsWafv2WebACLConfig_IPSetReference_forwardedIPConfig(webACLName, "MATCH", "Updated", "ANY"), Check: resource.ComposeTestCheckFunc( testAccCheckAwsWafv2WebACLExists(resourceName, &v), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), @@ -995,7 +995,7 @@ func TestAccAwsWafv2WebACL_IPSetReferenceStatement_IPSetForwardedIPConfig(t *tes ), }, { - Config: testAccAwsWafv2WebACLConfig_IPSetReferenceStatement(webACLName), + Config: testAccAwsWafv2WebACLConfig_IPSetReference(webACLName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsWafv2WebACLExists(resourceName, &v), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), @@ -1021,7 +1021,7 @@ func TestAccAwsWafv2WebACL_IPSetReferenceStatement_IPSetForwardedIPConfig(t *tes }) } -func TestAccAwsWafv2WebACL_RateBasedStatement_ForwardedIPConfig(t *testing.T) { +func TestAccAwsWafv2WebACL_RateBased_forwardedIPConfig(t *testing.T) { var v wafv2.WebACL webACLName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_wafv2_web_acl.test" @@ -1033,7 +1033,7 @@ func TestAccAwsWafv2WebACL_RateBasedStatement_ForwardedIPConfig(t *testing.T) { CheckDestroy: testAccCheckAwsWafv2WebACLDestroy, Steps: []resource.TestStep{ { - Config: testAccAwsWafv2WebACLConfig_RateBasedStatement_ForwardedIPConfig(webACLName, "MATCH", "X-Forwarded-For"), + Config: testAccAwsWafv2WebACLConfig_RateBasedStatement_forwardedIPConfig(webACLName, "MATCH", "X-Forwarded-For"), Check: resource.ComposeTestCheckFunc( testAccCheckAwsWafv2WebACLExists(resourceName, &v), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), @@ -1057,7 +1057,7 @@ func TestAccAwsWafv2WebACL_RateBasedStatement_ForwardedIPConfig(t *testing.T) { ), }, { - Config: testAccAwsWafv2WebACLConfig_RateBasedStatement_ForwardedIPConfig(webACLName, "NO_MATCH", "Updated"), + Config: testAccAwsWafv2WebACLConfig_RateBasedStatement_forwardedIPConfig(webACLName, "NO_MATCH", "Updated"), Check: resource.ComposeTestCheckFunc( testAccCheckAwsWafv2WebACLExists(resourceName, &v), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), @@ -1090,7 +1090,7 @@ func TestAccAwsWafv2WebACL_RateBasedStatement_ForwardedIPConfig(t *testing.T) { }) } -func TestAccAwsWafv2WebACL_RuleGroupReferenceStatement(t *testing.T) { +func TestAccAwsWafv2WebACL_RuleGroupReference_basic(t *testing.T) { var v wafv2.WebACL webACLName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_wafv2_web_acl.test" @@ -1123,7 +1123,7 @@ func TestAccAwsWafv2WebACL_RuleGroupReferenceStatement(t *testing.T) { ), }, { - Config: testAccAwsWafv2WebACLConfig_RuleGroupReferenceStatement_Update(webACLName), + Config: testAccAwsWafv2WebACLConfig_RuleGroupReferenceStatement_update(webACLName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsWafv2WebACLExists(resourceName, &v), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), @@ -1155,7 +1155,7 @@ func TestAccAwsWafv2WebACL_RuleGroupReferenceStatement(t *testing.T) { }) } -func TestAccAwsWafv2WebACL_CustomRequestHandling(t *testing.T) { +func TestAccAwsWafv2WebACL_Custom_requestHandling(t *testing.T) { var v wafv2.WebACL webACLName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_wafv2_web_acl.test" @@ -1167,7 +1167,7 @@ func TestAccAwsWafv2WebACL_CustomRequestHandling(t *testing.T) { CheckDestroy: testAccCheckAwsWafv2WebACLDestroy, Steps: []resource.TestStep{ { - Config: testAccAwsWafv2WebACLConfig_CustomRequestHandling_Allow(webACLName, "x-hdr1", "x-hdr2"), + Config: testAccAwsWafv2WebACLConfig_CustomRequestHandling_allow(webACLName, "x-hdr1", "x-hdr2"), Check: resource.ComposeTestCheckFunc( testAccCheckAwsWafv2WebACLExists(resourceName, &v), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), @@ -1198,7 +1198,7 @@ func TestAccAwsWafv2WebACL_CustomRequestHandling(t *testing.T) { ), }, { - Config: testAccAwsWafv2WebACLConfig_CustomRequestHandling_Count(webACLName, "x-hdr1", "x-hdr2"), + Config: testAccAwsWafv2WebACLConfig_CustomRequestHandling_count(webACLName, "x-hdr1", "x-hdr2"), Check: resource.ComposeTestCheckFunc( testAccCheckAwsWafv2WebACLExists(resourceName, &v), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), @@ -1238,7 +1238,7 @@ func TestAccAwsWafv2WebACL_CustomRequestHandling(t *testing.T) { }) } -func TestAccAwsWafv2WebACL_CustomResponse(t *testing.T) { +func TestAccAwsWafv2WebACL_Custom_response(t *testing.T) { var v wafv2.WebACL webACLName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_wafv2_web_acl.test" @@ -1323,7 +1323,7 @@ func TestAccAwsWafv2WebACL_CustomResponse(t *testing.T) { }) } -func TestAccAwsWafv2WebACL_Tags(t *testing.T) { +func TestAccAwsWafv2WebACL_tags(t *testing.T) { var v wafv2.WebACL webACLName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_wafv2_web_acl.test" @@ -1373,7 +1373,7 @@ func TestAccAwsWafv2WebACL_Tags(t *testing.T) { } // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/13862 -func TestAccAwsWafv2WebACL_MaxNestedRateBasedStatements(t *testing.T) { +func TestAccAwsWafv2WebACL_RateBased_maxNested(t *testing.T) { var v wafv2.WebACL webACLName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_wafv2_web_acl.test" @@ -1416,7 +1416,7 @@ func TestAccAwsWafv2WebACL_MaxNestedRateBasedStatements(t *testing.T) { }) } -func TestAccAwsWafv2WebACL_MaxNestedOperatorStatements(t *testing.T) { +func TestAccAwsWafv2WebACL_Operators_maxNested(t *testing.T) { var v wafv2.WebACL webACLName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_wafv2_web_acl.test" @@ -1531,8 +1531,8 @@ func testAccCheckAwsWafv2WebACLExists(n string, v *wafv2.WebACL) resource.TestCh func testAccAwsWafv2WebACLConfig_Basic(name string) string { return fmt.Sprintf(` resource "aws_wafv2_web_acl" "test" { - name = "%[1]s" - description = "%[1]s" + name = %[1]q + description = %[1]q scope = "REGIONAL" default_action { @@ -1551,7 +1551,7 @@ resource "aws_wafv2_web_acl" "test" { func testAccAwsWafv2WebACLConfig_BasicRule(name string) string { return fmt.Sprintf(` resource "aws_wafv2_web_acl" "test" { - name = "%[1]s" + name = %[1]q description = "Updated" scope = "REGIONAL" @@ -1607,7 +1607,7 @@ resource "aws_wafv2_web_acl" "test" { func testAccAwsWafv2WebACLConfig_UpdateRuleNamePriorityMetric(name, ruleName1, ruleName2 string, priority1, priority2 int) string { return fmt.Sprintf(` resource "aws_wafv2_web_acl" "test" { - name = "%[1]s" + name = %[1]q description = "Updated" scope = "REGIONAL" @@ -1616,7 +1616,7 @@ resource "aws_wafv2_web_acl" "test" { } rule { - name = "%[2]s" + name = %[2]q priority = %[3]d action { @@ -1646,13 +1646,13 @@ resource "aws_wafv2_web_acl" "test" { visibility_config { cloudwatch_metrics_enabled = false - metric_name = "%[2]s" + metric_name = %[2]q sampled_requests_enabled = false } } rule { - name = "%[4]s" + name = %[4]q priority = %[5]d action { @@ -1667,7 +1667,7 @@ resource "aws_wafv2_web_acl" "test" { visibility_config { cloudwatch_metrics_enabled = false - metric_name = "%[4]s" + metric_name = %[4]q sampled_requests_enabled = false } } @@ -1684,8 +1684,8 @@ resource "aws_wafv2_web_acl" "test" { func testAccAwsWafv2WebACLConfig_GeoMatchStatement(name, countryCodes string) string { return fmt.Sprintf(` resource "aws_wafv2_web_acl" "test" { - name = "%[1]s" - description = "%[1]s" + name = %[1]q + description = %[1]q scope = "REGIONAL" default_action { @@ -1727,11 +1727,11 @@ resource "aws_wafv2_web_acl" "test" { `, name, countryCodes) } -func testAccAwsWafv2WebACLConfig_CustomRequestHandling_Count(name, firstHeader string, secondHeader string) string { +func testAccAwsWafv2WebACLConfig_CustomRequestHandling_count(name, firstHeader string, secondHeader string) string { return fmt.Sprintf(` resource "aws_wafv2_web_acl" "test" { - name = "%[1]s" - description = "%[1]s" + name = %[1]q + description = %[1]q scope = "REGIONAL" default_action { @@ -1746,12 +1746,12 @@ resource "aws_wafv2_web_acl" "test" { count { custom_request_handling { insert_header { - name = "%[2]s" + name = %[2]q value = "test-value-1" } insert_header { - name = "%[3]s" + name = %[3]q value = "test-value-2" } } @@ -1780,11 +1780,11 @@ resource "aws_wafv2_web_acl" "test" { `, name, firstHeader, secondHeader) } -func testAccAwsWafv2WebACLConfig_CustomRequestHandling_Allow(name, firstHeader string, secondHeader string) string { +func testAccAwsWafv2WebACLConfig_CustomRequestHandling_allow(name, firstHeader string, secondHeader string) string { return fmt.Sprintf(` resource "aws_wafv2_web_acl" "test" { - name = "%[1]s" - description = "%[1]s" + name = %[1]q + description = %[1]q scope = "REGIONAL" default_action { @@ -1799,12 +1799,12 @@ resource "aws_wafv2_web_acl" "test" { allow { custom_request_handling { insert_header { - name = "%[2]s" + name = %[2]q value = "test-value-1" } insert_header { - name = "%[3]s" + name = %[3]q value = "test-value-2" } } @@ -1836,8 +1836,8 @@ resource "aws_wafv2_web_acl" "test" { func testAccAwsWafv2WebACLConfig_CustomResponse(name string, defaultStatusCode int, countryBlockStatusCode int, countryHeaderName string) string { return fmt.Sprintf(` resource "aws_wafv2_web_acl" "test" { - name = "%[1]s" - description = "%[1]s" + name = %[1]q + description = %[1]q scope = "REGIONAL" default_action { @@ -1858,7 +1858,7 @@ resource "aws_wafv2_web_acl" "test" { response_code = %[3]d response_header { - name = "%[4]s" + name = %[4]q value = "custom-response-header-value" } } @@ -1887,11 +1887,11 @@ resource "aws_wafv2_web_acl" "test" { `, name, defaultStatusCode, countryBlockStatusCode, countryHeaderName) } -func testAccAwsWafv2WebACLConfig_GeoMatchStatement_ForwardedIPConfig(name, fallbackBehavior, headerName string) string { +func testAccAwsWafv2WebACLConfig_GeoMatchStatement_forwardedIPConfig(name, fallbackBehavior, headerName string) string { return fmt.Sprintf(` resource "aws_wafv2_web_acl" "test" { - name = "%[1]s" - description = "%[1]s" + name = %[1]q + description = %[1]q scope = "REGIONAL" default_action { @@ -1918,8 +1918,8 @@ resource "aws_wafv2_web_acl" "test" { geo_match_statement { country_codes = ["CA"] forwarded_ip_config { - fallback_behavior = "%s" - header_name = "%s" + fallback_behavior = %[2]q + header_name = %[3]q } } } @@ -1942,7 +1942,7 @@ resource "aws_wafv2_web_acl" "test" { `, name, fallbackBehavior, headerName) } -func testAccAwsWafv2WebACLConfig_IPSetReferenceStatement(name string) string { +func testAccAwsWafv2WebACLConfig_IPSetReference(name string) string { return fmt.Sprintf(` resource "aws_wafv2_ip_set" "test" { name = "ip-set-%[1]s" @@ -1952,8 +1952,8 @@ resource "aws_wafv2_ip_set" "test" { } resource "aws_wafv2_web_acl" "test" { - name = "%[1]s" - description = "%[1]s" + name = %[1]q + description = %[1]q scope = "REGIONAL" default_action { @@ -1990,7 +1990,7 @@ resource "aws_wafv2_web_acl" "test" { `, name) } -func testAccAwsWafv2WebACLConfig_IPSetReferenceStatement_IPSetForwardedIPConfig(name, fallbackBehavior, headerName, position string) string { +func testAccAwsWafv2WebACLConfig_IPSetReference_forwardedIPConfig(name, fallbackBehavior, headerName, position string) string { return fmt.Sprintf(` resource "aws_wafv2_ip_set" "test" { name = "ip-set-%[1]s" @@ -2000,8 +2000,8 @@ resource "aws_wafv2_ip_set" "test" { } resource "aws_wafv2_web_acl" "test" { - name = "%[1]s" - description = "%[1]s" + name = %[1]q + description = %[1]q scope = "REGIONAL" default_action { @@ -2020,9 +2020,9 @@ resource "aws_wafv2_web_acl" "test" { ip_set_reference_statement { arn = aws_wafv2_ip_set.test.arn ip_set_forwarded_ip_config { - fallback_behavior = "%[2]s" - header_name = "%[3]s" - position = "%[4]s" + fallback_behavior = %[2]q + header_name = %[3]q + position = %[4]q } } } @@ -2046,8 +2046,8 @@ resource "aws_wafv2_web_acl" "test" { func testAccAwsWafv2WebACLConfig_ManagedRuleGroupStatement(name string) string { return fmt.Sprintf(` resource "aws_wafv2_web_acl" "test" { - name = "%[1]s" - description = "%[1]s" + name = %[1]q + description = %[1]q scope = "REGIONAL" default_action { @@ -2090,11 +2090,11 @@ resource "aws_wafv2_web_acl" "test" { `, name) } -func testAccAwsWafv2WebACLConfig_ManagedRuleGroupStatement_Update(name string) string { +func testAccAwsWafv2WebACLConfig_ManagedRuleGroupStatement_update(name string) string { return fmt.Sprintf(` resource "aws_wafv2_web_acl" "test" { - name = "%[1]s" - description = "%[1]s" + name = %[1]q + description = %[1]q scope = "REGIONAL" default_action { @@ -2154,8 +2154,8 @@ resource "aws_wafv2_web_acl" "test" { func testAccAwsWafv2WebACLConfig_RateBasedStatement(name string) string { return fmt.Sprintf(` resource "aws_wafv2_web_acl" "test" { - name = "%[1]s" - description = "%[1]s" + name = %[1]q + description = %[1]q scope = "REGIONAL" default_action { @@ -2197,11 +2197,11 @@ resource "aws_wafv2_web_acl" "test" { `, name) } -func testAccAwsWafv2WebACLConfig_RateBasedStatement_ForwardedIPConfig(name, fallbackBehavior, headerName string) string { +func testAccAwsWafv2WebACLConfig_RateBasedStatement_forwardedIPConfig(name, fallbackBehavior, headerName string) string { return fmt.Sprintf(` resource "aws_wafv2_web_acl" "test" { - name = "%[1]s" - description = "%[1]s" + name = %[1]q + description = %[1]q scope = "REGIONAL" default_action { @@ -2220,8 +2220,8 @@ resource "aws_wafv2_web_acl" "test" { rate_based_statement { aggregate_key_type = "FORWARDED_IP" forwarded_ip_config { - fallback_behavior = "%s" - header_name = "%s" + fallback_behavior = %[2]q + header_name = %[3]q } limit = 50000 } @@ -2248,11 +2248,11 @@ resource "aws_wafv2_web_acl" "test" { `, name, fallbackBehavior, headerName) } -func testAccAwsWafv2WebACLConfig_RateBasedStatement_Update(name string) string { +func testAccAwsWafv2WebACLConfig_RateBasedStatement_update(name string) string { return fmt.Sprintf(` resource "aws_wafv2_web_acl" "test" { - name = "%[1]s" - description = "%[1]s" + name = %[1]q + description = %[1]q scope = "REGIONAL" default_action { @@ -2316,7 +2316,7 @@ resource "aws_wafv2_rule_group" "test" { } resource "aws_wafv2_web_acl" "test" { - name = "%[1]s" + name = %[1]q scope = "REGIONAL" default_action { @@ -2358,7 +2358,7 @@ resource "aws_wafv2_web_acl" "test" { `, name) } -func testAccAwsWafv2WebACLConfig_RuleGroupReferenceStatement_Update(name string) string { +func testAccAwsWafv2WebACLConfig_RuleGroupReferenceStatement_update(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { capacity = 10 @@ -2436,7 +2436,7 @@ resource "aws_wafv2_rule_group" "test" { } resource "aws_wafv2_web_acl" "test" { - name = "%[1]s" + name = %[1]q scope = "REGIONAL" default_action { @@ -2489,7 +2489,7 @@ resource "aws_wafv2_web_acl" "test" { func testAccAwsWafv2WebACLConfig_Minimal(name string) string { return fmt.Sprintf(` resource "aws_wafv2_web_acl" "test" { - name = "%s" + name = %[1]q scope = "REGIONAL" default_action { @@ -2508,8 +2508,8 @@ resource "aws_wafv2_web_acl" "test" { func testAccAwsWafv2WebACLConfig_OneTag(name, tagKey, tagValue string) string { return fmt.Sprintf(` resource "aws_wafv2_web_acl" "test" { - name = "%[1]s" - description = "%[1]s" + name = %[1]q + description = %[1]q scope = "REGIONAL" default_action { @@ -2523,7 +2523,7 @@ resource "aws_wafv2_web_acl" "test" { } tags = { - "%s" = "%s" + %[2]q = %[3]q } } `, name, tagKey, tagValue) @@ -2532,8 +2532,8 @@ resource "aws_wafv2_web_acl" "test" { func testAccAwsWafv2WebACLConfig_TwoTags(name, tag1Key, tag1Value, tag2Key, tag2Value string) string { return fmt.Sprintf(` resource "aws_wafv2_web_acl" "test" { - name = "%[1]s" - description = "%[1]s" + name = %[1]q + description = %[1]q scope = "REGIONAL" default_action { @@ -2547,8 +2547,8 @@ resource "aws_wafv2_web_acl" "test" { } tags = { - "%s" = "%s" - "%s" = "%s" + %[2]q = %[3]q + %[4]q = %[5]q } } `, name, tag1Key, tag1Value, tag2Key, tag2Value) @@ -2557,7 +2557,7 @@ resource "aws_wafv2_web_acl" "test" { func testAccAwsWafv2WebACLConfig_multipleNestedRateBasedStatements(name string) string { return fmt.Sprintf(` resource "aws_wafv2_regex_pattern_set" "test" { - name = "%[1]s" + name = %[1]q scope = "REGIONAL" regular_expression { @@ -2566,15 +2566,15 @@ resource "aws_wafv2_regex_pattern_set" "test" { } resource "aws_wafv2_ip_set" "test" { - name = "%[1]s" + name = %[1]q scope = "REGIONAL" ip_address_version = "IPV4" addresses = ["1.2.3.4/32", "5.6.7.8/32"] } resource "aws_wafv2_web_acl" "test" { - name = "%[1]s" - description = "%[1]s" + name = %[1]q + description = %[1]q scope = "REGIONAL" default_action { @@ -2644,7 +2644,7 @@ resource "aws_wafv2_web_acl" "test" { func testAccAwsWafv2WebACLConfig_multipleNestedOperatorStatements(name string) string { return fmt.Sprintf(` resource "aws_wafv2_regex_pattern_set" "test" { - name = "%[1]s" + name = %[1]q scope = "REGIONAL" regular_expression { @@ -2653,15 +2653,15 @@ resource "aws_wafv2_regex_pattern_set" "test" { } resource "aws_wafv2_ip_set" "test" { - name = "%[1]s" + name = %[1]q scope = "REGIONAL" ip_address_version = "IPV4" addresses = ["1.2.3.4/32", "5.6.7.8/32"] } resource "aws_wafv2_web_acl" "test" { - name = "%[1]s" - description = "%[1]s" + name = %[1]q + description = %[1]q scope = "REGIONAL" default_action { From e771d9d61926def0054db3d7099f052db01b56f5 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Mon, 12 Jul 2021 20:57:27 -0400 Subject: [PATCH 349/398] r/wafv2_web_acl: Standardize some --- aws/resource_aws_wafv2_web_acl.go | 62 +++++++++++++++++++------------ 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/aws/resource_aws_wafv2_web_acl.go b/aws/resource_aws_wafv2_web_acl.go index 18c71038b57..bb9e69c896b 100644 --- a/aws/resource_aws_wafv2_web_acl.go +++ b/aws/resource_aws_wafv2_web_acl.go @@ -386,12 +386,12 @@ func wafv2ManagedRuleGroupStatementSchema(level int) *schema.Schema { Required: true, ValidateFunc: validation.StringLenBetween(1, 128), }, + "scope_down_statement": wafv2ScopeDownStatementSchema(level - 1), "vendor_name": { Type: schema.TypeString, Required: true, ValidateFunc: validation.StringLenBetween(1, 128), }, - "scope_down_statement": wafv2ScopeDownStatementSchema(level - 1), }, }, } @@ -633,8 +633,7 @@ func expandWafv2ManagedRuleGroupStatement(l []interface{}) *wafv2.ManagedRuleGro VendorName: aws.String(m["vendor_name"].(string)), } - s := m["scope_down_statement"].([]interface{}) - if len(s) > 0 && s[0] != nil { + if s, ok := m["scope_down_statement"].([]interface{}); ok && len(s) > 0 && s[0] != nil { r.ScopeDownStatement = expandWafv2Statement(s[0].(map[string]interface{})) } @@ -826,41 +825,56 @@ func flattenWafv2DefaultAction(a *wafv2.DefaultAction) interface{} { return []interface{}{m} } -func flattenWafv2ManagedRuleGroupStatement(r *wafv2.ManagedRuleGroupStatement) interface{} { - if r == nil { +func flattenWafv2ManagedRuleGroupStatement(apiObject *wafv2.ManagedRuleGroupStatement) interface{} { + if apiObject == nil { return []interface{}{} } - m := map[string]interface{}{ - "excluded_rule": flattenWafv2ExcludedRules(r.ExcludedRules), - "name": aws.StringValue(r.Name), - "vendor_name": aws.StringValue(r.VendorName), - "scope_down_statement": nil, + tfMap := map[string]interface{}{} + + if apiObject.ExcludedRules != nil { + tfMap["excluded_rule"] = flattenWafv2ExcludedRules(apiObject.ExcludedRules) } -if r.ScopeDownStatement != nil { - m["scope_down_statement"] = []interface{}{flattenWafv2Statement(r.ScopeDownStatement)} -} - return []interface{}{m} + if apiObject.Name != nil { + tfMap["name"] = aws.StringValue(apiObject.Name) + } + + if apiObject.ScopeDownStatement != nil { + tfMap["scope_down_statement"] = []interface{}{flattenWafv2Statement(apiObject.ScopeDownStatement)} + } + + if apiObject.VendorName != nil { + tfMap["vendor_name"] = aws.StringValue(apiObject.VendorName) + } + + return []interface{}{tfMap} } -func flattenWafv2RateBasedStatement(r *wafv2.RateBasedStatement) interface{} { - if r == nil { +func flattenWafv2RateBasedStatement(apiObject *wafv2.RateBasedStatement) interface{} { + if apiObject == nil { return []interface{}{} } - m := map[string]interface{}{ - "limit": int(aws.Int64Value(r.Limit)), - "aggregate_key_type": aws.StringValue(r.AggregateKeyType), - "forwarded_ip_config": flattenWafv2ForwardedIPConfig(r.ForwardedIPConfig), - "scope_down_statement": nil, + tfMap := map[string]interface{}{} + + if apiObject.AggregateKeyType != nil { + tfMap["aggregate_key_type"] = aws.StringValue(apiObject.AggregateKeyType) } - if r.ScopeDownStatement != nil { - m["scope_down_statement"] = []interface{}{flattenWafv2Statement(r.ScopeDownStatement)} + if apiObject.ForwardedIPConfig != nil { + tfMap["forwarded_ip_config"] = flattenWafv2ForwardedIPConfig(apiObject.ForwardedIPConfig) } - return []interface{}{m} + if apiObject.Limit != nil { + tfMap["limit"] = int(aws.Int64Value(apiObject.Limit)) + } + + if apiObject.ScopeDownStatement != nil { + tfMap["scope_down_statement"] = []interface{}{flattenWafv2Statement(apiObject.ScopeDownStatement)} + } + + return []interface{}{tfMap} } func flattenWafv2RuleGroupReferenceStatement(r *wafv2.RuleGroupReferenceStatement) interface{} { From fa5e588c11115e4ca55bad637f520cccdf95087d Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Mon, 12 Jul 2021 21:04:03 -0400 Subject: [PATCH 350/398] tests/r/wafv2_web_acl: Update test name --- aws/resource_aws_wafv2_web_acl_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_wafv2_web_acl_test.go b/aws/resource_aws_wafv2_web_acl_test.go index dff17df08fb..f0f6adadc28 100644 --- a/aws/resource_aws_wafv2_web_acl_test.go +++ b/aws/resource_aws_wafv2_web_acl_test.go @@ -541,7 +541,7 @@ func TestAccAwsWafv2WebACL_disappears(t *testing.T) { }) } -func TestAccAwsWafv2WebACL_managedRuleGroup(t *testing.T) { +func TestAccAwsWafv2WebACL_ManagedRuleGroup_basic(t *testing.T) { var v wafv2.WebACL webACLName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_wafv2_web_acl.test" From e31904d703d5d10b7376269652ef09e546549536 Mon Sep 17 00:00:00 2001 From: changelogbot Date: Tue, 13 Jul 2021 01:21:30 +0000 Subject: [PATCH 351/398] Update CHANGELOG.md for #20140 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b53ab33ee1..b5069ee03df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,9 @@ FEATURES: * **New Resource:** `aws_appconfig_application` ([#19307](https://github.com/hashicorp/terraform-provider-aws/issues/19307)) * **New Resource:** `aws_appconfig_configuration_profile` ([#19320](https://github.com/hashicorp/terraform-provider-aws/issues/19320)) +* **New Resource:** `aws_appconfig_deployment_strategy` ([#19359](https://github.com/hashicorp/terraform-provider-aws/issues/19359)) * **New Resource:** `aws_appconfig_environment` ([#19307](https://github.com/hashicorp/terraform-provider-aws/issues/19307)) +* **New Resource:** `aws_appconfig_hosted_configuration_version` ([#19324](https://github.com/hashicorp/terraform-provider-aws/issues/19324)) * **New Resource:** `aws_config_organization_conformance_pack` ([#17298](https://github.com/hashicorp/terraform-provider-aws/issues/17298)) ENHANCEMENTS: @@ -18,6 +20,8 @@ ENHANCEMENTS: * resource/aws_fsx_windows_file_system: Add `aliases` argument ([#20054](https://github.com/hashicorp/terraform-provider-aws/issues/20054)) * resource/aws_guardduty_detector: Add `datasources` argument ([#19954](https://github.com/hashicorp/terraform-provider-aws/issues/19954)) * resource/aws_guardduty_organization_configuration: Add `datasources` argument ([#15241](https://github.com/hashicorp/terraform-provider-aws/issues/15241)) +* resource/aws_iam_access_key: Add encrypted SES SMTP password ([#19579](https://github.com/hashicorp/terraform-provider-aws/issues/19579)) +* resource/aws_s3_bucket: Add the delete_marker_replication_status argument for V2 replication configurations ([#19323](https://github.com/hashicorp/terraform-provider-aws/issues/19323)) BUG FIXES: From 26271f3121bdcadee84437f3064b5d3be88ea776 Mon Sep 17 00:00:00 2001 From: Theophile Chevalier Date: Tue, 7 Jan 2020 20:26:09 +0100 Subject: [PATCH 352/398] Add source_hash to aws_s3_bucket_object Allows one to store a hash in state to trigger resource update. --- aws/resource_aws_s3_bucket_object.go | 12 ++++ aws/resource_aws_s3_bucket_object_test.go | 57 +++++++++++++++++++ website/docs/r/s3_bucket_object.html.markdown | 1 + 3 files changed, 70 insertions(+) diff --git a/aws/resource_aws_s3_bucket_object.go b/aws/resource_aws_s3_bucket_object.go index 4f90ae35c8c..cc95b3a5060 100644 --- a/aws/resource_aws_s3_bucket_object.go +++ b/aws/resource_aws_s3_bucket_object.go @@ -191,6 +191,11 @@ func resourceAwsS3BucketObject() *schema.Resource { Optional: true, ValidateFunc: validation.IsRFC3339Time, }, + + "source_hash": { + Type: schema.TypeString, + Optional: true, + }, }, } } @@ -564,6 +569,12 @@ func resourceAwsS3BucketObjectCustomizeDiff(_ context.Context, d *schema.Resourc if hasS3BucketObjectContentChanges(d) { return d.SetNewComputed("version_id") } + + if d.HasChange("source_hash") { + d.SetNewComputed("version_id") + d.SetNewComputed("etag") + } + return nil } @@ -582,6 +593,7 @@ func hasS3BucketObjectContentChanges(d resourceDiffer) bool { "metadata", "server_side_encryption", "source", + "source_hash", "storage_class", "website_redirect", } { diff --git a/aws/resource_aws_s3_bucket_object_test.go b/aws/resource_aws_s3_bucket_object_test.go index f88b4283918..dde4681eb30 100644 --- a/aws/resource_aws_s3_bucket_object_test.go +++ b/aws/resource_aws_s3_bucket_object_test.go @@ -247,6 +247,48 @@ func TestAccAWSS3BucketObject_contentBase64(t *testing.T) { }) } +func TestAccAWSS3BucketObject_SourceHashTrigger(t *testing.T) { + var obj, updated_obj s3.GetObjectOutput + resourceName := "aws_s3_bucket_object.object" + source := testAccAWSS3BucketObjectCreateTempFile(t, "{anything will do }") + rewrite_source := func(*terraform.State) error { + if err := ioutil.WriteFile(source, []byte("{any other thing will do }"), 0644); err != nil { + os.Remove(source) + t.Fatal(err) + } + return nil + } + rInt := acctest.RandInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSS3BucketObjectDestroy, + Steps: []resource.TestStep{ + { + PreConfig: func() {}, + Config: testAccAWSS3BucketObjectConfig_SourceHashTrigger(rInt, source), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3BucketObjectExists(resourceName, &obj), + testAccCheckAWSS3BucketObjectBody(&obj, "{anything will do }"), + resource.TestCheckResourceAttr(resourceName, "source_hash", "7b006ff4d70f68cc65061acf2f802e6f"), + rewrite_source, + ), + ExpectNonEmptyPlan: true, + }, + { + PreConfig: func() {}, + Config: testAccAWSS3BucketObjectConfig_SourceHashTrigger(rInt, source), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3BucketObjectExists(resourceName, &updated_obj), + testAccCheckAWSS3BucketObjectBody(&updated_obj, "{any other thing will do }"), + resource.TestCheckResourceAttr(resourceName, "source_hash", "77a736aa9e04d0dc96b9b30894963983"), + ), + }, + }, + }) +} + func TestAccAWSS3BucketObject_withContentCharacteristics(t *testing.T) { var obj s3.GetObjectOutput resourceName := "aws_s3_bucket_object.object" @@ -1582,6 +1624,21 @@ resource "aws_s3_bucket_object" "object" { `, randInt, contentBase64) } +func testAccAWSS3BucketObjectConfig_SourceHashTrigger(randInt int, source string) string { + return fmt.Sprintf(` +resource "aws_s3_bucket" "object_bucket" { + bucket = "tf-object-test-bucket-%d" +} + +resource "aws_s3_bucket_object" "object" { + bucket = "${aws_s3_bucket.object_bucket.bucket}" + key = "test-key" + source = "%s" + source_hash = "${filemd5("%s")}" +} +`, randInt, source, source) +} + func testAccAWSS3BucketObjectConfig_updateable(randInt int, bucketVersioning bool, source string) string { return fmt.Sprintf(` resource "aws_s3_bucket" "object_bucket_3" { diff --git a/website/docs/r/s3_bucket_object.html.markdown b/website/docs/r/s3_bucket_object.html.markdown index 6bbd9dfa11c..f9934409c5a 100644 --- a/website/docs/r/s3_bucket_object.html.markdown +++ b/website/docs/r/s3_bucket_object.html.markdown @@ -131,6 +131,7 @@ The following arguments are supported: for the object. Can be either "`STANDARD`", "`REDUCED_REDUNDANCY`", "`ONEZONE_IA`", "`INTELLIGENT_TIERING`", "`GLACIER`", "`DEEP_ARCHIVE`", or "`STANDARD_IA`". Defaults to "`STANDARD`". * `etag` - (Optional) Used to trigger updates. The only meaningful value is `${filemd5("path/to/file")}` (Terraform 0.11.12 or later) or `${md5(file("path/to/file"))}` (Terraform 0.11.11 or earlier). This attribute is not compatible with KMS encryption, `kms_key_id` or `server_side_encryption = "aws:kms"`. +* `source_hash` - (Optional) Used to trigger updates based on source local changes. If used, must be set to `${filemd5("path/to/source")}` (Terraform 0.11.12 or later). This differs from `etag` since the value is stored in the state and does not come from AWS. Especially useful to address `etag` KMS encryption limitations. * `server_side_encryption` - (Optional) Specifies server-side encryption of the object in S3. Valid values are "`AES256`" and "`aws:kms`". * `kms_key_id` - (Optional) Amazon Resource Name (ARN) of the KMS Key to use for object encryption. If the S3 Bucket has server-side encryption enabled, that value will automatically be used. If referencing the `aws_kms_key` resource, use the `arn` attribute. If referencing the `aws_kms_alias` data source or resource, use the `target_key_arn` attribute. Terraform will only perform drift detection if a configuration value From bef29fe79c6eb9dcf39ee9b8f481d6260cc79f44 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Mon, 12 Jul 2021 21:59:06 -0400 Subject: [PATCH 353/398] tests/r/s3_bucket_object: Clean up tests --- aws/resource_aws_s3_bucket_object_test.go | 369 +++++++++++----------- 1 file changed, 185 insertions(+), 184 deletions(-) diff --git a/aws/resource_aws_s3_bucket_object_test.go b/aws/resource_aws_s3_bucket_object_test.go index dde4681eb30..561aa388a3f 100644 --- a/aws/resource_aws_s3_bucket_object_test.go +++ b/aws/resource_aws_s3_bucket_object_test.go @@ -4,6 +4,7 @@ import ( "encoding/base64" "fmt" "io" + "io/ioutil" "log" "os" "reflect" @@ -130,7 +131,7 @@ func TestAccAWSS3BucketObject_noNameNoKey(t *testing.T) { func TestAccAWSS3BucketObject_empty(t *testing.T) { var obj s3.GetObjectOutput resourceName := "aws_s3_bucket_object.object" - rInt := acctest.RandInt() + rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -140,7 +141,7 @@ func TestAccAWSS3BucketObject_empty(t *testing.T) { Steps: []resource.TestStep{ { PreConfig: func() {}, - Config: testAccAWSS3BucketObjectConfigEmpty(rInt), + Config: testAccAWSS3BucketObjectConfigEmpty(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj), testAccCheckAWSS3BucketObjectBody(&obj, ""), @@ -153,7 +154,7 @@ func TestAccAWSS3BucketObject_empty(t *testing.T) { func TestAccAWSS3BucketObject_source(t *testing.T) { var obj s3.GetObjectOutput resourceName := "aws_s3_bucket_object.object" - rInt := acctest.RandInt() + rName := acctest.RandomWithPrefix("tf-acc-test") source := testAccAWSS3BucketObjectCreateTempFile(t, "{anything will do }") defer os.Remove(source) @@ -165,7 +166,7 @@ func TestAccAWSS3BucketObject_source(t *testing.T) { CheckDestroy: testAccCheckAWSS3BucketObjectDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSS3BucketObjectConfigSource(rInt, source), + Config: testAccAWSS3BucketObjectConfigSource(rName, source), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj), testAccCheckAWSS3BucketObjectBody(&obj, "{anything will do }"), @@ -178,7 +179,7 @@ func TestAccAWSS3BucketObject_source(t *testing.T) { func TestAccAWSS3BucketObject_content(t *testing.T) { var obj s3.GetObjectOutput resourceName := "aws_s3_bucket_object.object" - rInt := acctest.RandInt() + rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -188,7 +189,7 @@ func TestAccAWSS3BucketObject_content(t *testing.T) { Steps: []resource.TestStep{ { PreConfig: func() {}, - Config: testAccAWSS3BucketObjectConfigContent(rInt, "some_bucket_content"), + Config: testAccAWSS3BucketObjectConfigContent(rName, "some_bucket_content"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj), testAccCheckAWSS3BucketObjectBody(&obj, "some_bucket_content"), @@ -201,7 +202,7 @@ func TestAccAWSS3BucketObject_content(t *testing.T) { func TestAccAWSS3BucketObject_etagEncryption(t *testing.T) { var obj s3.GetObjectOutput resourceName := "aws_s3_bucket_object.object" - rInt := acctest.RandInt() + rName := acctest.RandomWithPrefix("tf-acc-test") source := testAccAWSS3BucketObjectCreateTempFile(t, "{anything will do }") defer os.Remove(source) @@ -213,7 +214,7 @@ func TestAccAWSS3BucketObject_etagEncryption(t *testing.T) { Steps: []resource.TestStep{ { PreConfig: func() {}, - Config: testAccAWSS3BucketObjectEtagEncryption(rInt, source), + Config: testAccAWSS3BucketObjectEtagEncryption(rName, source), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj), testAccCheckAWSS3BucketObjectBody(&obj, "{anything will do }"), @@ -227,7 +228,7 @@ func TestAccAWSS3BucketObject_etagEncryption(t *testing.T) { func TestAccAWSS3BucketObject_contentBase64(t *testing.T) { var obj s3.GetObjectOutput resourceName := "aws_s3_bucket_object.object" - rInt := acctest.RandInt() + rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -237,7 +238,7 @@ func TestAccAWSS3BucketObject_contentBase64(t *testing.T) { Steps: []resource.TestStep{ { PreConfig: func() {}, - Config: testAccAWSS3BucketObjectConfigContentBase64(rInt, base64.StdEncoding.EncodeToString([]byte("some_bucket_content"))), + Config: testAccAWSS3BucketObjectConfigContentBase64(rName, base64.StdEncoding.EncodeToString([]byte("some_bucket_content"))), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj), testAccCheckAWSS3BucketObjectBody(&obj, "some_bucket_content"), @@ -247,18 +248,18 @@ func TestAccAWSS3BucketObject_contentBase64(t *testing.T) { }) } -func TestAccAWSS3BucketObject_SourceHashTrigger(t *testing.T) { +func TestAccAWSS3BucketObject_sourceHashTrigger(t *testing.T) { var obj, updated_obj s3.GetObjectOutput resourceName := "aws_s3_bucket_object.object" source := testAccAWSS3BucketObjectCreateTempFile(t, "{anything will do }") - rewrite_source := func(*terraform.State) error { + rewriteSourceF := func(*terraform.State) error { if err := ioutil.WriteFile(source, []byte("{any other thing will do }"), 0644); err != nil { os.Remove(source) t.Fatal(err) } return nil } - rInt := acctest.RandInt() + rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -267,18 +268,18 @@ func TestAccAWSS3BucketObject_SourceHashTrigger(t *testing.T) { Steps: []resource.TestStep{ { PreConfig: func() {}, - Config: testAccAWSS3BucketObjectConfig_SourceHashTrigger(rInt, source), + Config: testAccAWSS3BucketObjectConfig_sourceHashTrigger(rName, source), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj), testAccCheckAWSS3BucketObjectBody(&obj, "{anything will do }"), resource.TestCheckResourceAttr(resourceName, "source_hash", "7b006ff4d70f68cc65061acf2f802e6f"), - rewrite_source, + rewriteSourceF, ), ExpectNonEmptyPlan: true, }, { PreConfig: func() {}, - Config: testAccAWSS3BucketObjectConfig_SourceHashTrigger(rInt, source), + Config: testAccAWSS3BucketObjectConfig_sourceHashTrigger(rName, source), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &updated_obj), testAccCheckAWSS3BucketObjectBody(&updated_obj, "{any other thing will do }"), @@ -292,7 +293,7 @@ func TestAccAWSS3BucketObject_SourceHashTrigger(t *testing.T) { func TestAccAWSS3BucketObject_withContentCharacteristics(t *testing.T) { var obj s3.GetObjectOutput resourceName := "aws_s3_bucket_object.object" - rInt := acctest.RandInt() + rName := acctest.RandomWithPrefix("tf-acc-test") source := testAccAWSS3BucketObjectCreateTempFile(t, "{anything will do }") defer os.Remove(source) @@ -304,7 +305,7 @@ func TestAccAWSS3BucketObject_withContentCharacteristics(t *testing.T) { CheckDestroy: testAccCheckAWSS3BucketObjectDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSS3BucketObjectConfig_withContentCharacteristics(rInt, source), + Config: testAccAWSS3BucketObjectConfig_withContentCharacteristics(rName, source), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj), testAccCheckAWSS3BucketObjectBody(&obj, "{anything will do }"), @@ -316,10 +317,10 @@ func TestAccAWSS3BucketObject_withContentCharacteristics(t *testing.T) { }) } -func TestAccAWSS3BucketObject_NonVersioned(t *testing.T) { +func TestAccAWSS3BucketObject_nonVersioned(t *testing.T) { sourceInitial := testAccAWSS3BucketObjectCreateTempFile(t, "initial object state") defer os.Remove(sourceInitial) - + rName := acctest.RandomWithPrefix("tf-acc-test") var originalObj s3.GetObjectOutput resourceName := "aws_s3_bucket_object.object" @@ -330,7 +331,7 @@ func TestAccAWSS3BucketObject_NonVersioned(t *testing.T) { CheckDestroy: testAccCheckAWSS3BucketObjectDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSS3BucketObjectConfig_NonVersioned(acctest.RandInt(), sourceInitial), + Config: testAccAWSS3BucketObjectConfig_nonVersioned(rName, sourceInitial), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &originalObj), testAccCheckAWSS3BucketObjectBody(&originalObj, "initial object state"), @@ -344,7 +345,7 @@ func TestAccAWSS3BucketObject_NonVersioned(t *testing.T) { func TestAccAWSS3BucketObject_updates(t *testing.T) { var originalObj, modifiedObj s3.GetObjectOutput resourceName := "aws_s3_bucket_object.object" - rInt := acctest.RandInt() + rName := acctest.RandomWithPrefix("tf-acc-test") sourceInitial := testAccAWSS3BucketObjectCreateTempFile(t, "initial object state") defer os.Remove(sourceInitial) @@ -358,7 +359,7 @@ func TestAccAWSS3BucketObject_updates(t *testing.T) { CheckDestroy: testAccCheckAWSS3BucketObjectDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSS3BucketObjectConfig_updateable(rInt, false, sourceInitial), + Config: testAccAWSS3BucketObjectConfig_updateable(rName, false, sourceInitial), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &originalObj), testAccCheckAWSS3BucketObjectBody(&originalObj, "initial object state"), @@ -369,7 +370,7 @@ func TestAccAWSS3BucketObject_updates(t *testing.T) { ), }, { - Config: testAccAWSS3BucketObjectConfig_updateable(rInt, false, sourceModified), + Config: testAccAWSS3BucketObjectConfig_updateable(rName, false, sourceModified), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &modifiedObj), testAccCheckAWSS3BucketObjectBody(&modifiedObj, "modified object"), @@ -386,7 +387,7 @@ func TestAccAWSS3BucketObject_updates(t *testing.T) { func TestAccAWSS3BucketObject_updateSameFile(t *testing.T) { var originalObj, modifiedObj s3.GetObjectOutput resourceName := "aws_s3_bucket_object.object" - rInt := acctest.RandInt() + rName := acctest.RandomWithPrefix("tf-acc-test") startingData := "lane 8" changingData := "chicane" @@ -409,7 +410,7 @@ func TestAccAWSS3BucketObject_updateSameFile(t *testing.T) { CheckDestroy: testAccCheckAWSS3BucketObjectDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSS3BucketObjectConfig_updateable(rInt, false, filename), + Config: testAccAWSS3BucketObjectConfig_updateable(rName, false, filename), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &originalObj), testAccCheckAWSS3BucketObjectBody(&originalObj, startingData), @@ -419,7 +420,7 @@ func TestAccAWSS3BucketObject_updateSameFile(t *testing.T) { ExpectNonEmptyPlan: true, }, { - Config: testAccAWSS3BucketObjectConfig_updateable(rInt, false, filename), + Config: testAccAWSS3BucketObjectConfig_updateable(rName, false, filename), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &modifiedObj), testAccCheckAWSS3BucketObjectBody(&modifiedObj, changingData), @@ -433,7 +434,7 @@ func TestAccAWSS3BucketObject_updateSameFile(t *testing.T) { func TestAccAWSS3BucketObject_updatesWithVersioning(t *testing.T) { var originalObj, modifiedObj s3.GetObjectOutput resourceName := "aws_s3_bucket_object.object" - rInt := acctest.RandInt() + rName := acctest.RandomWithPrefix("tf-acc-test") sourceInitial := testAccAWSS3BucketObjectCreateTempFile(t, "initial versioned object state") defer os.Remove(sourceInitial) @@ -447,7 +448,7 @@ func TestAccAWSS3BucketObject_updatesWithVersioning(t *testing.T) { CheckDestroy: testAccCheckAWSS3BucketObjectDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSS3BucketObjectConfig_updateable(rInt, true, sourceInitial), + Config: testAccAWSS3BucketObjectConfig_updateable(rName, true, sourceInitial), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &originalObj), testAccCheckAWSS3BucketObjectBody(&originalObj, "initial versioned object state"), @@ -455,7 +456,7 @@ func TestAccAWSS3BucketObject_updatesWithVersioning(t *testing.T) { ), }, { - Config: testAccAWSS3BucketObjectConfig_updateable(rInt, true, sourceModified), + Config: testAccAWSS3BucketObjectConfig_updateable(rName, true, sourceModified), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &modifiedObj), testAccCheckAWSS3BucketObjectBody(&modifiedObj, "modified versioned object"), @@ -509,7 +510,7 @@ func TestAccAWSS3BucketObject_updatesWithVersioningViaAccessPoint(t *testing.T) func TestAccAWSS3BucketObject_kms(t *testing.T) { var obj s3.GetObjectOutput resourceName := "aws_s3_bucket_object.object" - rInt := acctest.RandInt() + rName := acctest.RandomWithPrefix("tf-acc-test") source := testAccAWSS3BucketObjectCreateTempFile(t, "{anything will do }") defer os.Remove(source) @@ -522,7 +523,7 @@ func TestAccAWSS3BucketObject_kms(t *testing.T) { Steps: []resource.TestStep{ { PreConfig: func() {}, - Config: testAccAWSS3BucketObjectConfig_withKMSId(rInt, source), + Config: testAccAWSS3BucketObjectConfig_withKMSId(rName, source), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj), testAccCheckAWSS3BucketObjectSSE(resourceName, "aws:kms"), @@ -536,7 +537,7 @@ func TestAccAWSS3BucketObject_kms(t *testing.T) { func TestAccAWSS3BucketObject_sse(t *testing.T) { var obj s3.GetObjectOutput resourceName := "aws_s3_bucket_object.object" - rInt := acctest.RandInt() + rName := acctest.RandomWithPrefix("tf-acc-test") source := testAccAWSS3BucketObjectCreateTempFile(t, "{anything will do }") defer os.Remove(source) @@ -549,7 +550,7 @@ func TestAccAWSS3BucketObject_sse(t *testing.T) { Steps: []resource.TestStep{ { PreConfig: func() {}, - Config: testAccAWSS3BucketObjectConfig_withSSE(rInt, source), + Config: testAccAWSS3BucketObjectConfig_withSSE(rName, source), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj), testAccCheckAWSS3BucketObjectSSE(resourceName, "AES256"), @@ -563,7 +564,7 @@ func TestAccAWSS3BucketObject_sse(t *testing.T) { func TestAccAWSS3BucketObject_acl(t *testing.T) { var obj1, obj2, obj3 s3.GetObjectOutput resourceName := "aws_s3_bucket_object.object" - rInt := acctest.RandInt() + rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -572,7 +573,7 @@ func TestAccAWSS3BucketObject_acl(t *testing.T) { CheckDestroy: testAccCheckAWSS3BucketObjectDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSS3BucketObjectConfig_acl(rInt, "some_bucket_content", "private"), + Config: testAccAWSS3BucketObjectConfig_acl(rName, "some_bucket_content", "private"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj1), testAccCheckAWSS3BucketObjectBody(&obj1, "some_bucket_content"), @@ -581,7 +582,7 @@ func TestAccAWSS3BucketObject_acl(t *testing.T) { ), }, { - Config: testAccAWSS3BucketObjectConfig_acl(rInt, "some_bucket_content", "public-read"), + Config: testAccAWSS3BucketObjectConfig_acl(rName, "some_bucket_content", "public-read"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj2), testAccCheckAWSS3BucketObjectVersionIdEquals(&obj2, &obj1), @@ -591,7 +592,7 @@ func TestAccAWSS3BucketObject_acl(t *testing.T) { ), }, { - Config: testAccAWSS3BucketObjectConfig_acl(rInt, "changed_some_bucket_content", "private"), + Config: testAccAWSS3BucketObjectConfig_acl(rName, "changed_some_bucket_content", "private"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj3), testAccCheckAWSS3BucketObjectVersionIdDiffers(&obj3, &obj2), @@ -605,7 +606,7 @@ func TestAccAWSS3BucketObject_acl(t *testing.T) { } func TestAccAWSS3BucketObject_metadata(t *testing.T) { - rInt := acctest.RandInt() + rName := acctest.RandomWithPrefix("tf-acc-test") var obj s3.GetObjectOutput resourceName := "aws_s3_bucket_object.object" @@ -616,7 +617,7 @@ func TestAccAWSS3BucketObject_metadata(t *testing.T) { CheckDestroy: testAccCheckAWSS3BucketObjectDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSS3BucketObjectConfig_withMetadata(rInt, "key1", "value1", "key2", "value2"), + Config: testAccAWSS3BucketObjectConfig_withMetadata(rName, "key1", "value1", "key2", "value2"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj), resource.TestCheckResourceAttr(resourceName, "metadata.%", "2"), @@ -625,7 +626,7 @@ func TestAccAWSS3BucketObject_metadata(t *testing.T) { ), }, { - Config: testAccAWSS3BucketObjectConfig_withMetadata(rInt, "key1", "value1updated", "key3", "value3"), + Config: testAccAWSS3BucketObjectConfig_withMetadata(rName, "key1", "value1updated", "key3", "value3"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj), resource.TestCheckResourceAttr(resourceName, "metadata.%", "2"), @@ -634,7 +635,7 @@ func TestAccAWSS3BucketObject_metadata(t *testing.T) { ), }, { - Config: testAccAWSS3BucketObjectConfigEmpty(rInt), + Config: testAccAWSS3BucketObjectConfigEmpty(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj), resource.TestCheckResourceAttr(resourceName, "metadata.%", "0"), @@ -647,7 +648,7 @@ func TestAccAWSS3BucketObject_metadata(t *testing.T) { func TestAccAWSS3BucketObject_storageClass(t *testing.T) { var obj s3.GetObjectOutput resourceName := "aws_s3_bucket_object.object" - rInt := acctest.RandInt() + rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -657,7 +658,7 @@ func TestAccAWSS3BucketObject_storageClass(t *testing.T) { Steps: []resource.TestStep{ { PreConfig: func() {}, - Config: testAccAWSS3BucketObjectConfigContent(rInt, "some_bucket_content"), + Config: testAccAWSS3BucketObjectConfigContent(rName, "some_bucket_content"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj), resource.TestCheckResourceAttr(resourceName, "storage_class", "STANDARD"), @@ -665,7 +666,7 @@ func TestAccAWSS3BucketObject_storageClass(t *testing.T) { ), }, { - Config: testAccAWSS3BucketObjectConfig_storageClass(rInt, "REDUCED_REDUNDANCY"), + Config: testAccAWSS3BucketObjectConfig_storageClass(rName, "REDUCED_REDUNDANCY"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj), resource.TestCheckResourceAttr(resourceName, "storage_class", "REDUCED_REDUNDANCY"), @@ -673,7 +674,7 @@ func TestAccAWSS3BucketObject_storageClass(t *testing.T) { ), }, { - Config: testAccAWSS3BucketObjectConfig_storageClass(rInt, "GLACIER"), + Config: testAccAWSS3BucketObjectConfig_storageClass(rName, "GLACIER"), Check: resource.ComposeTestCheckFunc( // Can't GetObject on an object in Glacier without restoring it. resource.TestCheckResourceAttr(resourceName, "storage_class", "GLACIER"), @@ -681,7 +682,7 @@ func TestAccAWSS3BucketObject_storageClass(t *testing.T) { ), }, { - Config: testAccAWSS3BucketObjectConfig_storageClass(rInt, "INTELLIGENT_TIERING"), + Config: testAccAWSS3BucketObjectConfig_storageClass(rName, "INTELLIGENT_TIERING"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj), resource.TestCheckResourceAttr(resourceName, "storage_class", "INTELLIGENT_TIERING"), @@ -689,7 +690,7 @@ func TestAccAWSS3BucketObject_storageClass(t *testing.T) { ), }, { - Config: testAccAWSS3BucketObjectConfig_storageClass(rInt, "DEEP_ARCHIVE"), + Config: testAccAWSS3BucketObjectConfig_storageClass(rName, "DEEP_ARCHIVE"), Check: resource.ComposeTestCheckFunc( // Can't GetObject on an object in DEEP_ARCHIVE without restoring it. resource.TestCheckResourceAttr(resourceName, "storage_class", "DEEP_ARCHIVE"), @@ -960,10 +961,10 @@ func TestAccAWSS3BucketObject_tagsMultipleSlashes(t *testing.T) { }) } -func TestAccAWSS3BucketObject_ObjectLockLegalHoldStartWithNone(t *testing.T) { +func TestAccAWSS3BucketObject_objectLockLegalHoldStartWithNone(t *testing.T) { var obj1, obj2, obj3 s3.GetObjectOutput resourceName := "aws_s3_bucket_object.object" - rInt := acctest.RandInt() + rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -972,7 +973,7 @@ func TestAccAWSS3BucketObject_ObjectLockLegalHoldStartWithNone(t *testing.T) { CheckDestroy: testAccCheckAWSS3BucketObjectDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSS3BucketObjectConfig_noObjectLockLegalHold(rInt, "stuff"), + Config: testAccAWSS3BucketObjectConfig_noObjectLockLegalHold(rName, "stuff"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj1), testAccCheckAWSS3BucketObjectBody(&obj1, "stuff"), @@ -982,7 +983,7 @@ func TestAccAWSS3BucketObject_ObjectLockLegalHoldStartWithNone(t *testing.T) { ), }, { - Config: testAccAWSS3BucketObjectConfig_withObjectLockLegalHold(rInt, "stuff", "ON"), + Config: testAccAWSS3BucketObjectConfig_withObjectLockLegalHold(rName, "stuff", "ON"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj2), testAccCheckAWSS3BucketObjectVersionIdEquals(&obj2, &obj1), @@ -994,7 +995,7 @@ func TestAccAWSS3BucketObject_ObjectLockLegalHoldStartWithNone(t *testing.T) { }, // Remove legal hold but create a new object version to test force_destroy { - Config: testAccAWSS3BucketObjectConfig_withObjectLockLegalHold(rInt, "changed stuff", "OFF"), + Config: testAccAWSS3BucketObjectConfig_withObjectLockLegalHold(rName, "changed stuff", "OFF"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj3), testAccCheckAWSS3BucketObjectVersionIdDiffers(&obj3, &obj2), @@ -1008,10 +1009,10 @@ func TestAccAWSS3BucketObject_ObjectLockLegalHoldStartWithNone(t *testing.T) { }) } -func TestAccAWSS3BucketObject_ObjectLockLegalHoldStartWithOn(t *testing.T) { +func TestAccAWSS3BucketObject_objectLockLegalHoldStartWithOn(t *testing.T) { var obj1, obj2 s3.GetObjectOutput resourceName := "aws_s3_bucket_object.object" - rInt := acctest.RandInt() + rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -1020,7 +1021,7 @@ func TestAccAWSS3BucketObject_ObjectLockLegalHoldStartWithOn(t *testing.T) { CheckDestroy: testAccCheckAWSS3BucketObjectDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSS3BucketObjectConfig_withObjectLockLegalHold(rInt, "stuff", "ON"), + Config: testAccAWSS3BucketObjectConfig_withObjectLockLegalHold(rName, "stuff", "ON"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj1), testAccCheckAWSS3BucketObjectBody(&obj1, "stuff"), @@ -1030,7 +1031,7 @@ func TestAccAWSS3BucketObject_ObjectLockLegalHoldStartWithOn(t *testing.T) { ), }, { - Config: testAccAWSS3BucketObjectConfig_withObjectLockLegalHold(rInt, "stuff", "OFF"), + Config: testAccAWSS3BucketObjectConfig_withObjectLockLegalHold(rName, "stuff", "OFF"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj2), testAccCheckAWSS3BucketObjectVersionIdEquals(&obj2, &obj1), @@ -1044,10 +1045,10 @@ func TestAccAWSS3BucketObject_ObjectLockLegalHoldStartWithOn(t *testing.T) { }) } -func TestAccAWSS3BucketObject_ObjectLockRetentionStartWithNone(t *testing.T) { +func TestAccAWSS3BucketObject_objectLockRetentionStartWithNone(t *testing.T) { var obj1, obj2, obj3 s3.GetObjectOutput resourceName := "aws_s3_bucket_object.object" - rInt := acctest.RandInt() + rName := acctest.RandomWithPrefix("tf-acc-test") retainUntilDate := time.Now().UTC().AddDate(0, 0, 10).Format(time.RFC3339) resource.ParallelTest(t, resource.TestCase{ @@ -1057,7 +1058,7 @@ func TestAccAWSS3BucketObject_ObjectLockRetentionStartWithNone(t *testing.T) { CheckDestroy: testAccCheckAWSS3BucketObjectDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSS3BucketObjectConfig_noObjectLockRetention(rInt, "stuff"), + Config: testAccAWSS3BucketObjectConfig_noObjectLockRetention(rName, "stuff"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj1), testAccCheckAWSS3BucketObjectBody(&obj1, "stuff"), @@ -1067,7 +1068,7 @@ func TestAccAWSS3BucketObject_ObjectLockRetentionStartWithNone(t *testing.T) { ), }, { - Config: testAccAWSS3BucketObjectConfig_withObjectLockRetention(rInt, "stuff", retainUntilDate), + Config: testAccAWSS3BucketObjectConfig_withObjectLockRetention(rName, "stuff", retainUntilDate), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj2), testAccCheckAWSS3BucketObjectVersionIdEquals(&obj2, &obj1), @@ -1079,7 +1080,7 @@ func TestAccAWSS3BucketObject_ObjectLockRetentionStartWithNone(t *testing.T) { }, // Remove retention period but create a new object version to test force_destroy { - Config: testAccAWSS3BucketObjectConfig_noObjectLockRetention(rInt, "changed stuff"), + Config: testAccAWSS3BucketObjectConfig_noObjectLockRetention(rName, "changed stuff"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj3), testAccCheckAWSS3BucketObjectVersionIdDiffers(&obj3, &obj2), @@ -1093,10 +1094,10 @@ func TestAccAWSS3BucketObject_ObjectLockRetentionStartWithNone(t *testing.T) { }) } -func TestAccAWSS3BucketObject_ObjectLockRetentionStartWithSet(t *testing.T) { +func TestAccAWSS3BucketObject_objectLockRetentionStartWithSet(t *testing.T) { var obj1, obj2, obj3, obj4 s3.GetObjectOutput resourceName := "aws_s3_bucket_object.object" - rInt := acctest.RandInt() + rName := acctest.RandomWithPrefix("tf-acc-test") retainUntilDate1 := time.Now().UTC().AddDate(0, 0, 20).Format(time.RFC3339) retainUntilDate2 := time.Now().UTC().AddDate(0, 0, 30).Format(time.RFC3339) retainUntilDate3 := time.Now().UTC().AddDate(0, 0, 10).Format(time.RFC3339) @@ -1108,7 +1109,7 @@ func TestAccAWSS3BucketObject_ObjectLockRetentionStartWithSet(t *testing.T) { CheckDestroy: testAccCheckAWSS3BucketObjectDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSS3BucketObjectConfig_withObjectLockRetention(rInt, "stuff", retainUntilDate1), + Config: testAccAWSS3BucketObjectConfig_withObjectLockRetention(rName, "stuff", retainUntilDate1), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj1), testAccCheckAWSS3BucketObjectBody(&obj1, "stuff"), @@ -1118,7 +1119,7 @@ func TestAccAWSS3BucketObject_ObjectLockRetentionStartWithSet(t *testing.T) { ), }, { - Config: testAccAWSS3BucketObjectConfig_withObjectLockRetention(rInt, "stuff", retainUntilDate2), + Config: testAccAWSS3BucketObjectConfig_withObjectLockRetention(rName, "stuff", retainUntilDate2), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj2), testAccCheckAWSS3BucketObjectVersionIdEquals(&obj2, &obj1), @@ -1129,7 +1130,7 @@ func TestAccAWSS3BucketObject_ObjectLockRetentionStartWithSet(t *testing.T) { ), }, { - Config: testAccAWSS3BucketObjectConfig_withObjectLockRetention(rInt, "stuff", retainUntilDate3), + Config: testAccAWSS3BucketObjectConfig_withObjectLockRetention(rName, "stuff", retainUntilDate3), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj3), testAccCheckAWSS3BucketObjectVersionIdEquals(&obj3, &obj2), @@ -1140,7 +1141,7 @@ func TestAccAWSS3BucketObject_ObjectLockRetentionStartWithSet(t *testing.T) { ), }, { - Config: testAccAWSS3BucketObjectConfig_noObjectLockRetention(rInt, "stuff"), + Config: testAccAWSS3BucketObjectConfig_noObjectLockRetention(rName, "stuff"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj4), testAccCheckAWSS3BucketObjectVersionIdEquals(&obj4, &obj3), @@ -1157,7 +1158,7 @@ func TestAccAWSS3BucketObject_ObjectLockRetentionStartWithSet(t *testing.T) { func TestAccAWSS3BucketObject_objectBucketKeyEnabled(t *testing.T) { var obj s3.GetObjectOutput resourceName := "aws_s3_bucket_object.object" - rInt := acctest.RandInt() + rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -1166,7 +1167,7 @@ func TestAccAWSS3BucketObject_objectBucketKeyEnabled(t *testing.T) { CheckDestroy: testAccCheckAWSS3BucketObjectDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSS3BucketObjectConfig_objectBucketKeyEnabled(rInt, "stuff"), + Config: testAccAWSS3BucketObjectConfig_objectBucketKeyEnabled(rName, "stuff"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj), testAccCheckAWSS3BucketObjectBody(&obj, "stuff"), @@ -1180,7 +1181,7 @@ func TestAccAWSS3BucketObject_objectBucketKeyEnabled(t *testing.T) { func TestAccAWSS3BucketObject_bucketBucketKeyEnabled(t *testing.T) { var obj s3.GetObjectOutput resourceName := "aws_s3_bucket_object.object" - rInt := acctest.RandInt() + rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -1189,7 +1190,7 @@ func TestAccAWSS3BucketObject_bucketBucketKeyEnabled(t *testing.T) { CheckDestroy: testAccCheckAWSS3BucketObjectDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSS3BucketObjectConfig_bucketBucketKeyEnabled(rInt, "stuff"), + Config: testAccAWSS3BucketObjectConfig_bucketBucketKeyEnabled(rName, "stuff"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj), testAccCheckAWSS3BucketObjectBody(&obj, "stuff"), @@ -1203,7 +1204,7 @@ func TestAccAWSS3BucketObject_bucketBucketKeyEnabled(t *testing.T) { func TestAccAWSS3BucketObject_defaultBucketSSE(t *testing.T) { var obj1 s3.GetObjectOutput resourceName := "aws_s3_bucket_object.object" - rInt := acctest.RandInt() + rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -1212,7 +1213,7 @@ func TestAccAWSS3BucketObject_defaultBucketSSE(t *testing.T) { CheckDestroy: testAccCheckAWSS3BucketObjectDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSS3BucketObjectConfig_defaultBucketSSE(rInt, "stuff"), + Config: testAccAWSS3BucketObjectConfig_defaultBucketSSE(rName, "stuff"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj1), testAccCheckAWSS3BucketObjectBody(&obj1, "stuff"), @@ -1535,114 +1536,114 @@ resource "aws_s3_bucket_object" "object" { `, bucket, key) } -func testAccAWSS3BucketObjectConfigEmpty(randInt int) string { +func testAccAWSS3BucketObjectConfigEmpty(rName string) string { return fmt.Sprintf(` -resource "aws_s3_bucket" "object_bucket" { - bucket = "tf-object-test-bucket-%d" +resource "aws_s3_bucket" "test" { + bucket = %[1]q } resource "aws_s3_bucket_object" "object" { - bucket = aws_s3_bucket.object_bucket.bucket + bucket = aws_s3_bucket.test.bucket key = "test-key" } -`, randInt) +`, rName) } -func testAccAWSS3BucketObjectConfigSource(randInt int, source string) string { +func testAccAWSS3BucketObjectConfigSource(rName string, source string) string { return fmt.Sprintf(` -resource "aws_s3_bucket" "object_bucket" { - bucket = "tf-object-test-bucket-%[1]d" +resource "aws_s3_bucket" "test" { + bucket = %[1]q } resource "aws_s3_bucket_object" "object" { - bucket = aws_s3_bucket.object_bucket.bucket + bucket = aws_s3_bucket.test.bucket key = "test-key" source = %[2]q content_type = "binary/octet-stream" } -`, randInt, source) +`, rName, source) } -func testAccAWSS3BucketObjectConfig_withContentCharacteristics(randInt int, source string) string { +func testAccAWSS3BucketObjectConfig_withContentCharacteristics(rName string, source string) string { return fmt.Sprintf(` -resource "aws_s3_bucket" "object_bucket" { - bucket = "tf-object-test-bucket-%[1]d" +resource "aws_s3_bucket" "test" { + bucket = %[1]q } resource "aws_s3_bucket_object" "object" { - bucket = aws_s3_bucket.object_bucket.bucket + bucket = aws_s3_bucket.test.bucket key = "test-key" source = %[2]q content_language = "en" content_type = "binary/octet-stream" website_redirect = "http://google.com" } -`, randInt, source) +`, rName, source) } -func testAccAWSS3BucketObjectConfigContent(randInt int, content string) string { +func testAccAWSS3BucketObjectConfigContent(rName string, content string) string { return fmt.Sprintf(` -resource "aws_s3_bucket" "object_bucket" { - bucket = "tf-object-test-bucket-%[1]d" +resource "aws_s3_bucket" "test" { + bucket = %[1]q } resource "aws_s3_bucket_object" "object" { - bucket = aws_s3_bucket.object_bucket.bucket + bucket = aws_s3_bucket.test.bucket key = "test-key" content = %[2]q } -`, randInt, content) +`, rName, content) } -func testAccAWSS3BucketObjectEtagEncryption(randInt int, source string) string { +func testAccAWSS3BucketObjectEtagEncryption(rName string, source string) string { return fmt.Sprintf(` -resource "aws_s3_bucket" "object_bucket" { - bucket = "tf-object-test-bucket-%[1]d" +resource "aws_s3_bucket" "test" { + bucket = %[1]q } resource "aws_s3_bucket_object" "object" { - bucket = aws_s3_bucket.object_bucket.bucket + bucket = aws_s3_bucket.test.bucket key = "test-key" server_side_encryption = "AES256" source = %[2]q etag = filemd5(%[2]q) } -`, randInt, source) +`, rName, source) } -func testAccAWSS3BucketObjectConfigContentBase64(randInt int, contentBase64 string) string { +func testAccAWSS3BucketObjectConfigContentBase64(rName string, contentBase64 string) string { return fmt.Sprintf(` -resource "aws_s3_bucket" "object_bucket" { - bucket = "tf-object-test-bucket-%[1]d" +resource "aws_s3_bucket" "test" { + bucket = %[1]q } resource "aws_s3_bucket_object" "object" { - bucket = aws_s3_bucket.object_bucket.bucket + bucket = aws_s3_bucket.test.bucket key = "test-key" content_base64 = %[2]q } -`, randInt, contentBase64) +`, rName, contentBase64) } -func testAccAWSS3BucketObjectConfig_SourceHashTrigger(randInt int, source string) string { +func testAccAWSS3BucketObjectConfig_sourceHashTrigger(rName string, source string) string { return fmt.Sprintf(` -resource "aws_s3_bucket" "object_bucket" { - bucket = "tf-object-test-bucket-%d" +resource "aws_s3_bucket" "test" { + bucket = %[1]q } resource "aws_s3_bucket_object" "object" { - bucket = "${aws_s3_bucket.object_bucket.bucket}" + bucket = aws_s3_bucket.test.bucket key = "test-key" - source = "%s" - source_hash = "${filemd5("%s")}" + source = %[2]q + source_hash = filemd5(%[2]q) } -`, randInt, source, source) +`, rName, source) } -func testAccAWSS3BucketObjectConfig_updateable(randInt int, bucketVersioning bool, source string) string { +func testAccAWSS3BucketObjectConfig_updateable(rName string, bucketVersioning bool, source string) string { return fmt.Sprintf(` resource "aws_s3_bucket" "object_bucket_3" { - bucket = "tf-object-test-bucket-%[1]d" + bucket = %[1]q versioning { enabled = %[2]t @@ -1655,7 +1656,7 @@ resource "aws_s3_bucket_object" "object" { source = %[3]q etag = filemd5(%[3]q) } -`, randInt, bucketVersioning, source) +`, rName, bucketVersioning, source) } func testAccAWSS3BucketObjectConfig_updateableViaAccessPoint(rName string, bucketVersioning bool, source string) string { @@ -1682,42 +1683,42 @@ resource "aws_s3_bucket_object" "test" { `, rName, bucketVersioning, source) } -func testAccAWSS3BucketObjectConfig_withKMSId(randInt int, source string) string { +func testAccAWSS3BucketObjectConfig_withKMSId(rName string, source string) string { return fmt.Sprintf(` resource "aws_kms_key" "kms_key_1" {} -resource "aws_s3_bucket" "object_bucket" { - bucket = "tf-object-test-bucket-%[1]d" +resource "aws_s3_bucket" "test" { + bucket = %[1]q } resource "aws_s3_bucket_object" "object" { - bucket = aws_s3_bucket.object_bucket.bucket + bucket = aws_s3_bucket.test.bucket key = "test-key" source = %[2]q kms_key_id = aws_kms_key.kms_key_1.arn } -`, randInt, source) +`, rName, source) } -func testAccAWSS3BucketObjectConfig_withSSE(randInt int, source string) string { +func testAccAWSS3BucketObjectConfig_withSSE(rName string, source string) string { return fmt.Sprintf(` -resource "aws_s3_bucket" "object_bucket" { - bucket = "tf-object-test-bucket-%[1]d" +resource "aws_s3_bucket" "test" { + bucket = %[1]q } resource "aws_s3_bucket_object" "object" { - bucket = aws_s3_bucket.object_bucket.bucket + bucket = aws_s3_bucket.test.bucket key = "test-key" source = %[2]q server_side_encryption = "AES256" } -`, randInt, source) +`, rName, source) } -func testAccAWSS3BucketObjectConfig_acl(randInt int, content, acl string) string { +func testAccAWSS3BucketObjectConfig_acl(rName string, content, acl string) string { return fmt.Sprintf(` -resource "aws_s3_bucket" "object_bucket" { - bucket = "tf-object-test-bucket-%[1]d" +resource "aws_s3_bucket" "test" { + bucket = %[1]q versioning { enabled = true @@ -1725,32 +1726,32 @@ resource "aws_s3_bucket" "object_bucket" { } resource "aws_s3_bucket_object" "object" { - bucket = aws_s3_bucket.object_bucket.bucket + bucket = aws_s3_bucket.test.bucket key = "test-key" content = %[2]q acl = %[3]q } -`, randInt, content, acl) +`, rName, content, acl) } -func testAccAWSS3BucketObjectConfig_storageClass(randInt int, storage_class string) string { +func testAccAWSS3BucketObjectConfig_storageClass(rName string, storage_class string) string { return fmt.Sprintf(` -resource "aws_s3_bucket" "object_bucket" { - bucket = "tf-object-test-bucket-%[1]d" +resource "aws_s3_bucket" "test" { + bucket = %[1]q } resource "aws_s3_bucket_object" "object" { - bucket = aws_s3_bucket.object_bucket.bucket + bucket = aws_s3_bucket.test.bucket key = "test-key" content = "some_bucket_content" storage_class = %[2]q } -`, randInt, storage_class) +`, rName, storage_class) } func testAccAWSS3BucketObjectConfig_withTags(rName, key, content string) string { return fmt.Sprintf(` -resource "aws_s3_bucket" "object_bucket" { +resource "aws_s3_bucket" "test" { bucket = %[1]q versioning { @@ -1759,7 +1760,7 @@ resource "aws_s3_bucket" "object_bucket" { } resource "aws_s3_bucket_object" "object" { - bucket = aws_s3_bucket.object_bucket.bucket + bucket = aws_s3_bucket.test.bucket key = %[2]q content = %[3]q @@ -1774,7 +1775,7 @@ resource "aws_s3_bucket_object" "object" { func testAccAWSS3BucketObjectConfig_withUpdatedTags(rName, key, content string) string { return fmt.Sprintf(` -resource "aws_s3_bucket" "object_bucket" { +resource "aws_s3_bucket" "test" { bucket = %[1]q versioning { @@ -1783,7 +1784,7 @@ resource "aws_s3_bucket" "object_bucket" { } resource "aws_s3_bucket_object" "object" { - bucket = aws_s3_bucket.object_bucket.bucket + bucket = aws_s3_bucket.test.bucket key = %[2]q content = %[3]q @@ -1799,7 +1800,7 @@ resource "aws_s3_bucket_object" "object" { func testAccAWSS3BucketObjectConfig_withNoTags(rName, key, content string) string { return fmt.Sprintf(` -resource "aws_s3_bucket" "object_bucket" { +resource "aws_s3_bucket" "test" { bucket = %[1]q versioning { @@ -1808,21 +1809,21 @@ resource "aws_s3_bucket" "object_bucket" { } resource "aws_s3_bucket_object" "object" { - bucket = aws_s3_bucket.object_bucket.bucket + bucket = aws_s3_bucket.test.bucket key = %[2]q content = %[3]q } `, rName, key, content) } -func testAccAWSS3BucketObjectConfig_withMetadata(randInt int, metadataKey1, metadataValue1, metadataKey2, metadataValue2 string) string { +func testAccAWSS3BucketObjectConfig_withMetadata(rName string, metadataKey1, metadataValue1, metadataKey2, metadataValue2 string) string { return fmt.Sprintf(` -resource "aws_s3_bucket" "object_bucket" { - bucket = "tf-object-test-bucket-%[1]d" +resource "aws_s3_bucket" "test" { + bucket = %[1]q } resource "aws_s3_bucket_object" "object" { - bucket = aws_s3_bucket.object_bucket.bucket + bucket = aws_s3_bucket.test.bucket key = "test-key" metadata = { @@ -1830,13 +1831,13 @@ resource "aws_s3_bucket_object" "object" { %[4]s = %[5]q } } -`, randInt, metadataKey1, metadataValue1, metadataKey2, metadataValue2) +`, rName, metadataKey1, metadataValue1, metadataKey2, metadataValue2) } -func testAccAWSS3BucketObjectConfig_noObjectLockLegalHold(randInt int, content string) string { +func testAccAWSS3BucketObjectConfig_noObjectLockLegalHold(rName string, content string) string { return fmt.Sprintf(` -resource "aws_s3_bucket" "object_bucket" { - bucket = "tf-object-test-bucket-%[1]d" +resource "aws_s3_bucket" "test" { + bucket = %[1]q versioning { enabled = true @@ -1848,18 +1849,18 @@ resource "aws_s3_bucket" "object_bucket" { } resource "aws_s3_bucket_object" "object" { - bucket = aws_s3_bucket.object_bucket.bucket + bucket = aws_s3_bucket.test.bucket key = "test-key" content = %[2]q force_destroy = true } -`, randInt, content) +`, rName, content) } -func testAccAWSS3BucketObjectConfig_withObjectLockLegalHold(randInt int, content, legalHoldStatus string) string { +func testAccAWSS3BucketObjectConfig_withObjectLockLegalHold(rName string, content, legalHoldStatus string) string { return fmt.Sprintf(` -resource "aws_s3_bucket" "object_bucket" { - bucket = "tf-object-test-bucket-%[1]d" +resource "aws_s3_bucket" "test" { + bucket = %[1]q versioning { enabled = true @@ -1871,19 +1872,19 @@ resource "aws_s3_bucket" "object_bucket" { } resource "aws_s3_bucket_object" "object" { - bucket = aws_s3_bucket.object_bucket.bucket + bucket = aws_s3_bucket.test.bucket key = "test-key" content = %[2]q object_lock_legal_hold_status = %[3]q force_destroy = true } -`, randInt, content, legalHoldStatus) +`, rName, content, legalHoldStatus) } -func testAccAWSS3BucketObjectConfig_noObjectLockRetention(randInt int, content string) string { +func testAccAWSS3BucketObjectConfig_noObjectLockRetention(rName string, content string) string { return fmt.Sprintf(` -resource "aws_s3_bucket" "object_bucket" { - bucket = "tf-object-test-bucket-%[1]d" +resource "aws_s3_bucket" "test" { + bucket = %[1]q versioning { enabled = true @@ -1895,18 +1896,18 @@ resource "aws_s3_bucket" "object_bucket" { } resource "aws_s3_bucket_object" "object" { - bucket = aws_s3_bucket.object_bucket.bucket + bucket = aws_s3_bucket.test.bucket key = "test-key" content = %[2]q force_destroy = true } -`, randInt, content) +`, rName, content) } -func testAccAWSS3BucketObjectConfig_withObjectLockRetention(randInt int, content, retainUntilDate string) string { +func testAccAWSS3BucketObjectConfig_withObjectLockRetention(rName string, content, retainUntilDate string) string { return fmt.Sprintf(` -resource "aws_s3_bucket" "object_bucket" { - bucket = "tf-object-test-bucket-%[1]d" +resource "aws_s3_bucket" "test" { + bucket = %[1]q versioning { enabled = true @@ -1918,17 +1919,17 @@ resource "aws_s3_bucket" "object_bucket" { } resource "aws_s3_bucket_object" "object" { - bucket = aws_s3_bucket.object_bucket.bucket + bucket = aws_s3_bucket.test.bucket key = "test-key" content = %[2]q force_destroy = true object_lock_mode = "GOVERNANCE" object_lock_retain_until_date = %[3]q } -`, randInt, content, retainUntilDate) +`, rName, content, retainUntilDate) } -func testAccAWSS3BucketObjectConfig_NonVersioned(randInt int, source string) string { +func testAccAWSS3BucketObjectConfig_nonVersioned(rName string, source string) string { policy := `{ "Version": "2012-10-17", "Statement": [ @@ -1952,7 +1953,7 @@ func testAccAWSS3BucketObjectConfig_NonVersioned(randInt int, source string) str return testAccProviderConfigAssumeRolePolicy(policy) + fmt.Sprintf(` resource "aws_s3_bucket" "object_bucket_3" { - bucket = "tf-object-test-bucket-%[1]d" + bucket = %[1]q } resource "aws_s3_bucket_object" "object" { @@ -1961,39 +1962,39 @@ resource "aws_s3_bucket_object" "object" { source = %[2]q etag = filemd5(%[2]q) } -`, randInt, source) +`, rName, source) } -func testAccAWSS3BucketObjectConfig_objectBucketKeyEnabled(randInt int, content string) string { +func testAccAWSS3BucketObjectConfig_objectBucketKeyEnabled(rName string, content string) string { return fmt.Sprintf(` resource "aws_kms_key" "test" { description = "Encrypts test bucket objects" deletion_window_in_days = 7 } -resource "aws_s3_bucket" "object_bucket" { - bucket = "tf-object-test-bucket-%[1]d" +resource "aws_s3_bucket" "test" { + bucket = %[1]q } resource "aws_s3_bucket_object" "object" { - bucket = aws_s3_bucket.object_bucket.bucket + bucket = aws_s3_bucket.test.bucket key = "test-key" content = %q kms_key_id = aws_kms_key.test.arn bucket_key_enabled = true } -`, randInt, content) +`, rName, content) } -func testAccAWSS3BucketObjectConfig_bucketBucketKeyEnabled(randInt int, content string) string { +func testAccAWSS3BucketObjectConfig_bucketBucketKeyEnabled(rName string, content string) string { return fmt.Sprintf(` resource "aws_kms_key" "test" { description = "Encrypts test bucket objects" deletion_window_in_days = 7 } -resource "aws_s3_bucket" "object_bucket" { - bucket = "tf-object-test-bucket-%[1]d" +resource "aws_s3_bucket" "test" { + bucket = %[1]q server_side_encryption_configuration { rule { @@ -2007,22 +2008,22 @@ resource "aws_s3_bucket" "object_bucket" { } resource "aws_s3_bucket_object" "object" { - bucket = aws_s3_bucket.object_bucket.bucket + bucket = aws_s3_bucket.test.bucket key = "test-key" content = %q } -`, randInt, content) +`, rName, content) } -func testAccAWSS3BucketObjectConfig_defaultBucketSSE(randInt int, content string) string { +func testAccAWSS3BucketObjectConfig_defaultBucketSSE(rName string, content string) string { return fmt.Sprintf(` resource "aws_kms_key" "test" { description = "Encrypts test bucket objects" deletion_window_in_days = 7 } -resource "aws_s3_bucket" "object_bucket" { - bucket = "tf-object-test-bucket-%d" +resource "aws_s3_bucket" "test" { + bucket = %[1]q server_side_encryption_configuration { rule { apply_server_side_encryption_by_default { @@ -2034,9 +2035,9 @@ resource "aws_s3_bucket" "object_bucket" { } resource "aws_s3_bucket_object" "object" { - bucket = aws_s3_bucket.object_bucket.bucket + bucket = aws_s3_bucket.test.bucket key = "test-key" - content = %q + content = %[2]q } -`, randInt, content) +`, rName, content) } From 2ee18a55fb851c8592d4394996b2e87a20825d7f Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Mon, 12 Jul 2021 22:40:21 -0400 Subject: [PATCH 354/398] tests/r/s3_bucket_object: Fix tests --- aws/resource_aws_s3_bucket_object_test.go | 35 +++++++++++++---------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/aws/resource_aws_s3_bucket_object_test.go b/aws/resource_aws_s3_bucket_object_test.go index 561aa388a3f..33fc74424fc 100644 --- a/aws/resource_aws_s3_bucket_object_test.go +++ b/aws/resource_aws_s3_bucket_object_test.go @@ -4,7 +4,6 @@ import ( "encoding/base64" "fmt" "io" - "io/ioutil" "log" "os" "reflect" @@ -251,15 +250,21 @@ func TestAccAWSS3BucketObject_contentBase64(t *testing.T) { func TestAccAWSS3BucketObject_sourceHashTrigger(t *testing.T) { var obj, updated_obj s3.GetObjectOutput resourceName := "aws_s3_bucket_object.object" - source := testAccAWSS3BucketObjectCreateTempFile(t, "{anything will do }") - rewriteSourceF := func(*terraform.State) error { - if err := ioutil.WriteFile(source, []byte("{any other thing will do }"), 0644); err != nil { - os.Remove(source) + rName := acctest.RandomWithPrefix("tf-acc-test") + + startingData := "Ebben!" + changingData := "Ne andrò lontana" + + filename := testAccAWSS3BucketObjectCreateTempFile(t, startingData) + defer os.Remove(filename) + + rewriteFile := func(*terraform.State) error { + if err := os.WriteFile(filename, []byte(changingData), 0644); err != nil { + os.Remove(filename) t.Fatal(err) } return nil } - rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -268,22 +273,22 @@ func TestAccAWSS3BucketObject_sourceHashTrigger(t *testing.T) { Steps: []resource.TestStep{ { PreConfig: func() {}, - Config: testAccAWSS3BucketObjectConfig_sourceHashTrigger(rName, source), + Config: testAccAWSS3BucketObjectConfig_sourceHashTrigger(rName, filename), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &obj), - testAccCheckAWSS3BucketObjectBody(&obj, "{anything will do }"), - resource.TestCheckResourceAttr(resourceName, "source_hash", "7b006ff4d70f68cc65061acf2f802e6f"), - rewriteSourceF, + testAccCheckAWSS3BucketObjectBody(&obj, "Ebben!"), + resource.TestCheckResourceAttr(resourceName, "source_hash", "7c7e02a79f28968882bb1426c8f8bfc6"), + rewriteFile, ), ExpectNonEmptyPlan: true, }, { PreConfig: func() {}, - Config: testAccAWSS3BucketObjectConfig_sourceHashTrigger(rName, source), + Config: testAccAWSS3BucketObjectConfig_sourceHashTrigger(rName, filename), Check: resource.ComposeTestCheckFunc( testAccCheckAWSS3BucketObjectExists(resourceName, &updated_obj), - testAccCheckAWSS3BucketObjectBody(&updated_obj, "{any other thing will do }"), - resource.TestCheckResourceAttr(resourceName, "source_hash", "77a736aa9e04d0dc96b9b30894963983"), + testAccCheckAWSS3BucketObjectBody(&updated_obj, "Ne andrò lontana"), + resource.TestCheckResourceAttr(resourceName, "source_hash", "cffc5e20de2d21764145b1124c9b337b"), ), }, }, @@ -1260,12 +1265,12 @@ func TestAccAWSS3BucketObject_ignoreTags(t *testing.T) { testAccCheckAWSS3BucketObjectExists(resourceName, &obj), testAccCheckAWSS3BucketObjectBody(&obj, "stuff"), resource.TestCheckResourceAttr(resourceName, "tags.%", "3"), - resource.TestCheckResourceAttr(resourceName, "tags.Key1", "AAA"), + resource.TestCheckResourceAttr(resourceName, "tags.Key1", "A@AA"), resource.TestCheckResourceAttr(resourceName, "tags.Key2", "BBB"), resource.TestCheckResourceAttr(resourceName, "tags.Key3", "CCC"), testAccCheckAWSS3BucketObjectCheckTags(resourceName, map[string]string{ "ignorekey1": "ignorevalue1", - "Key1": "AAA", + "Key1": "A@AA", "Key2": "BBB", "Key3": "CCC", }), From 73fad554a32a50a6937d0ba12de30709655d36c0 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Mon, 12 Jul 2021 22:40:43 -0400 Subject: [PATCH 355/398] docs/r/s3_bucket_object: Clean up documentation --- website/docs/r/s3_bucket_object.html.markdown | 65 +++++++++---------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/website/docs/r/s3_bucket_object.html.markdown b/website/docs/r/s3_bucket_object.html.markdown index f9934409c5a..9d53f250998 100644 --- a/website/docs/r/s3_bucket_object.html.markdown +++ b/website/docs/r/s3_bucket_object.html.markdown @@ -113,37 +113,35 @@ resource "aws_s3_bucket_object" "examplebucket_object" { -> **Note:** If you specify `content_encoding` you are responsible for encoding the body appropriately. `source`, `content`, and `content_base64` all expect already encoded/compressed bytes. -The following arguments are supported: +The following arguments are required: -* `bucket` - (Required) The name of the bucket to put the file in. Alternatively, an [S3 access point](https://docs.aws.amazon.com/AmazonS3/latest/dev/using-access-points.html) ARN can be specified. -* `key` - (Required) The name of the object once it is in the bucket. -* `source` - (Optional, conflicts with `content` and `content_base64`) The path to a file that will be read and uploaded as raw bytes for the object content. -* `content` - (Optional, conflicts with `source` and `content_base64`) Literal string value to use as the object content, which will be uploaded as UTF-8-encoded text. -* `content_base64` - (Optional, conflicts with `source` and `content`) Base64-encoded data that will be decoded and uploaded as raw bytes for the object content. This allows safely uploading non-UTF8 binary data, but is recommended only for small content such as the result of the `gzipbase64` function with small text strings. For larger objects, use `source` to stream the content from a disk file. -* `acl` - (Optional) The [canned ACL](https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl) to apply. Valid values are `private`, `public-read`, `public-read-write`, `aws-exec-read`, `authenticated-read`, `bucket-owner-read`, and `bucket-owner-full-control`. Defaults to `private`. -* `cache_control` - (Optional) Specifies caching behavior along the request/reply chain Read [w3c cache_control](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9) for further details. -* `content_disposition` - (Optional) Specifies presentational information for the object. Read [w3c content_disposition](http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.5.1) for further information. -* `content_encoding` - (Optional) Specifies what content encodings have been applied to the object and thus what decoding mechanisms must be applied to obtain the media-type referenced by the Content-Type header field. Read [w3c content encoding](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11) for further information. -* `content_language` - (Optional) The language the content is in e.g. en-US or en-GB. -* `content_type` - (Optional) A standard MIME type describing the format of the object data, e.g. application/octet-stream. All Valid MIME Types are valid for this input. -* `website_redirect` - (Optional) Specifies a target URL for [website redirect](http://docs.aws.amazon.com/AmazonS3/latest/dev/how-to-page-redirect.html). -* `storage_class` - (Optional) Specifies the desired [Storage Class](http://docs.aws.amazon.com/AmazonS3/latest/dev/storage-class-intro.html) -for the object. Can be either "`STANDARD`", "`REDUCED_REDUNDANCY`", "`ONEZONE_IA`", "`INTELLIGENT_TIERING`", "`GLACIER`", "`DEEP_ARCHIVE`", or "`STANDARD_IA`". Defaults to "`STANDARD`". -* `etag` - (Optional) Used to trigger updates. The only meaningful value is `${filemd5("path/to/file")}` (Terraform 0.11.12 or later) or `${md5(file("path/to/file"))}` (Terraform 0.11.11 or earlier). -This attribute is not compatible with KMS encryption, `kms_key_id` or `server_side_encryption = "aws:kms"`. -* `source_hash` - (Optional) Used to trigger updates based on source local changes. If used, must be set to `${filemd5("path/to/source")}` (Terraform 0.11.12 or later). This differs from `etag` since the value is stored in the state and does not come from AWS. Especially useful to address `etag` KMS encryption limitations. -* `server_side_encryption` - (Optional) Specifies server-side encryption of the object in S3. Valid values are "`AES256`" and "`aws:kms`". -* `kms_key_id` - (Optional) Amazon Resource Name (ARN) of the KMS Key to use for object encryption. If the S3 Bucket has server-side encryption enabled, that value will automatically be used. If referencing the -`aws_kms_key` resource, use the `arn` attribute. If referencing the `aws_kms_alias` data source or resource, use the `target_key_arn` attribute. Terraform will only perform drift detection if a configuration value -is provided. +* `bucket` - (Required) Name of the bucket to put the file in. Alternatively, an [S3 access point](https://docs.aws.amazon.com/AmazonS3/latest/dev/using-access-points.html) ARN can be specified. +* `key` - (Required) Name of the object once it is in the bucket. + +The following arguments are optional: + +* `acl` - (Optional) [Canned ACL](https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl) to apply. Valid values are `private`, `public-read`, `public-read-write`, `aws-exec-read`, `authenticated-read`, `bucket-owner-read`, and `bucket-owner-full-control`. Defaults to `private`. * `bucket_key_enabled` - (Optional) Whether or not to use [Amazon S3 Bucket Keys](https://docs.aws.amazon.com/AmazonS3/latest/dev/bucket-key.html) for SSE-KMS. -* `metadata` - (Optional) A map of keys/values to provision metadata (will be automatically prefixed by `x-amz-meta-`, note that only lowercase label are currently supported by the AWS Go API). -* `tags` - (Optional) A map of tags to assign to the object. If configured with a provider [`default_tags` configuration block](/docs/providers/aws/index.html#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. -* `force_destroy` - (Optional) Allow the object to be deleted by removing any legal hold on any object version. -Default is `false`. This value should be set to `true` only if the bucket has S3 object lock enabled. -* `object_lock_legal_hold_status` - (Optional) The [legal hold](https://docs.aws.amazon.com/AmazonS3/latest/dev/object-lock-overview.html#object-lock-legal-holds) status that you want to apply to the specified object. Valid values are `ON` and `OFF`. -* `object_lock_mode` - (Optional) The object lock [retention mode](https://docs.aws.amazon.com/AmazonS3/latest/dev/object-lock-overview.html#object-lock-retention-modes) that you want to apply to this object. Valid values are `GOVERNANCE` and `COMPLIANCE`. -* `object_lock_retain_until_date` - (Optional) The date and time, in [RFC3339 format](https://tools.ietf.org/html/rfc3339#section-5.8), when this object's object lock will [expire](https://docs.aws.amazon.com/AmazonS3/latest/dev/object-lock-overview.html#object-lock-retention-periods). +* `cache_control` - (Optional) Caching behavior along the request/reply chain Read [w3c cache_control](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9) for further details. +* `content_base64` - (Optional, conflicts with `source` and `content`) Base64-encoded data that will be decoded and uploaded as raw bytes for the object content. This allows safely uploading non-UTF8 binary data, but is recommended only for small content such as the result of the `gzipbase64` function with small text strings. For larger objects, use `source` to stream the content from a disk file. +* `content_disposition` - (Optional) Presentational information for the object. Read [w3c content_disposition](http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.5.1) for further information. +* `content_encoding` - (Optional) Content encodings that have been applied to the object and thus what decoding mechanisms must be applied to obtain the media-type referenced by the Content-Type header field. Read [w3c content encoding](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11) for further information. +* `content_language` - (Optional) Language the content is in e.g. en-US or en-GB. +* `content_type` - (Optional) Standard MIME type describing the format of the object data, e.g. application/octet-stream. All Valid MIME Types are valid for this input. +* `content` - (Optional, conflicts with `source` and `content_base64`) Literal string value to use as the object content, which will be uploaded as UTF-8-encoded text. +* `etag` - (Optional) Triggers updates when the value changes. The only meaningful value is `filemd5("path/to/file")` (Terraform 0.11.12 or later) or `${md5(file("path/to/file"))}` (Terraform 0.11.11 or earlier). This attribute is not compatible with KMS encryption, `kms_key_id` or `server_side_encryption = "aws:kms"` (see `source_hash` instead). +* `force_destroy` - (Optional) Whether to allow the object to be deleted by removing any legal hold on any object version. Default is `false`. This value should be set to `true` only if the bucket has S3 object lock enabled. +* `kms_key_id` - (Optional) ARN of the KMS Key to use for object encryption. If the S3 Bucket has server-side encryption enabled, that value will automatically be used. If referencing the `aws_kms_key` resource, use the `arn` attribute. If referencing the `aws_kms_alias` data source or resource, use the `target_key_arn` attribute. Terraform will only perform drift detection if a configuration value is provided. +* `metadata` - (Optional) Map of keys/values to provision metadata (will be automatically prefixed by `x-amz-meta-`, note that only lowercase label are currently supported by the AWS Go API). +* `object_lock_legal_hold_status` - (Optional) [Legal hold](https://docs.aws.amazon.com/AmazonS3/latest/dev/object-lock-overview.html#object-lock-legal-holds) status that you want to apply to the specified object. Valid values are `ON` and `OFF`. +* `object_lock_mode` - (Optional) Object lock [retention mode](https://docs.aws.amazon.com/AmazonS3/latest/dev/object-lock-overview.html#object-lock-retention-modes) that you want to apply to this object. Valid values are `GOVERNANCE` and `COMPLIANCE`. +* `object_lock_retain_until_date` - (Optional) Date and time, in [RFC3339 format](https://tools.ietf.org/html/rfc3339#section-5.8), when this object's object lock will [expire](https://docs.aws.amazon.com/AmazonS3/latest/dev/object-lock-overview.html#object-lock-retention-periods). +* `server_side_encryption` - (Optional) Server-side encryption of the object in S3. Valid values are "`AES256`" and "`aws:kms`". +* `source_hash` - (Optional) Triggers updates like `etag` but useful to address `etag` encryption limitations. Set using `filemd5("path/to/source")` (Terraform 0.11.12 or later). (The value is only stored in state and not saved by AWS.) +* `source` - (Optional, conflicts with `content` and `content_base64`) Path to a file that will be read and uploaded as raw bytes for the object content. +* `storage_class` - (Optional) [Storage Class](http://docs.aws.amazon.com/AmazonS3/latest/dev/storage-class-intro.html) for the object. Can be either "`STANDARD`", "`REDUCED_REDUNDANCY`", "`ONEZONE_IA`", "`INTELLIGENT_TIERING`", "`GLACIER`", "`DEEP_ARCHIVE`", or "`STANDARD_IA`". Defaults to "`STANDARD`". +* `tags` - (Optional) Map of tags to assign to the object. If configured with a provider [`default_tags` configuration block](/docs/providers/aws/index.html#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. +* `website_redirect` - (Optional) Target URL for [website redirect](http://docs.aws.amazon.com/AmazonS3/latest/dev/how-to-page-redirect.html). If no content is provided through `source`, `content` or `content_base64`, then the object will be empty. @@ -153,8 +151,7 @@ If no content is provided through `source`, `content` or `content_base64`, then In addition to all arguments above, the following attributes are exported: -* `id` - the `key` of the resource supplied above -* `etag` - the ETag generated for the object (an MD5 sum of the object content). For plaintext objects or objects encrypted with an AWS-managed key, the hash is an MD5 digest of the object data. For objects encrypted with a KMS key or objects created by either the Multipart Upload or Part Copy operation, the hash is not an MD5 digest, regardless of the method of encryption. More information on possible values can be found on [Common Response Headers](https://docs.aws.amazon.com/AmazonS3/latest/API/RESTCommonResponseHeaders.html). -* `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](/docs/providers/aws/index.html#default_tags-configuration-block). -* `version_id` - A unique version ID value for the object, if bucket versioning -is enabled. +* `etag` - ETag generated for the object (an MD5 sum of the object content). For plaintext objects or objects encrypted with an AWS-managed key, the hash is an MD5 digest of the object data. For objects encrypted with a KMS key or objects created by either the Multipart Upload or Part Copy operation, the hash is not an MD5 digest, regardless of the method of encryption. More information on possible values can be found on [Common Response Headers](https://docs.aws.amazon.com/AmazonS3/latest/API/RESTCommonResponseHeaders.html). +* `id` - `key` of the resource supplied above +* `tags_all` - Map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](/docs/providers/aws/index.html#default_tags-configuration-block). +* `version_id` - Unique version ID value for the object, if bucket versioning is enabled. \ No newline at end of file From 76e6415a0d6ce2a247b236e8eb131a0fc7667d27 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Mon, 12 Jul 2021 22:44:14 -0400 Subject: [PATCH 356/398] r/s3_bucket_object: Add changelog --- .changelog/11522.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/11522.txt diff --git a/.changelog/11522.txt b/.changelog/11522.txt new file mode 100644 index 00000000000..1eaade7f178 --- /dev/null +++ b/.changelog/11522.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_s3_bucket_object: Add `source_hash` argument to compliment `etag`'s encryption limitations +``` \ No newline at end of file From efc078e6fe0302b9df2a52fdd07e0a030fb6f012 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Mon, 12 Jul 2021 22:47:18 -0400 Subject: [PATCH 357/398] tests/r/s3_bucket_object: Add errorcheck --- aws/resource_aws_s3_bucket_object_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/aws/resource_aws_s3_bucket_object_test.go b/aws/resource_aws_s3_bucket_object_test.go index 33fc74424fc..e7d2c0d6dae 100644 --- a/aws/resource_aws_s3_bucket_object_test.go +++ b/aws/resource_aws_s3_bucket_object_test.go @@ -268,6 +268,7 @@ func TestAccAWSS3BucketObject_sourceHashTrigger(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, s3.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckAWSS3BucketObjectDestroy, Steps: []resource.TestStep{ From 35560820a22d7cb9f90b31d635afec2de97b943a Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Mon, 12 Jul 2021 22:36:52 -0700 Subject: [PATCH 358/398] Corrects Semgrep rule for `!d.IsNewResource()` check --- .semgrep.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.semgrep.yml b/.semgrep.yml index 575fc5949d2..45f0978901f 100644 --- a/.semgrep.yml +++ b/.semgrep.yml @@ -348,7 +348,7 @@ rules: return nil } - pattern-not-inside: | - if <... d.IsNewResource() ...> { ... } + if <... !d.IsNewResource() ...> { ... } severity: WARNING - id: helper-schema-resource-Retry-without-TimeoutError-check From 4283c8a4c9f5e7589b422ec761c36eca25dd823b Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Mon, 12 Jul 2021 22:38:16 -0700 Subject: [PATCH 359/398] Addresses PR feedback --- .semgrep.yml | 12 +++++++++--- aws/internal/service/ec2/errors.go | 6 ++++++ aws/resource_aws_default_security_group.go | 15 +++++---------- ...e_aws_globalaccelerator_endpoint_group_test.go | 4 +--- aws/resource_aws_security_group.go | 11 ++++++----- aws/resource_aws_security_group_rule.go | 15 +++++++-------- 6 files changed, 34 insertions(+), 29 deletions(-) diff --git a/.semgrep.yml b/.semgrep.yml index 45f0978901f..718408630ad 100644 --- a/.semgrep.yml +++ b/.semgrep.yml @@ -327,10 +327,16 @@ rules: - aws/resource_aws_athena_*.go - aws/resource_aws_autoscaling_*.go - aws/resource_aws_autoscalingplans_scaling_plan.go - - aws/resource_aws_[b-g]*.go + - aws/resource_aws_[b-ce-g]*.go + - aws/resource_aws_d[a-df-z]*.go + - aws/resource_aws_devicefarm*.go - aws/resource_aws_i*.go - - aws/resource_aws_[k-t]*.go - - aws/resource_aws_[v-x]*.go + - aws/resource_aws_[k-r]*.go + - aws/resource_aws_s[a-df-z3]*.go + - aws/resource_aws_se[d-z]*.go + - aws/resource_aws_sec[a-t]*.go + - aws/resource_aws_securityhub*.go + - aws/resource_aws_[t-x]*.go include: - aws/resource*.go patterns: diff --git a/aws/internal/service/ec2/errors.go b/aws/internal/service/ec2/errors.go index e4c94a55408..d7f9a9a110d 100644 --- a/aws/internal/service/ec2/errors.go +++ b/aws/internal/service/ec2/errors.go @@ -83,6 +83,12 @@ const ( InvalidVpnGatewayIDNotFound = "InvalidVpnGatewayID.NotFound" ) +const ( + ErrCodeInvalidPermissionDuplicate = "InvalidPermission.Duplicate" + ErrCodeInvalidPermissionMalformed = "InvalidPermission.Malformed" + ErrCodeInvalidPermissionNotFound = "InvalidPermission.NotFound" +) + func UnsuccessfulItemError(apiObject *ec2.UnsuccessfulItemError) error { if apiObject == nil { return nil diff --git a/aws/resource_aws_default_security_group.go b/aws/resource_aws_default_security_group.go index 97be5dc68d3..d08c0b858ba 100644 --- a/aws/resource_aws_default_security_group.go +++ b/aws/resource_aws_default_security_group.go @@ -268,7 +268,7 @@ func resourceAwsDefaultSecurityGroupRead(d *schema.ResourceData, meta interface{ ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig group, err := finder.SecurityGroupByID(conn, d.Id()) - if tfresource.NotFound(err) { + if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] Security group (%s) not found, removing from state", d.Id()) d.SetId("") return nil @@ -328,24 +328,19 @@ func resourceAwsDefaultSecurityGroupUpdate(d *schema.ResourceData, meta interfac conn := meta.(*AWSClient).ec2conn group, err := finder.SecurityGroupByID(conn, d.Id()) - if tfresource.NotFound(err) { - log.Printf("[WARN] Security group (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } if err != nil { - return err + return fmt.Errorf("error updating Default Security Group (%s): %w", d.Id(), err) } err = resourceAwsSecurityGroupUpdateRules(d, "ingress", meta, group) if err != nil { - return err + return fmt.Errorf("error updating Default Security Group (%s): %w", d.Id(), err) } if d.Get("vpc_id") != nil { err = resourceAwsSecurityGroupUpdateRules(d, "egress", meta, group) if err != nil { - return err + return fmt.Errorf("error updating Default Security Group (%s): %w", d.Id(), err) } } @@ -353,7 +348,7 @@ func resourceAwsDefaultSecurityGroupUpdate(d *schema.ResourceData, meta interfac o, n := d.GetChange("tags_all") if err := keyvaluetags.Ec2UpdateTags(conn, d.Id(), o, n); err != nil { - return fmt.Errorf("error updating EC2 Security Group (%s) tags: %w", d.Id(), err) + return fmt.Errorf("error updating Default Security Group (%s) tags: %w", d.Id(), err) } } diff --git a/aws/resource_aws_globalaccelerator_endpoint_group_test.go b/aws/resource_aws_globalaccelerator_endpoint_group_test.go index aac2d49854b..0c12f664605 100644 --- a/aws/resource_aws_globalaccelerator_endpoint_group_test.go +++ b/aws/resource_aws_globalaccelerator_endpoint_group_test.go @@ -1,7 +1,6 @@ package aws import ( - "errors" "fmt" "regexp" "testing" @@ -472,8 +471,7 @@ func testAccCheckGlobalAcceleratorEndpointGroupDeleteGlobalAcceleratorSecurityGr conn := testAccProvider.Meta().(*AWSClient).ec2conn sg, err := ec2finder.SecurityGroupByNameAndVpcID(conn, "GlobalAccelerator", aws.StringValue(vpc.VpcId)) - var nfe *resource.NotFoundError - if errors.As(err, &nfe) { + if tfresource.NotFound(err) { // Already gone. return nil } diff --git a/aws/resource_aws_security_group.go b/aws/resource_aws_security_group.go index ffb90175518..784f43dbf93 100644 --- a/aws/resource_aws_security_group.go +++ b/aws/resource_aws_security_group.go @@ -20,6 +20,7 @@ import ( "github.com/terraform-providers/terraform-provider-aws/aws/internal/hashcode" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" "github.com/terraform-providers/terraform-provider-aws/aws/internal/naming" + tfec2 "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2/finder" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2/waiter" ) @@ -328,7 +329,7 @@ func resourceAwsSecurityGroupCreate(d *schema.ResourceData, meta interface{}) er if err != nil { //If we have a NotFound or InvalidParameterValue, then we are trying to remove the default IPv6 egress of a non-IPv6 //enabled SG - if !tfawserr.ErrCodeEquals(err, "InvalidPermission.NotFound") && !tfawserr.ErrMessageContains(err, "InvalidParameterValue", "remote-ipv6-range") { + if !tfawserr.ErrCodeEquals(err, tfec2.ErrCodeInvalidPermissionNotFound) && !tfawserr.ErrMessageContains(err, tfec2.ErrCodeInvalidParameterValue, "remote-ipv6-range") { return fmt.Errorf("Error revoking default IPv6 egress rule for Security Group (%s): %w", d.Id(), err) } } @@ -345,7 +346,7 @@ func resourceAwsSecurityGroupRead(d *schema.ResourceData, meta interface{}) erro sg, err := finder.SecurityGroupByID(conn, d.Id()) var nfe *resource.NotFoundError - if errors.As(err, &nfe) { + if !d.IsNewResource() && errors.As(err, &nfe) { log.Printf("[WARN] Security group (%s) not found, removing from state", d.Id()) d.SetId("") return nil @@ -412,13 +413,13 @@ func resourceAwsSecurityGroupUpdate(d *schema.ResourceData, meta interface{}) er err = resourceAwsSecurityGroupUpdateRules(d, "ingress", meta, group) if err != nil { - return err + return fmt.Errorf("error updating Security Group (%s): %w", d.Id(), err) } if d.Get("vpc_id") != nil { err = resourceAwsSecurityGroupUpdateRules(d, "egress", meta, group) if err != nil { - return err + return fmt.Errorf("error updating Security Group (%s): %w", d.Id(), err) } } @@ -426,7 +427,7 @@ func resourceAwsSecurityGroupUpdate(d *schema.ResourceData, meta interface{}) er o, n := d.GetChange("tags_all") if err := keyvaluetags.Ec2UpdateTags(conn, d.Id(), o, n); err != nil { - return fmt.Errorf("error updating EC2 Security Group (%s) tags: %w", d.Id(), err) + return fmt.Errorf("error updating Security Group (%s) tags: %w", d.Id(), err) } } diff --git a/aws/resource_aws_security_group_rule.go b/aws/resource_aws_security_group_rule.go index 7f3bfdf8c4d..e6844c405bc 100644 --- a/aws/resource_aws_security_group_rule.go +++ b/aws/resource_aws_security_group_rule.go @@ -16,6 +16,7 @@ import ( "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/hashcode" + tfec2 "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2/finder" "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) @@ -211,7 +212,7 @@ func resourceAwsSecurityGroupRuleCreate(d *schema.ResourceData, meta interface{} return fmt.Errorf("Security Group Rule must be type 'ingress' or type 'egress'") } - if tfawserr.ErrCodeEquals(autherr, "InvalidPermission.Duplicate") { + if tfawserr.ErrCodeEquals(autherr, tfec2.ErrCodeInvalidPermissionDuplicate) { return fmt.Errorf(`[WARN] A duplicate Security Group rule was found on (%s). This may be a side effect of a now-fixed Terraform issue causing two security groups with identical attributes but different source_security_group_ids to overwrite each @@ -281,7 +282,7 @@ func resourceAwsSecurityGroupRuleRead(d *schema.ResourceData, meta interface{}) conn := meta.(*AWSClient).ec2conn sg_id := d.Get("security_group_id").(string) sg, err := finder.SecurityGroupByID(conn, sg_id) - if tfresource.NotFound(err) { + if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] Security Group (%s) not found, removing Rule (%s) from state", sg_id, d.Id()) d.SetId("") return nil @@ -308,18 +309,16 @@ func resourceAwsSecurityGroupRuleRead(d *schema.ResourceData, meta interface{}) return err } - if len(rules) == 0 { - log.Printf("[WARN] No %s rules were found for Security Group (%s) looking for Security Group Rule (%s)", - ruleType, *sg.GroupName, d.Id()) + if !d.IsNewResource() && len(rules) == 0 { + log.Printf("[WARN] No %s rules were found for Security Group (%s) looking for Security Group Rule (%s)", ruleType, aws.StringValue(sg.GroupName), d.Id()) d.SetId("") return nil } rule = findRuleMatch(p, rules, isVPC) - if rule == nil { - log.Printf("[DEBUG] Unable to find matching %s Security Group Rule (%s) for Group %s", - ruleType, d.Id(), sg_id) + if !d.IsNewResource() && rule == nil { + log.Printf("[DEBUG] Unable to find matching %s Security Group Rule (%s) for Group %s", ruleType, d.Id(), sg_id) d.SetId("") return nil } From e40cebfc75c8c82560d172ae1c2dade8036541fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Jul 2021 06:06:03 +0000 Subject: [PATCH 360/398] build(deps): bump integrations/github in /infrastructure/repository Bumps [integrations/github](https://github.com/integrations/terraform-provider-github) from 4.12.1 to 4.12.2. - [Release notes](https://github.com/integrations/terraform-provider-github/releases) - [Changelog](https://github.com/integrations/terraform-provider-github/blob/main/CHANGELOG.md) - [Commits](https://github.com/integrations/terraform-provider-github/compare/v4.12.1...v4.12.2) --- updated-dependencies: - dependency-name: integrations/github dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- infrastructure/repository/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/repository/main.tf b/infrastructure/repository/main.tf index d10b66d7471..f5636948278 100644 --- a/infrastructure/repository/main.tf +++ b/infrastructure/repository/main.tf @@ -10,7 +10,7 @@ terraform { required_providers { github = { source = "integrations/github" - version = "4.12.1" + version = "4.12.2" } } From 832a220ae144d4ae7077356cb173c4cefed3dc14 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Jul 2021 06:09:53 +0000 Subject: [PATCH 361/398] build(deps): bump github.com/aws/aws-sdk-go from 1.39.4 to 1.39.5 Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.39.4 to 1.39.5. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Changelog](https://github.com/aws/aws-sdk-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.39.4...v1.39.5) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 13234f81e5e..b5a2ef936f5 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.16 require ( github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect - github.com/aws/aws-sdk-go v1.39.4 + github.com/aws/aws-sdk-go v1.39.5 github.com/beevik/etree v1.1.0 github.com/fatih/color v1.9.0 // indirect github.com/hashicorp/aws-sdk-go-base v0.7.1 diff --git a/go.sum b/go.sum index fc92a009e9d..4ae44b94f00 100644 --- a/go.sum +++ b/go.sum @@ -66,8 +66,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= github.com/aws/aws-sdk-go v1.25.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.31.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/aws/aws-sdk-go v1.39.4 h1:nXBChUaG5cinrl3yg4/rUyssOOLH/ohk4S9K03kJirE= -github.com/aws/aws-sdk-go v1.39.4/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= +github.com/aws/aws-sdk-go v1.39.5 h1:yoJEE1NJxbpZ3CtPxvOSFJ9ByxiXmBTKk8J+XU5ldtg= +github.com/aws/aws-sdk-go v1.39.5/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= From 922b885e0aa65d45ff4ef96866eedbe880b7b8b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Jul 2021 06:10:38 +0000 Subject: [PATCH 362/398] build(deps): bump github.com/aws/aws-sdk-go in /awsproviderlint Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.39.4 to 1.39.5. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Changelog](https://github.com/aws/aws-sdk-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.39.4...v1.39.5) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- awsproviderlint/go.mod | 2 +- awsproviderlint/go.sum | 4 ++-- .../github.com/aws/aws-sdk-go/aws/endpoints/defaults.go | 2 ++ .../vendor/github.com/aws/aws-sdk-go/aws/version.go | 2 +- awsproviderlint/vendor/modules.txt | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/awsproviderlint/go.mod b/awsproviderlint/go.mod index 1a77c9b93d8..060ecbee7f5 100644 --- a/awsproviderlint/go.mod +++ b/awsproviderlint/go.mod @@ -3,7 +3,7 @@ module github.com/terraform-providers/terraform-provider-aws/awsproviderlint go 1.16 require ( - github.com/aws/aws-sdk-go v1.39.4 + github.com/aws/aws-sdk-go v1.39.5 github.com/bflad/tfproviderlint v0.27.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.7.0 golang.org/x/tools v0.0.0-20201028111035-eafbe7b904eb diff --git a/awsproviderlint/go.sum b/awsproviderlint/go.sum index b9de0de284d..58a3527e686 100644 --- a/awsproviderlint/go.sum +++ b/awsproviderlint/go.sum @@ -70,8 +70,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= github.com/aws/aws-sdk-go v1.25.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.39.4 h1:nXBChUaG5cinrl3yg4/rUyssOOLH/ohk4S9K03kJirE= -github.com/aws/aws-sdk-go v1.39.4/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= +github.com/aws/aws-sdk-go v1.39.5 h1:yoJEE1NJxbpZ3CtPxvOSFJ9ByxiXmBTKk8J+XU5ldtg= +github.com/aws/aws-sdk-go v1.39.5/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/bflad/gopaniccheck v0.1.0 h1:tJftp+bv42ouERmUMWLoUn/5bi/iQZjHPznM00cP/bU= github.com/bflad/gopaniccheck v0.1.0/go.mod h1:ZCj2vSr7EqVeDaqVsWN4n2MwdROx1YL+LFo47TSWtsA= github.com/bflad/tfproviderlint v0.27.0 h1:KXF+dYaWJ/OSVyWIrk2NIYgQBMDDSOC4VQB/P+P5nhI= diff --git a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go index 60ff236dfd5..2dcc49e7fda 100644 --- a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go +++ b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go @@ -3582,6 +3582,8 @@ var awsPartition = partition{ }, Endpoints: endpoints{ "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-2": endpoint{}, }, }, "honeycode": service{ diff --git a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/version.go b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/version.go index 8b253ba02b1..f148c09d942 100644 --- a/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/version.go +++ b/awsproviderlint/vendor/github.com/aws/aws-sdk-go/aws/version.go @@ -5,4 +5,4 @@ package aws const SDKName = "aws-sdk-go" // SDKVersion is the version of this SDK -const SDKVersion = "1.39.4" +const SDKVersion = "1.39.5" diff --git a/awsproviderlint/vendor/modules.txt b/awsproviderlint/vendor/modules.txt index a9d77a9d5ef..ec3c23fb377 100644 --- a/awsproviderlint/vendor/modules.txt +++ b/awsproviderlint/vendor/modules.txt @@ -14,7 +14,7 @@ github.com/agext/levenshtein github.com/apparentlymart/go-textseg/v12/textseg # github.com/apparentlymart/go-textseg/v13 v13.0.0 github.com/apparentlymart/go-textseg/v13/textseg -# github.com/aws/aws-sdk-go v1.39.4 +# github.com/aws/aws-sdk-go v1.39.5 ## explicit github.com/aws/aws-sdk-go/aws github.com/aws/aws-sdk-go/aws/arn From 4f9b9cb397158cf0a7e377d406cde56414985c8d Mon Sep 17 00:00:00 2001 From: changelogbot Date: Tue, 13 Jul 2021 12:55:57 +0000 Subject: [PATCH 363/398] Update CHANGELOG.md for #20159 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5069ee03df..bc5478f213a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ ENHANCEMENTS: * resource/aws_guardduty_organization_configuration: Add `datasources` argument ([#15241](https://github.com/hashicorp/terraform-provider-aws/issues/15241)) * resource/aws_iam_access_key: Add encrypted SES SMTP password ([#19579](https://github.com/hashicorp/terraform-provider-aws/issues/19579)) * resource/aws_s3_bucket: Add the delete_marker_replication_status argument for V2 replication configurations ([#19323](https://github.com/hashicorp/terraform-provider-aws/issues/19323)) +* resource/aws_s3_bucket_object: Add `source_hash` argument to compliment `etag`'s encryption limitations ([#11522](https://github.com/hashicorp/terraform-provider-aws/issues/11522)) +* resource/aws_wafv2_web_acl: Support `scope_down_statement` on `managed_rule_group_statement` ([#19407](https://github.com/hashicorp/terraform-provider-aws/issues/19407)) BUG FIXES: From 48d2e61e1c6f2f876f4ad3fa17953e144f960bc9 Mon Sep 17 00:00:00 2001 From: Andrew Babichev Date: Tue, 29 Sep 2020 21:36:01 +0300 Subject: [PATCH 364/398] New resource: aws_securityhub_standards_control --- aws/provider.go | 1 + ...ource_aws_securityhub_standards_control.go | 139 ++++++++++++++++ ..._aws_securityhub_standards_control_test.go | 154 ++++++++++++++++++ ...securityhub_standards_subscription_test.go | 14 +- .../r/securityhub_standards_control.markdown | 54 ++++++ 5 files changed, 356 insertions(+), 6 deletions(-) create mode 100644 aws/resource_aws_securityhub_standards_control.go create mode 100644 aws/resource_aws_securityhub_standards_control_test.go create mode 100644 website/docs/r/securityhub_standards_control.markdown diff --git a/aws/provider.go b/aws/provider.go index bd2642be4db..60169a08e5b 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -1056,6 +1056,7 @@ func Provider() *schema.Provider { "aws_securityhub_member": resourceAwsSecurityHubMember(), "aws_securityhub_organization_admin_account": resourceAwsSecurityHubOrganizationAdminAccount(), "aws_securityhub_product_subscription": resourceAwsSecurityHubProductSubscription(), + "aws_securityhub_standards_control": resourceAwsSecurityHubStandardsControl(), "aws_securityhub_standards_subscription": resourceAwsSecurityHubStandardsSubscription(), "aws_servicecatalog_budget_resource_association": resourceAwsServiceCatalogBudgetResourceAssociation(), "aws_servicecatalog_constraint": resourceAwsServiceCatalogConstraint(), diff --git a/aws/resource_aws_securityhub_standards_control.go b/aws/resource_aws_securityhub_standards_control.go new file mode 100644 index 00000000000..c49bbfd49ff --- /dev/null +++ b/aws/resource_aws_securityhub_standards_control.go @@ -0,0 +1,139 @@ +package aws + +import ( + "context" + "log" + "path" + "strings" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" + "github.com/aws/aws-sdk-go/service/securityhub" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceAwsSecurityHubStandardsControl() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceAwsSecurityHubStandardsControlPut, + ReadContext: resourceAwsSecurityHubStandardsControlRead, + UpdateContext: resourceAwsSecurityHubStandardsControlPut, + DeleteContext: resourceAwsSecurityHubStandardsControlDelete, + + Schema: map[string]*schema.Schema{ + "standards_control_arn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArn, + }, + "control_status": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(securityhub.ControlStatus_Values(), false), + }, + "disabled_reason": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "control_id": { + Type: schema.TypeString, + Computed: true, + }, + "control_status_updated_at": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "related_requirements": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "remediation_url": { + Type: schema.TypeString, + Computed: true, + }, + "severity_rating": { + Type: schema.TypeString, + Computed: true, + }, + "title": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceAwsSecurityHubStandardsControlRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).securityhubconn + + a := d.Get("standards_control_arn").(string) + controlArn, err := arn.Parse(a) + if err != nil { + return diag.Errorf("error parsing standards control ARN %q", controlArn) + } + + subscriptionArn := path.Dir(strings.ReplaceAll(controlArn.String(), "control", "subscription")) + + log.Printf("[DEBUG] Read Security Hub standard control %s", controlArn) + + resp, err := conn.DescribeStandardsControls(&securityhub.DescribeStandardsControlsInput{ + StandardsSubscriptionArn: aws.String(subscriptionArn), + }) + if err != nil { + return diag.Errorf("error reading Security Hub standard controls within %q subscription: %s", subscriptionArn, err) + } + + for _, c := range resp.Controls { + if aws.StringValue(c.StandardsControlArn) != controlArn.String() { + continue + } + + d.Set("control_status", c.ControlStatus) + d.Set("control_status_updated_at", c.ControlStatusUpdatedAt.Format(time.RFC3339)) + d.Set("description", c.Description) + d.Set("disabled_reason", c.DisabledReason) + d.Set("severity_rating", c.SeverityRating) + d.Set("title", c.Title) + + if err := d.Set("related_requirements", flattenStringList(c.RelatedRequirements)); err != nil { + return diag.Errorf("error setting related_requirements: %s", err) + } + } + + return nil +} + +func resourceAwsSecurityHubStandardsControlPut(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).securityhubconn + + d.SetId(d.Get("standards_control_arn").(string)) + + log.Printf("[DEBUG] Update Security Hub standard control %s", d.Id()) + + _, err := conn.UpdateStandardsControl(&securityhub.UpdateStandardsControlInput{ + StandardsControlArn: aws.String(d.Get("standards_control_arn").(string)), + ControlStatus: aws.String(d.Get("control_status").(string)), + DisabledReason: aws.String(d.Get("disabled_reason").(string)), + }) + if err != nil { + d.SetId("") + return diag.Errorf("error updating Security Hub standard control %q: %s", d.Id(), err) + } + + return resourceAwsSecurityHubStandardsControlRead(ctx, d, meta) +} + +func resourceAwsSecurityHubStandardsControlDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + log.Printf("[WARN] Cannot delete Security Hub standard control. Terraform will remove this resource from the state.") + return nil +} diff --git a/aws/resource_aws_securityhub_standards_control_test.go b/aws/resource_aws_securityhub_standards_control_test.go new file mode 100644 index 00000000000..4f943abc993 --- /dev/null +++ b/aws/resource_aws_securityhub_standards_control_test.go @@ -0,0 +1,154 @@ +package aws + +import ( + "fmt" + "path" + "regexp" + "strings" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/securityhub" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccAWSSecurityHubStandardsControl_basic(t *testing.T) { + var standardsControl *securityhub.StandardsControl + + resourceName := "aws_securityhub_standards_control.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSecurityHubStandardsControlExists(resourceName, standardsControl), + Steps: []resource.TestStep{ + { + Config: testAccAWSSecurityHubStandardsControlConfig_basic(), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSSecurityHubStandardsControlExists(resourceName, standardsControl), + resource.TestCheckResourceAttr(resourceName, "control_status", "ENABLED"), + resource.TestMatchResourceAttr(resourceName, "control_status_updated_at", regexp.MustCompile(`\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z`)), + resource.TestCheckResourceAttr(resourceName, "description", "IAM password policies can prevent the reuse of a given password by the same user. It is recommended that the password policy prevent the reuse of passwords."), + resource.TestCheckResourceAttr(resourceName, "disabled_reason", ""), + resource.TestCheckResourceAttr(resourceName, "related_requirements.0", "CIS AWS Foundations 1.10"), + resource.TestCheckResourceAttr(resourceName, "severity_rating", "LOW"), + resource.TestCheckResourceAttr(resourceName, "title", "Ensure IAM password policy prevents password reuse"), + ), + }, + }, + }) +} + +func TestAccAWSSecurityHubStandardsControl_disabledControlStatus(t *testing.T) { + var standardsControl *securityhub.StandardsControl + + resourceName := "aws_securityhub_standards_control.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSecurityHubStandardsControlExists(resourceName, standardsControl), + Steps: []resource.TestStep{ + { + Config: testAccAWSSecurityHubStandardsControlConfig_disabledControlStatus(), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSSecurityHubStandardsControlExists(resourceName, standardsControl), + resource.TestCheckResourceAttr(resourceName, "control_status", "DISABLED"), + resource.TestCheckResourceAttr(resourceName, "disabled_reason", "We handle password policies within Okta"), + ), + }, + }, + }) +} + +func TestAccAWSSecurityHubStandardsControl_enabledControlStatusAndDisabledReason(t *testing.T) { + var standardsControl *securityhub.StandardsControl + + resourceName := "aws_securityhub_standards_control.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSecurityHubStandardsControlExists(resourceName, standardsControl), + Steps: []resource.TestStep{ + { + Config: testAccAWSSecurityHubStandardsControlConfig_enabledControlStatus(), + ExpectError: regexp.MustCompile("InvalidInputException: DisabledReason should not be given for action other than disabling control"), + }, + }, + }) +} + +func testAccCheckAWSSecurityHubStandardsControlExists(n string, control *securityhub.StandardsControl) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + conn := testAccProvider.Meta().(*AWSClient).securityhubconn + + arn := rs.Primary.ID + subscription_arn := path.Dir(strings.ReplaceAll(arn, "control", "subscription")) + + resp, err := conn.DescribeStandardsControls(&securityhub.DescribeStandardsControlsInput{ + StandardsSubscriptionArn: aws.String(subscription_arn), + }) + if err != nil { + return fmt.Errorf("error reading Security Hub %s standard controls: %s", subscription_arn, err) + } + + controlNotFound := true + + for _, c := range resp.Controls { + if aws.StringValue(c.StandardsControlArn) != arn { + continue + } + + controlNotFound = false + control = c + } + + if controlNotFound { + return fmt.Errorf("Security Hub %s standard control %s not found", subscription_arn, arn) + } + + return nil + } +} + +func testAccAWSSecurityHubStandardsControlConfig_basic() string { + return composeConfig( + testAccAWSSecurityHubStandardsSubscriptionConfig_basic, + ` +resource aws_securityhub_standards_control test { + standards_control_arn = format("%s/1.10", replace(aws_securityhub_standards_subscription.test.id, "subscription", "control")) + control_status = "ENABLED" +} +`) +} + +func testAccAWSSecurityHubStandardsControlConfig_disabledControlStatus() string { + return composeConfig( + testAccAWSSecurityHubStandardsSubscriptionConfig_basic, + ` +resource aws_securityhub_standards_control test { + standards_control_arn = format("%s/1.11", replace(aws_securityhub_standards_subscription.test.id, "subscription", "control")) + control_status = "DISABLED" + disabled_reason = "We handle password policies within Okta" +} +`) +} + +func testAccAWSSecurityHubStandardsControlConfig_enabledControlStatus() string { + return composeConfig( + testAccAWSSecurityHubStandardsSubscriptionConfig_basic, + ` +resource aws_securityhub_standards_control test { + standards_control_arn = format("%s/1.12", replace(aws_securityhub_standards_subscription.test.id, "subscription", "control")) + control_status = "ENABLED" + disabled_reason = "We handle password policies within Okta" +} +`) +} diff --git a/aws/resource_aws_securityhub_standards_subscription_test.go b/aws/resource_aws_securityhub_standards_subscription_test.go index f05b8f5a679..0e3bb4b5668 100644 --- a/aws/resource_aws_securityhub_standards_subscription_test.go +++ b/aws/resource_aws_securityhub_standards_subscription_test.go @@ -13,6 +13,8 @@ import ( func testAccAWSSecurityHubStandardsSubscription_basic(t *testing.T) { var standardsSubscription *securityhub.StandardsSubscription + resourceName := "aws_securityhub_standards_subscription.test" + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), @@ -22,11 +24,11 @@ func testAccAWSSecurityHubStandardsSubscription_basic(t *testing.T) { { Config: testAccAWSSecurityHubStandardsSubscriptionConfig_basic, Check: resource.ComposeTestCheckFunc( - testAccCheckAWSSecurityHubStandardsSubscriptionExists("aws_securityhub_standards_subscription.example", standardsSubscription), + testAccCheckAWSSecurityHubStandardsSubscriptionExists(resourceName, standardsSubscription), ), }, { - ResourceName: "aws_securityhub_standards_subscription.example", + ResourceName: resourceName, ImportState: true, ImportStateVerify: true, }, @@ -95,16 +97,16 @@ func testAccCheckAWSSecurityHubStandardsSubscriptionDestroy(s *terraform.State) } const testAccAWSSecurityHubStandardsSubscriptionConfig_empty = ` -resource "aws_securityhub_account" "example" {} +resource "aws_securityhub_account" "test" {} ` const testAccAWSSecurityHubStandardsSubscriptionConfig_basic = ` -resource "aws_securityhub_account" "example" {} +resource "aws_securityhub_account" "test" {} data "aws_partition" "current" {} -resource "aws_securityhub_standards_subscription" "example" { - depends_on = [aws_securityhub_account.example] +resource "aws_securityhub_standards_subscription" "test" { standards_arn = "arn:${data.aws_partition.current.partition}:securityhub:::ruleset/cis-aws-foundations-benchmark/v/1.2.0" + depends_on = [aws_securityhub_account.test] } ` diff --git a/website/docs/r/securityhub_standards_control.markdown b/website/docs/r/securityhub_standards_control.markdown new file mode 100644 index 00000000000..e5b90dc7860 --- /dev/null +++ b/website/docs/r/securityhub_standards_control.markdown @@ -0,0 +1,54 @@ +--- +subcategory: "Security Hub" +layout: "aws" +page_title: "AWS: aws_securityhub_standards_control" +description: |- + Enable/disable Security Hub standards controls. +--- + +# Resource: aws_securityhub_standards_control + +Disable/enable Security Hub standards control in the current region. + +The `aws_securityhub_standards_control` behaves differently from normal resources, in that +Terraform does not _create_ this resource, but instead "adopts" it +into management. When you _delete_ this resource configuration, Terraform "abandons" resource as is and just removes it from the state. + +## Example Usage + +```hcl +resource aws_securityhub_account example {} + +resource aws_securityhub_standards_subscription cis_aws_foundations_benchmark { + standards_arn = "arn:aws:securityhub:::ruleset/cis-aws-foundations-benchmark/v/1.2.0" + depends_on = [aws_securityhub_account.example] +} + +resource aws_securityhub_standards_control ensure_iam_password_policy_prevents_password_reuse { + arn = "arn:aws:securityhub:us-east-1:111111111111:control/cis-aws-foundations-benchmark/v/1.2.0/1.10" + control_status = "DISABLED" + disabled_reason = "We handle password policies within Okta" + depends_on = [aws_securityhub_standards_subscription.cis_aws_foundations_benchmark] +} +``` + +## Arguments Reference + +The following arguments are supported: + +* `standards_control_arn` - (Required) The standards control ARN. +* `control_status` – (Required) The control status could be `ENABLED` or `DISABLED`. You have to specify `disabled_reason` argument for `DISABLED` control status. +* `disabled_reason` – (Optional) A description of the reason why you are disabling a security standard control. If you specify this attribute, `control_status` will be set to `DISABLED` automatically. + +## Attributes Reference + +The following attributes are exported in addition to the arguments listed above: + +* `id` - The standard control ARN. +* `control_id` – The identifier of the security standard control. +* `control_status_updated_at` – The date and time that the status of the security standard control was most recently updated. +* `description` – The standard control longer description. Provides information about what the control is checking for. +* `related_requirements` – The list of requirements that are related to this control. +* `remediation_url` – A link to remediation information for the control in the Security Hub user documentation. +* `severity_rating` – The severity of findings generated from this security standard control. +* `title` – The standard control title. From 5899f97e78cc92cf2875bf82aa4985bb0e0ee0da Mon Sep 17 00:00:00 2001 From: Andrew Babichev Date: Mon, 9 Nov 2020 23:15:26 +0200 Subject: [PATCH 365/398] Gratify impi --- aws/resource_aws_securityhub_standards_control.go | 1 - 1 file changed, 1 deletion(-) diff --git a/aws/resource_aws_securityhub_standards_control.go b/aws/resource_aws_securityhub_standards_control.go index c49bbfd49ff..be5d817eb11 100644 --- a/aws/resource_aws_securityhub_standards_control.go +++ b/aws/resource_aws_securityhub_standards_control.go @@ -10,7 +10,6 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/securityhub" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" From bd64f13fd3924263bee1391fc1c04b59f4fbdd25 Mon Sep 17 00:00:00 2001 From: Andrew Babichev Date: Wed, 19 May 2021 17:24:15 +0300 Subject: [PATCH 366/398] Add ErrorCheck --- aws/resource_aws_securityhub_standards_control_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aws/resource_aws_securityhub_standards_control_test.go b/aws/resource_aws_securityhub_standards_control_test.go index 4f943abc993..24535392f62 100644 --- a/aws/resource_aws_securityhub_standards_control_test.go +++ b/aws/resource_aws_securityhub_standards_control_test.go @@ -20,6 +20,7 @@ func TestAccAWSSecurityHubStandardsControl_basic(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckAWSSecurityHubStandardsControlExists(resourceName, standardsControl), Steps: []resource.TestStep{ @@ -47,6 +48,7 @@ func TestAccAWSSecurityHubStandardsControl_disabledControlStatus(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckAWSSecurityHubStandardsControlExists(resourceName, standardsControl), Steps: []resource.TestStep{ @@ -69,6 +71,7 @@ func TestAccAWSSecurityHubStandardsControl_enabledControlStatusAndDisabledReason resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckAWSSecurityHubStandardsControlExists(resourceName, standardsControl), Steps: []resource.TestStep{ From f8c8a95c47decd23ff4a8181064c66129da10230 Mon Sep 17 00:00:00 2001 From: Andrew Babichev Date: Wed, 19 May 2021 17:24:24 +0300 Subject: [PATCH 367/398] Fix doc with ```terraform --- website/docs/r/securityhub_standards_control.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/securityhub_standards_control.markdown b/website/docs/r/securityhub_standards_control.markdown index e5b90dc7860..f0065775336 100644 --- a/website/docs/r/securityhub_standards_control.markdown +++ b/website/docs/r/securityhub_standards_control.markdown @@ -16,7 +16,7 @@ into management. When you _delete_ this resource configuration, Terraform "aband ## Example Usage -```hcl +```terraform resource aws_securityhub_account example {} resource aws_securityhub_standards_subscription cis_aws_foundations_benchmark { From 246c64b408ada50d8118ea6c4c5529d1eebd9d55 Mon Sep 17 00:00:00 2001 From: Andrew Babichev Date: Wed, 19 May 2021 17:42:37 +0300 Subject: [PATCH 368/398] Fix to "Argument Reference" --- website/docs/r/securityhub_standards_control.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/securityhub_standards_control.markdown b/website/docs/r/securityhub_standards_control.markdown index f0065775336..4af7879e56c 100644 --- a/website/docs/r/securityhub_standards_control.markdown +++ b/website/docs/r/securityhub_standards_control.markdown @@ -32,7 +32,7 @@ resource aws_securityhub_standards_control ensure_iam_password_policy_prevents_p } ``` -## Arguments Reference +## Argument Reference The following arguments are supported: From 7c28583dd3154bda0fe0ee01382accb9349d1ada Mon Sep 17 00:00:00 2001 From: Andrew Babichev Date: Wed, 19 May 2021 17:56:42 +0300 Subject: [PATCH 369/398] Fix to "In addition to all arguments above, the following attributes are exported" --- website/docs/r/securityhub_standards_control.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/securityhub_standards_control.markdown b/website/docs/r/securityhub_standards_control.markdown index 4af7879e56c..54effa68336 100644 --- a/website/docs/r/securityhub_standards_control.markdown +++ b/website/docs/r/securityhub_standards_control.markdown @@ -42,7 +42,7 @@ The following arguments are supported: ## Attributes Reference -The following attributes are exported in addition to the arguments listed above: +In addition to all arguments above, the following attributes are exported: * `id` - The standard control ARN. * `control_id` – The identifier of the security standard control. From c578a7978176bb9004059ba424de565a0915cc43 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 13 Jul 2021 09:28:04 -0400 Subject: [PATCH 370/398] Add CHANGELOG entry. --- .changelog/14714.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/14714.txt diff --git a/.changelog/14714.txt b/.changelog/14714.txt new file mode 100644 index 00000000000..bf596739059 --- /dev/null +++ b/.changelog/14714.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_securityhub_standards_control +``` \ No newline at end of file From b7f2d94c00a7745ba62f94946e93429ff0f23ffb Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 13 Jul 2021 09:39:48 -0400 Subject: [PATCH 371/398] Correct argument name in example. --- website/docs/r/securityhub_standards_control.markdown | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/website/docs/r/securityhub_standards_control.markdown b/website/docs/r/securityhub_standards_control.markdown index 54effa68336..38388c87a69 100644 --- a/website/docs/r/securityhub_standards_control.markdown +++ b/website/docs/r/securityhub_standards_control.markdown @@ -25,10 +25,11 @@ resource aws_securityhub_standards_subscription cis_aws_foundations_benchmark { } resource aws_securityhub_standards_control ensure_iam_password_policy_prevents_password_reuse { - arn = "arn:aws:securityhub:us-east-1:111111111111:control/cis-aws-foundations-benchmark/v/1.2.0/1.10" - control_status = "DISABLED" - disabled_reason = "We handle password policies within Okta" - depends_on = [aws_securityhub_standards_subscription.cis_aws_foundations_benchmark] + standards_control_arn = "arn:aws:securityhub:us-east-1:111111111111:control/cis-aws-foundations-benchmark/v/1.2.0/1.10" + control_status = "DISABLED" + disabled_reason = "We handle password policies within Okta" + + depends_on = [aws_securityhub_standards_subscription.cis_aws_foundations_benchmark] } ``` From 48cb7198f2ef0e4debe411673c970bbe6b34ad5c Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 13 Jul 2021 12:17:02 -0400 Subject: [PATCH 372/398] aws_securityhub_standards_control: Use internal finder package and serialize tests. --- aws/internal/service/securityhub/arn.go | 44 ++++++++ aws/internal/service/securityhub/arn_test.go | 65 +++++++++++ .../service/securityhub/finder/finder.go | 81 ++++++++++++++ .../service/securityhub/waiter/status.go | 21 ++++ .../service/securityhub/waiter/waiter.go | 37 +++++++ ...ource_aws_securityhub_standards_control.go | 103 +++++++++--------- ..._aws_securityhub_standards_control_test.go | 78 ++++++------- ..._aws_securityhub_standards_subscription.go | 54 +++++---- ...securityhub_standards_subscription_test.go | 61 ++++++----- aws/resource_aws_securityhub_test.go | 8 +- 10 files changed, 408 insertions(+), 144 deletions(-) create mode 100644 aws/internal/service/securityhub/arn.go create mode 100644 aws/internal/service/securityhub/arn_test.go diff --git a/aws/internal/service/securityhub/arn.go b/aws/internal/service/securityhub/arn.go new file mode 100644 index 00000000000..105541e3021 --- /dev/null +++ b/aws/internal/service/securityhub/arn.go @@ -0,0 +1,44 @@ +package securityhub + +import ( + "fmt" + "strings" + + "github.com/aws/aws-sdk-go/aws/arn" +) + +const ( + ARNSeparator = "/" + ARNService = "securityhub" +) + +// StandardsControlARNToStandardsSubscriptionARN converts a security standard control ARN to a subscription ARN. +func StandardsControlARNToStandardsSubscriptionARN(inputARN string) (string, error) { + parsedARN, err := arn.Parse(inputARN) + + if err != nil { + return "", fmt.Errorf("error parsing ARN (%s): %w", inputARN, err) + } + + if actual, expected := parsedARN.Service, ARNService; actual != expected { + return "", fmt.Errorf("expected service %s in ARN (%s), got: %s", expected, inputARN, actual) + } + + inputResourceParts := strings.Split(parsedARN.Resource, ARNSeparator) + + if actual, expected := len(inputResourceParts), 3; actual < expected { + return "", fmt.Errorf("expected at least %d resource parts in ARN (%s), got: %d", expected, inputARN, actual) + } + + outputResourceParts := append([]string{"subscription"}, inputResourceParts[1:len(inputResourceParts)-1]...) + + outputARN := arn.ARN{ + Partition: parsedARN.Partition, + Service: parsedARN.Service, + Region: parsedARN.Region, + AccountID: parsedARN.AccountID, + Resource: strings.Join(outputResourceParts, ARNSeparator), + }.String() + + return outputARN, nil +} diff --git a/aws/internal/service/securityhub/arn_test.go b/aws/internal/service/securityhub/arn_test.go new file mode 100644 index 00000000000..76a9e61994b --- /dev/null +++ b/aws/internal/service/securityhub/arn_test.go @@ -0,0 +1,65 @@ +package securityhub_test + +import ( + "regexp" + "testing" + + tfsecurityhub "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/securityhub" +) + +func TestStandardsControlARNToStandardsSubscriptionARN(t *testing.T) { + testCases := []struct { + TestName string + InputARN string + ExpectedError *regexp.Regexp + ExpectedARN string + }{ + { + TestName: "empty ARN", + InputARN: "", + ExpectedError: regexp.MustCompile(`error parsing ARN`), + }, + { + TestName: "unparsable ARN", + InputARN: "test", + ExpectedError: regexp.MustCompile(`error parsing ARN`), + }, + { + TestName: "invalid ARN service", + InputARN: "arn:aws:ec2:us-west-2:1234567890:control/cis-aws-foundations-benchmark/v/1.2.0/1.1", + ExpectedError: regexp.MustCompile(`expected service securityhub`), + }, + { + TestName: "invalid ARN resource parts", + InputARN: "arn:aws:securityhub:us-west-2:1234567890:control/cis-aws-foundations-benchmark", + ExpectedError: regexp.MustCompile(`expected at least 3 resource parts`), + }, + { + TestName: "valid ARN", + InputARN: "arn:aws:securityhub:us-west-2:1234567890:control/cis-aws-foundations-benchmark/v/1.2.0/1.1", + ExpectedARN: "arn:aws:securityhub:us-west-2:1234567890:subscription/cis-aws-foundations-benchmark/v/1.2.0", + }, + } + + for _, testCase := range testCases { + t.Run(testCase.TestName, func(t *testing.T) { + got, err := tfsecurityhub.StandardsControlARNToStandardsSubscriptionARN(testCase.InputARN) + + if err == nil && testCase.ExpectedError != nil { + t.Fatalf("expected error %s, got no error", testCase.ExpectedError.String()) + } + + if err != nil && testCase.ExpectedError == nil { + t.Fatalf("got unexpected error: %s", err) + } + + if err != nil && !testCase.ExpectedError.MatchString(err.Error()) { + t.Fatalf("expected error %s, got: %s", testCase.ExpectedError.String(), err) + } + + if got != testCase.ExpectedARN { + t.Errorf("got %s, expected %s", got, testCase.ExpectedARN) + } + }) + } +} diff --git a/aws/internal/service/securityhub/finder/finder.go b/aws/internal/service/securityhub/finder/finder.go index ecb245bb2ad..4b99b707f1c 100644 --- a/aws/internal/service/securityhub/finder/finder.go +++ b/aws/internal/service/securityhub/finder/finder.go @@ -5,6 +5,8 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/securityhub" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) func AdminAccount(conn *securityhub.SecurityHub, adminAccountID string) (*securityhub.AdminAccount, error) { @@ -51,3 +53,82 @@ func Insight(ctx context.Context, conn *securityhub.SecurityHub, arn string) (*s return output.Insights[0], nil } + +func StandardsControlByStandardsSubscriptionARNAndStandardsControlARN(ctx context.Context, conn *securityhub.SecurityHub, standardsSubscriptionARN, standardsControlARN string) (*securityhub.StandardsControl, error) { + input := &securityhub.DescribeStandardsControlsInput{ + StandardsSubscriptionArn: aws.String(standardsSubscriptionARN), + } + var output *securityhub.StandardsControl + + err := conn.DescribeStandardsControlsPagesWithContext(ctx, input, func(page *securityhub.DescribeStandardsControlsOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, control := range page.Controls { + if aws.StringValue(control.StandardsControlArn) == standardsControlARN { + output = control + + return false + } + } + + return !lastPage + }) + + if tfawserr.ErrCodeEquals(err, securityhub.ErrCodeResourceNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, &resource.NotFoundError{ + Message: "Empty result", + LastRequest: input, + } + } + + return output, nil +} + +func StandardsSubscriptionByARN(conn *securityhub.SecurityHub, arn string) (*securityhub.StandardsSubscription, error) { + input := &securityhub.GetEnabledStandardsInput{ + StandardsSubscriptionArns: aws.StringSlice([]string{arn}), + } + + output, err := conn.GetEnabledStandards(input) + + if tfawserr.ErrCodeEquals(err, securityhub.ErrCodeResourceNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if output == nil || len(output.StandardsSubscriptions) == 0 || output.StandardsSubscriptions[0] == nil { + return nil, &resource.NotFoundError{ + Message: "Empty result", + LastRequest: input, + } + } + + // TODO Check for multiple results. + // TODO https://github.com/hashicorp/terraform-provider-aws/pull/17613. + + subscription := output.StandardsSubscriptions[0] + + if status := aws.StringValue(subscription.StandardsStatus); status == securityhub.StandardsStatusFailed { + return nil, &resource.NotFoundError{ + Message: status, + LastRequest: input, + } + } + + return subscription, nil +} diff --git a/aws/internal/service/securityhub/waiter/status.go b/aws/internal/service/securityhub/waiter/status.go index 6ab9c336004..d377ec257b3 100644 --- a/aws/internal/service/securityhub/waiter/status.go +++ b/aws/internal/service/securityhub/waiter/status.go @@ -5,6 +5,7 @@ import ( "github.com/aws/aws-sdk-go/service/securityhub" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/securityhub/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) const ( @@ -13,6 +14,8 @@ const ( // AdminStatus Unknown AdminStatusUnknown = "Unknown" + + StandardsStatusNotFound = "NotFound" ) // AdminAccountAdminStatus fetches the AdminAccount and its AdminStatus @@ -31,3 +34,21 @@ func AdminAccountAdminStatus(conn *securityhub.SecurityHub, adminAccountID strin return adminAccount, aws.StringValue(adminAccount.Status), nil } } + +func StandardsSubscriptionStatus(conn *securityhub.SecurityHub, arn string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := finder.StandardsSubscriptionByARN(conn, arn) + + if tfresource.NotFound(err) { + // Return a fake result and status to deal with the INCOMPLETE subscription status + // being a target for both Create and Delete. + return "", StandardsStatusNotFound, nil + } + + if err != nil { + return nil, "", err + } + + return output, aws.StringValue(output.StandardsStatus), nil + } +} diff --git a/aws/internal/service/securityhub/waiter/waiter.go b/aws/internal/service/securityhub/waiter/waiter.go index deac42a9091..d91080fcfb0 100644 --- a/aws/internal/service/securityhub/waiter/waiter.go +++ b/aws/internal/service/securityhub/waiter/waiter.go @@ -13,6 +13,9 @@ const ( // Maximum amount of time to wait for an AdminAccount to return NotFound AdminAccountNotFoundTimeout = 5 * time.Minute + + StandardsSubscriptionCreateTimeout = 3 * time.Minute + StandardsSubscriptionDeleteTimeout = 3 * time.Minute ) // AdminAccountEnabled waits for an AdminAccount to return Enabled @@ -50,3 +53,37 @@ func AdminAccountNotFound(conn *securityhub.SecurityHub, adminAccountID string) return nil, err } + +func StandardsSubscriptionCreated(conn *securityhub.SecurityHub, arn string) (*securityhub.StandardsSubscription, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{securityhub.StandardsStatusPending}, + Target: []string{securityhub.StandardsStatusReady, securityhub.StandardsStatusIncomplete}, + Refresh: StandardsSubscriptionStatus(conn, arn), + Timeout: StandardsSubscriptionCreateTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*securityhub.StandardsSubscription); ok { + return output, err + } + + return nil, err +} + +func StandardsSubscriptionDeleted(conn *securityhub.SecurityHub, arn string) (*securityhub.StandardsSubscription, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{securityhub.StandardsStatusDeleting}, + Target: []string{StandardsStatusNotFound, securityhub.StandardsStatusIncomplete}, + Refresh: StandardsSubscriptionStatus(conn, arn), + Timeout: StandardsSubscriptionDeleteTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*securityhub.StandardsSubscription); ok { + return output, err + } + + return nil, err +} diff --git a/aws/resource_aws_securityhub_standards_control.go b/aws/resource_aws_securityhub_standards_control.go index be5d817eb11..9ff8d4d7bc9 100644 --- a/aws/resource_aws_securityhub_standards_control.go +++ b/aws/resource_aws_securityhub_standards_control.go @@ -3,16 +3,16 @@ package aws import ( "context" "log" - "path" - "strings" "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/securityhub" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + tfsecurityhub "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/securityhub" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/securityhub/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func resourceAwsSecurityHubStandardsControl() *schema.Resource { @@ -23,47 +23,56 @@ func resourceAwsSecurityHubStandardsControl() *schema.Resource { DeleteContext: resourceAwsSecurityHubStandardsControlDelete, Schema: map[string]*schema.Schema{ - "standards_control_arn": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validateArn, + "control_id": { + Type: schema.TypeString, + Computed: true, }, + "control_status": { Type: schema.TypeString, Required: true, ValidateFunc: validation.StringInSlice(securityhub.ControlStatus_Values(), false), }, - "disabled_reason": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - "control_id": { + + "control_status_updated_at": { Type: schema.TypeString, Computed: true, }, - "control_status_updated_at": { + + "description": { Type: schema.TypeString, Computed: true, }, - "description": { + + "disabled_reason": { Type: schema.TypeString, + Optional: true, Computed: true, }, + "related_requirements": { Type: schema.TypeList, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, + "remediation_url": { Type: schema.TypeString, Computed: true, }, + "severity_rating": { Type: schema.TypeString, Computed: true, }, + + "standards_control_arn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArn, + }, + "title": { Type: schema.TypeString, Computed: true, @@ -75,39 +84,34 @@ func resourceAwsSecurityHubStandardsControl() *schema.Resource { func resourceAwsSecurityHubStandardsControlRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*AWSClient).securityhubconn - a := d.Get("standards_control_arn").(string) - controlArn, err := arn.Parse(a) + standardsSubscriptionARN, err := tfsecurityhub.StandardsControlARNToStandardsSubscriptionARN(d.Id()) + if err != nil { - return diag.Errorf("error parsing standards control ARN %q", controlArn) + return diag.FromErr(err) } - subscriptionArn := path.Dir(strings.ReplaceAll(controlArn.String(), "control", "subscription")) + control, err := finder.StandardsControlByStandardsSubscriptionARNAndStandardsControlARN(ctx, conn, standardsSubscriptionARN, d.Id()) - log.Printf("[DEBUG] Read Security Hub standard control %s", controlArn) + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] Security Hub Standards Control (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } - resp, err := conn.DescribeStandardsControls(&securityhub.DescribeStandardsControlsInput{ - StandardsSubscriptionArn: aws.String(subscriptionArn), - }) if err != nil { - return diag.Errorf("error reading Security Hub standard controls within %q subscription: %s", subscriptionArn, err) + return diag.Errorf("error reading Security Hub Standards Control (%s): %s", d.Id(), err) } - for _, c := range resp.Controls { - if aws.StringValue(c.StandardsControlArn) != controlArn.String() { - continue - } - - d.Set("control_status", c.ControlStatus) - d.Set("control_status_updated_at", c.ControlStatusUpdatedAt.Format(time.RFC3339)) - d.Set("description", c.Description) - d.Set("disabled_reason", c.DisabledReason) - d.Set("severity_rating", c.SeverityRating) - d.Set("title", c.Title) - - if err := d.Set("related_requirements", flattenStringList(c.RelatedRequirements)); err != nil { - return diag.Errorf("error setting related_requirements: %s", err) - } - } + d.Set("control_id", control.ControlId) + d.Set("control_status", control.ControlStatus) + d.Set("control_status_updated_at", control.ControlStatusUpdatedAt.Format(time.RFC3339)) + d.Set("description", control.Description) + d.Set("disabled_reason", control.DisabledReason) + d.Set("related_requirements", aws.StringValueSlice(control.RelatedRequirements)) + d.Set("remediation_url", control.RemediationUrl) + d.Set("severity_rating", control.SeverityRating) + d.Set("standards_control_arn", control.StandardsControlArn) + d.Set("title", control.Title) return nil } @@ -117,22 +121,23 @@ func resourceAwsSecurityHubStandardsControlPut(ctx context.Context, d *schema.Re d.SetId(d.Get("standards_control_arn").(string)) - log.Printf("[DEBUG] Update Security Hub standard control %s", d.Id()) - - _, err := conn.UpdateStandardsControl(&securityhub.UpdateStandardsControlInput{ - StandardsControlArn: aws.String(d.Get("standards_control_arn").(string)), + input := &securityhub.UpdateStandardsControlInput{ ControlStatus: aws.String(d.Get("control_status").(string)), DisabledReason: aws.String(d.Get("disabled_reason").(string)), - }) + StandardsControlArn: aws.String(d.Id()), + } + + log.Printf("[DEBUG] Updating Security Hub Standards Control: %s", input) + _, err := conn.UpdateStandardsControlWithContext(ctx, input) + if err != nil { - d.SetId("") - return diag.Errorf("error updating Security Hub standard control %q: %s", d.Id(), err) + return diag.Errorf("error updating Security Hub Standards Control (%s): %s", d.Id(), err) } return resourceAwsSecurityHubStandardsControlRead(ctx, d, meta) } func resourceAwsSecurityHubStandardsControlDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - log.Printf("[WARN] Cannot delete Security Hub standard control. Terraform will remove this resource from the state.") + log.Printf("[WARN] Cannot delete Security Hub Standards Control. Terraform will remove this resource from the state.") return nil } diff --git a/aws/resource_aws_securityhub_standards_control_test.go b/aws/resource_aws_securityhub_standards_control_test.go index 24535392f62..0db41d357cf 100644 --- a/aws/resource_aws_securityhub_standards_control_test.go +++ b/aws/resource_aws_securityhub_standards_control_test.go @@ -1,38 +1,38 @@ package aws import ( + "context" "fmt" - "path" "regexp" - "strings" "testing" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/securityhub" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + tfsecurityhub "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/securityhub" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/securityhub/finder" ) -func TestAccAWSSecurityHubStandardsControl_basic(t *testing.T) { - var standardsControl *securityhub.StandardsControl - +func testAccAWSSecurityHubStandardsControl_basic(t *testing.T) { + var standardsControl securityhub.StandardsControl resourceName := "aws_securityhub_standards_control.test" resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSSecurityHubStandardsControlExists(resourceName, standardsControl), + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, Steps: []resource.TestStep{ { Config: testAccAWSSecurityHubStandardsControlConfig_basic(), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSSecurityHubStandardsControlExists(resourceName, standardsControl), + testAccCheckAWSSecurityHubStandardsControlExists(resourceName, &standardsControl), + resource.TestCheckResourceAttr(resourceName, "control_id", "CIS.1.10"), resource.TestCheckResourceAttr(resourceName, "control_status", "ENABLED"), - resource.TestMatchResourceAttr(resourceName, "control_status_updated_at", regexp.MustCompile(`\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z`)), + resource.TestCheckResourceAttrSet(resourceName, "control_status_updated_at"), resource.TestCheckResourceAttr(resourceName, "description", "IAM password policies can prevent the reuse of a given password by the same user. It is recommended that the password policy prevent the reuse of passwords."), resource.TestCheckResourceAttr(resourceName, "disabled_reason", ""), resource.TestCheckResourceAttr(resourceName, "related_requirements.0", "CIS AWS Foundations 1.10"), + resource.TestCheckResourceAttrSet(resourceName, "remediation_url"), resource.TestCheckResourceAttr(resourceName, "severity_rating", "LOW"), resource.TestCheckResourceAttr(resourceName, "title", "Ensure IAM password policy prevents password reuse"), ), @@ -41,21 +41,19 @@ func TestAccAWSSecurityHubStandardsControl_basic(t *testing.T) { }) } -func TestAccAWSSecurityHubStandardsControl_disabledControlStatus(t *testing.T) { - var standardsControl *securityhub.StandardsControl - +func testAccAWSSecurityHubStandardsControl_disabledControlStatus(t *testing.T) { + var standardsControl securityhub.StandardsControl resourceName := "aws_securityhub_standards_control.test" resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSSecurityHubStandardsControlExists(resourceName, standardsControl), + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, Steps: []resource.TestStep{ { Config: testAccAWSSecurityHubStandardsControlConfig_disabledControlStatus(), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSSecurityHubStandardsControlExists(resourceName, standardsControl), + testAccCheckAWSSecurityHubStandardsControlExists(resourceName, &standardsControl), resource.TestCheckResourceAttr(resourceName, "control_status", "DISABLED"), resource.TestCheckResourceAttr(resourceName, "disabled_reason", "We handle password policies within Okta"), ), @@ -64,16 +62,11 @@ func TestAccAWSSecurityHubStandardsControl_disabledControlStatus(t *testing.T) { }) } -func TestAccAWSSecurityHubStandardsControl_enabledControlStatusAndDisabledReason(t *testing.T) { - var standardsControl *securityhub.StandardsControl - - resourceName := "aws_securityhub_standards_control.test" - +func testAccAWSSecurityHubStandardsControl_enabledControlStatusAndDisabledReason(t *testing.T) { resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSSecurityHubStandardsControlExists(resourceName, standardsControl), + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, Steps: []resource.TestStep{ { Config: testAccAWSSecurityHubStandardsControlConfig_enabledControlStatus(), @@ -90,32 +83,25 @@ func testAccCheckAWSSecurityHubStandardsControlExists(n string, control *securit return fmt.Errorf("Not found: %s", n) } + if rs.Primary.ID == "" { + return fmt.Errorf("No Security Hub Standards Control ID is set") + } + conn := testAccProvider.Meta().(*AWSClient).securityhubconn - arn := rs.Primary.ID - subscription_arn := path.Dir(strings.ReplaceAll(arn, "control", "subscription")) + standardsSubscriptionARN, err := tfsecurityhub.StandardsControlARNToStandardsSubscriptionARN(rs.Primary.ID) - resp, err := conn.DescribeStandardsControls(&securityhub.DescribeStandardsControlsInput{ - StandardsSubscriptionArn: aws.String(subscription_arn), - }) if err != nil { - return fmt.Errorf("error reading Security Hub %s standard controls: %s", subscription_arn, err) + return err } - controlNotFound := true - - for _, c := range resp.Controls { - if aws.StringValue(c.StandardsControlArn) != arn { - continue - } + output, err := finder.StandardsControlByStandardsSubscriptionARNAndStandardsControlARN(context.TODO(), conn, standardsSubscriptionARN, rs.Primary.ID) - controlNotFound = false - control = c + if err != nil { + return err } - if controlNotFound { - return fmt.Errorf("Security Hub %s standard control %s not found", subscription_arn, arn) - } + *control = *output return nil } diff --git a/aws/resource_aws_securityhub_standards_subscription.go b/aws/resource_aws_securityhub_standards_subscription.go index ebf53ef11f7..1fc28e812a5 100644 --- a/aws/resource_aws_securityhub_standards_subscription.go +++ b/aws/resource_aws_securityhub_standards_subscription.go @@ -7,6 +7,9 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/securityhub" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/securityhub/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/securityhub/waiter" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func resourceAwsSecurityHubStandardsSubscription() *schema.Resource { @@ -14,6 +17,7 @@ func resourceAwsSecurityHubStandardsSubscription() *schema.Resource { Create: resourceAwsSecurityHubStandardsSubscriptionCreate, Read: resourceAwsSecurityHubStandardsSubscriptionRead, Delete: resourceAwsSecurityHubStandardsSubscriptionDelete, + Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, @@ -31,23 +35,30 @@ func resourceAwsSecurityHubStandardsSubscription() *schema.Resource { func resourceAwsSecurityHubStandardsSubscriptionCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).securityhubconn - log.Printf("[DEBUG] Enabling Security Hub standard %s", d.Get("standards_arn")) - resp, err := conn.BatchEnableStandards(&securityhub.BatchEnableStandardsInput{ + standardsARN := d.Get("standards_arn").(string) + input := &securityhub.BatchEnableStandardsInput{ StandardsSubscriptionRequests: []*securityhub.StandardsSubscriptionRequest{ { - StandardsArn: aws.String(d.Get("standards_arn").(string)), + StandardsArn: aws.String(standardsARN), }, }, - }) + } + + log.Printf("[DEBUG] Creating Security Hub Standards Subscription: %s", input) + output, err := conn.BatchEnableStandards(input) if err != nil { - return fmt.Errorf("Error enabling Security Hub standard: %s", err) + return fmt.Errorf("error enabling Security Hub Standard (%s): %w", standardsARN, err) } - standardsSubscription := resp.StandardsSubscriptions[0] + d.SetId(aws.StringValue(output.StandardsSubscriptions[0].StandardsSubscriptionArn)) - d.SetId(aws.StringValue(standardsSubscription.StandardsSubscriptionArn)) + _, err = waiter.StandardsSubscriptionCreated(conn, d.Id()) + + if err != nil { + return fmt.Errorf("error waiting for Security Hub Standards Subscription (%s) to create: %w", d.Id(), err) + } return resourceAwsSecurityHubStandardsSubscriptionRead(d, meta) } @@ -55,38 +66,35 @@ func resourceAwsSecurityHubStandardsSubscriptionCreate(d *schema.ResourceData, m func resourceAwsSecurityHubStandardsSubscriptionRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).securityhubconn - log.Printf("[DEBUG] Reading Security Hub standard %s", d.Id()) - resp, err := conn.GetEnabledStandards(&securityhub.GetEnabledStandardsInput{ - StandardsSubscriptionArns: []*string{aws.String(d.Id())}, - }) - - if err != nil { - return fmt.Errorf("Error reading Security Hub standard %s: %s", d.Id(), err) - } + output, err := finder.StandardsSubscriptionByARN(conn, d.Id()) - if len(resp.StandardsSubscriptions) == 0 { - log.Printf("[WARN] Security Hub standard (%s) not found, removing from state", d.Id()) + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] Security Hub Standards Subscription (%s) not found, removing from state", d.Id()) d.SetId("") return nil } - standardsSubscription := resp.StandardsSubscriptions[0] - - d.Set("standards_arn", standardsSubscription.StandardsArn) + d.Set("standards_arn", output.StandardsArn) return nil } func resourceAwsSecurityHubStandardsSubscriptionDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).securityhubconn - log.Printf("[DEBUG] Disabling Security Hub standard %s", d.Id()) + log.Printf("[DEBUG] Deleting Security Hub Standards Subscription: %s", d.Id()) _, err := conn.BatchDisableStandards(&securityhub.BatchDisableStandardsInput{ - StandardsSubscriptionArns: []*string{aws.String(d.Id())}, + StandardsSubscriptionArns: aws.StringSlice([]string{d.Id()}), }) if err != nil { - return fmt.Errorf("Error disabling Security Hub standard %s: %s", d.Id(), err) + return fmt.Errorf("error disabling Security Hub Standard (%s): %w", d.Id(), err) + } + + _, err = waiter.StandardsSubscriptionDeleted(conn, d.Id()) + + if err != nil { + return fmt.Errorf("error waiting for Security Hub Standards Subscription (%s) to delete: %w", d.Id(), err) } return nil diff --git a/aws/resource_aws_securityhub_standards_subscription_test.go b/aws/resource_aws_securityhub_standards_subscription_test.go index 0e3bb4b5668..25f2f9a8559 100644 --- a/aws/resource_aws_securityhub_standards_subscription_test.go +++ b/aws/resource_aws_securityhub_standards_subscription_test.go @@ -4,15 +4,15 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/securityhub" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/securityhub/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func testAccAWSSecurityHubStandardsSubscription_basic(t *testing.T) { - var standardsSubscription *securityhub.StandardsSubscription - + var standardsSubscription securityhub.StandardsSubscription resourceName := "aws_securityhub_standards_subscription.test" resource.Test(t, resource.TestCase{ @@ -24,7 +24,7 @@ func testAccAWSSecurityHubStandardsSubscription_basic(t *testing.T) { { Config: testAccAWSSecurityHubStandardsSubscriptionConfig_basic, Check: resource.ComposeTestCheckFunc( - testAccCheckAWSSecurityHubStandardsSubscriptionExists(resourceName, standardsSubscription), + testAccCheckAWSSecurityHubStandardsSubscriptionExists(resourceName, &standardsSubscription), ), }, { @@ -32,11 +32,27 @@ func testAccAWSSecurityHubStandardsSubscription_basic(t *testing.T) { ImportState: true, ImportStateVerify: true, }, + }, + }) +} + +func testAccAWSSecurityHubStandardsSubscription_disappears(t *testing.T) { + var standardsSubscription securityhub.StandardsSubscription + resourceName := "aws_securityhub_standards_subscription.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSecurityHubAccountDestroy, + Steps: []resource.TestStep{ { - // Check Destroy - but only target the specific resource (otherwise Security Hub - // will be disabled and the destroy check will fail) - Config: testAccAWSSecurityHubStandardsSubscriptionConfig_empty, - Check: testAccCheckAWSSecurityHubStandardsSubscriptionDestroy, + Config: testAccAWSSecurityHubStandardsSubscriptionConfig_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSecurityHubStandardsSubscriptionExists(resourceName, &standardsSubscription), + testAccCheckResourceDisappears(testAccProvider, resourceAwsSecurityHubStandardsSubscription(), resourceName), + ), + ExpectNonEmptyPlan: true, }, }, }) @@ -49,21 +65,19 @@ func testAccCheckAWSSecurityHubStandardsSubscriptionExists(n string, standardsSu return fmt.Errorf("Not found: %s", n) } + if rs.Primary.ID == "" { + return fmt.Errorf("No Security Hub Standards Subscription ID is set") + } + conn := testAccProvider.Meta().(*AWSClient).securityhubconn - resp, err := conn.GetEnabledStandards(&securityhub.GetEnabledStandardsInput{ - StandardsSubscriptionArns: []*string{aws.String(rs.Primary.ID)}, - }) + output, err := finder.StandardsSubscriptionByARN(conn, rs.Primary.ID) if err != nil { return err } - if len(resp.StandardsSubscriptions) == 0 { - return fmt.Errorf("Security Hub standard %s not found", rs.Primary.ID) - } - - standardsSubscription = resp.StandardsSubscriptions[0] + *standardsSubscription = *output return nil } @@ -77,20 +91,17 @@ func testAccCheckAWSSecurityHubStandardsSubscriptionDestroy(s *terraform.State) continue } - resp, err := conn.GetEnabledStandards(&securityhub.GetEnabledStandardsInput{ - StandardsSubscriptionArns: []*string{aws.String(rs.Primary.ID)}, - }) + _, err := finder.StandardsSubscriptionByARN(conn, rs.Primary.ID) + + if tfresource.NotFound(err) { + continue + } if err != nil { - if isAWSErr(err, securityhub.ErrCodeResourceNotFoundException, "") { - continue - } return err } - if len(resp.StandardsSubscriptions) != 0 { - return fmt.Errorf("Security Hub standard %s still exists", rs.Primary.ID) - } + return fmt.Errorf("Security Hub Standards Subscription %s still exists", rs.Primary.ID) } return nil diff --git a/aws/resource_aws_securityhub_test.go b/aws/resource_aws_securityhub_test.go index ee4601b8d31..78ae1839dd4 100644 --- a/aws/resource_aws_securityhub_test.go +++ b/aws/resource_aws_securityhub_test.go @@ -43,8 +43,14 @@ func TestAccAWSSecurityHub_serial(t *testing.T) { "ProductSubscription": { "basic": testAccAWSSecurityHubProductSubscription_basic, }, + "StandardsControl": { + "basic": testAccAWSSecurityHubStandardsControl_basic, + "DisabledControlStatus": testAccAWSSecurityHubStandardsControl_disabledControlStatus, + "EnabledControlStatusAndDisabledReason": testAccAWSSecurityHubStandardsControl_enabledControlStatusAndDisabledReason, + }, "StandardsSubscription": { - "basic": testAccAWSSecurityHubStandardsSubscription_basic, + "basic": testAccAWSSecurityHubStandardsSubscription_basic, + "disappears": testAccAWSSecurityHubStandardsSubscription_disappears, }, } From 7495f70be54ab3819ab8c5ce31bc7314775e1025 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 13 Jul 2021 13:51:55 -0400 Subject: [PATCH 373/398] Fix awsprovidelint error 'AT001: missing CheckDestroy'. --- ..._aws_securityhub_standards_control_test.go | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/aws/resource_aws_securityhub_standards_control_test.go b/aws/resource_aws_securityhub_standards_control_test.go index 0db41d357cf..d1f4629849e 100644 --- a/aws/resource_aws_securityhub_standards_control_test.go +++ b/aws/resource_aws_securityhub_standards_control_test.go @@ -18,9 +18,10 @@ func testAccAWSSecurityHubStandardsControl_basic(t *testing.T) { resourceName := "aws_securityhub_standards_control.test" resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), - Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, + CheckDestroy: nil, //lintignore:AT001 Steps: []resource.TestStep{ { Config: testAccAWSSecurityHubStandardsControlConfig_basic(), @@ -46,9 +47,10 @@ func testAccAWSSecurityHubStandardsControl_disabledControlStatus(t *testing.T) { resourceName := "aws_securityhub_standards_control.test" resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), - Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, + CheckDestroy: nil, //lintignore:AT001 Steps: []resource.TestStep{ { Config: testAccAWSSecurityHubStandardsControlConfig_disabledControlStatus(), @@ -64,9 +66,10 @@ func testAccAWSSecurityHubStandardsControl_disabledControlStatus(t *testing.T) { func testAccAWSSecurityHubStandardsControl_enabledControlStatusAndDisabledReason(t *testing.T) { resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), - Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, + CheckDestroy: nil, //lintignore:AT001 Steps: []resource.TestStep{ { Config: testAccAWSSecurityHubStandardsControlConfig_enabledControlStatus(), From f83cb8bbcb53c54d98f136e83ec7cb56ac694432 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 13 Jul 2021 13:52:46 -0400 Subject: [PATCH 374/398] Fix golangci-lint error 'deadcode'. --- ...esource_aws_securityhub_standards_subscription_test.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/aws/resource_aws_securityhub_standards_subscription_test.go b/aws/resource_aws_securityhub_standards_subscription_test.go index 25f2f9a8559..d0b77d76edf 100644 --- a/aws/resource_aws_securityhub_standards_subscription_test.go +++ b/aws/resource_aws_securityhub_standards_subscription_test.go @@ -19,7 +19,7 @@ func testAccAWSSecurityHubStandardsSubscription_basic(t *testing.T) { PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), Providers: testAccProviders, - CheckDestroy: testAccCheckAWSSecurityHubAccountDestroy, + CheckDestroy: testAccCheckAWSSecurityHubStandardsSubscriptionDestroy, Steps: []resource.TestStep{ { Config: testAccAWSSecurityHubStandardsSubscriptionConfig_basic, @@ -44,7 +44,7 @@ func testAccAWSSecurityHubStandardsSubscription_disappears(t *testing.T) { PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), Providers: testAccProviders, - CheckDestroy: testAccCheckAWSSecurityHubAccountDestroy, + CheckDestroy: testAccCheckAWSSecurityHubStandardsSubscriptionDestroy, Steps: []resource.TestStep{ { Config: testAccAWSSecurityHubStandardsSubscriptionConfig_basic, @@ -107,10 +107,6 @@ func testAccCheckAWSSecurityHubStandardsSubscriptionDestroy(s *terraform.State) return nil } -const testAccAWSSecurityHubStandardsSubscriptionConfig_empty = ` -resource "aws_securityhub_account" "test" {} -` - const testAccAWSSecurityHubStandardsSubscriptionConfig_basic = ` resource "aws_securityhub_account" "test" {} From f92c7af611ea7dcf95b79fcc2b2761368eb3061c Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 13 Jul 2021 13:59:10 -0400 Subject: [PATCH 375/398] r/aws_securityhub_standards_subscription: Correctly handle INCOMPLETE status in 'testAccCheckAWSSecurityHubStandardsSubscriptionDestroy'. Acceptance test output: % make testacc TEST=./aws TESTARGS='-run=TestAccAWSSecurityHub_serial/StandardsSubscription' ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./aws -v -count 1 -parallel 20 -run=TestAccAWSSecurityHub_serial/StandardsSubscription -timeout 180m === RUN TestAccAWSSecurityHub_serial === RUN TestAccAWSSecurityHub_serial/StandardsSubscription === RUN TestAccAWSSecurityHub_serial/StandardsSubscription/basic === RUN TestAccAWSSecurityHub_serial/StandardsSubscription/disappears --- PASS: TestAccAWSSecurityHub_serial (39.10s) --- PASS: TestAccAWSSecurityHub_serial/StandardsSubscription (39.10s) --- PASS: TestAccAWSSecurityHub_serial/StandardsSubscription/basic (22.59s) --- PASS: TestAccAWSSecurityHub_serial/StandardsSubscription/disappears (16.51s) PASS ok github.com/terraform-providers/terraform-provider-aws/aws 42.508s --- ...esource_aws_securityhub_standards_subscription_test.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/aws/resource_aws_securityhub_standards_subscription_test.go b/aws/resource_aws_securityhub_standards_subscription_test.go index d0b77d76edf..3988a43e550 100644 --- a/aws/resource_aws_securityhub_standards_subscription_test.go +++ b/aws/resource_aws_securityhub_standards_subscription_test.go @@ -4,6 +4,7 @@ import ( "fmt" "testing" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/securityhub" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -91,7 +92,7 @@ func testAccCheckAWSSecurityHubStandardsSubscriptionDestroy(s *terraform.State) continue } - _, err := finder.StandardsSubscriptionByARN(conn, rs.Primary.ID) + output, err := finder.StandardsSubscriptionByARN(conn, rs.Primary.ID) if tfresource.NotFound(err) { continue @@ -101,6 +102,11 @@ func testAccCheckAWSSecurityHubStandardsSubscriptionDestroy(s *terraform.State) return err } + // INCOMPLETE subscription status => deleted. + if aws.StringValue(output.StandardsStatus) == securityhub.StandardsStatusIncomplete { + continue + } + return fmt.Errorf("Security Hub Standards Subscription %s still exists", rs.Primary.ID) } From a199a6be8618ec9ace2806dc9ba414890abab00a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 13 Jul 2021 15:07:06 -0400 Subject: [PATCH 376/398] Correct new argument name (retro v3.47.0) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc5478f213a..837d38996d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -87,7 +87,7 @@ ENHANCEMENTS: * resource/aws_eks_cluster: Allow updates to `encryption_config` ([#19144](https://github.com/hashicorp/terraform-provider-aws/issues/19144)) * resource/aws_lb_target_group: Add support for `app_cookie` stickiness type and `cookie_name` argument ([#18102](https://github.com/hashicorp/terraform-provider-aws/issues/18102)) * resource/aws_main_route_table_association: Wait for association to reach the required state ([#19426](https://github.com/hashicorp/terraform-provider-aws/issues/19426)) -* resource/aws_neptune_cluster: Add `copy_snapshot_to_tags` argument ([#19899](https://github.com/hashicorp/terraform-provider-aws/issues/19899)) +* resource/aws_neptune_cluster: Add `copy_tags_to_snapshot` argument ([#19899](https://github.com/hashicorp/terraform-provider-aws/issues/19899)) * resource/aws_route: Add retries when creating, deleting and replacing routes ([#19426](https://github.com/hashicorp/terraform-provider-aws/issues/19426)) * resource/aws_route_table: Add retries when creating, deleting and replacing routes ([#19426](https://github.com/hashicorp/terraform-provider-aws/issues/19426)) * resource/aws_route_table_association: Wait for association to reach the required state ([#19426](https://github.com/hashicorp/terraform-provider-aws/issues/19426)) From 72e623415ce30dfa463aa0cac7d3ef433b262824 Mon Sep 17 00:00:00 2001 From: changelogbot Date: Tue, 13 Jul 2021 19:10:04 +0000 Subject: [PATCH 377/398] Update CHANGELOG.md for #20168 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 837d38996d3..b17d94a7058 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ FEATURES: * **New Resource:** `aws_appconfig_environment` ([#19307](https://github.com/hashicorp/terraform-provider-aws/issues/19307)) * **New Resource:** `aws_appconfig_hosted_configuration_version` ([#19324](https://github.com/hashicorp/terraform-provider-aws/issues/19324)) * **New Resource:** `aws_config_organization_conformance_pack` ([#17298](https://github.com/hashicorp/terraform-provider-aws/issues/17298)) +* **New Resource:** `aws_securityhub_standards_control` ([#14714](https://github.com/hashicorp/terraform-provider-aws/issues/14714)) ENHANCEMENTS: From b805c0ec7340f1a8ae57cea675d571b0eb20d489 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Jul 2021 06:04:32 +0000 Subject: [PATCH 378/398] build(deps): bump alex-page/github-project-automation-plus Bumps [alex-page/github-project-automation-plus](https://github.com/alex-page/github-project-automation-plus) from 0.8.0 to 0.8.1. - [Release notes](https://github.com/alex-page/github-project-automation-plus/releases) - [Commits](https://github.com/alex-page/github-project-automation-plus/compare/v0.8.0...v0.8.1) --- updated-dependencies: - dependency-name: alex-page/github-project-automation-plus dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/project.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/project.yml b/.github/workflows/project.yml index 7739fa63b75..7c9f0d5852a 100644 --- a/.github/workflows/project.yml +++ b/.github/workflows/project.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Move team PRs to Review column - uses: alex-page/github-project-automation-plus@v0.8.0 + uses: alex-page/github-project-automation-plus@v0.8.1 if: contains(fromJSON('["anGie44", "bill-rich", "breathingdust", "ewbankkit", "gdavison", "maryelizbeth", "YakDriver"]'), github.actor) && github.event.pull_request.draft == false with: project: AWS Provider Working Board From dc0ef30fce9ea93c382a443830a45917007583de Mon Sep 17 00:00:00 2001 From: Suzuki Shunsuke Date: Wed, 14 Jul 2021 20:34:20 +0900 Subject: [PATCH 379/398] fix: add the attribute "environment_id" to aws_appconfig_environment --- aws/resource_aws_appconfig_environment.go | 6 ++++++ website/docs/r/appconfig_environment.html.markdown | 1 + 2 files changed, 7 insertions(+) diff --git a/aws/resource_aws_appconfig_environment.go b/aws/resource_aws_appconfig_environment.go index 14f4d67513e..9a6c15a5564 100644 --- a/aws/resource_aws_appconfig_environment.go +++ b/aws/resource_aws_appconfig_environment.go @@ -32,6 +32,10 @@ func resourceAwsAppconfigEnvironment() *schema.Resource { ForceNew: true, ValidateFunc: validation.StringMatch(regexp.MustCompile(`[a-z0-9]{4,7}`), ""), }, + "environment_id": { + Type: schema.TypeString, + Computed: true, + }, "arn": { Type: schema.TypeString, Computed: true, @@ -110,6 +114,7 @@ func resourceAwsAppconfigEnvironmentCreate(d *schema.ResourceData, meta interfac return fmt.Errorf("error creating AppConfig Environment for Application (%s): empty response", appId) } + d.Set("environment_id", environment.Id) d.SetId(fmt.Sprintf("%s:%s", aws.StringValue(environment.Id), aws.StringValue(environment.ApplicationId))) return resourceAwsAppconfigEnvironmentRead(d, meta) @@ -148,6 +153,7 @@ func resourceAwsAppconfigEnvironmentRead(d *schema.ResourceData, meta interface{ } d.Set("application_id", output.ApplicationId) + d.Set("environment_id", output.Id) d.Set("description", output.Description) d.Set("name", output.Name) d.Set("state", output.State) diff --git a/website/docs/r/appconfig_environment.html.markdown b/website/docs/r/appconfig_environment.html.markdown index 506e3b1266b..ee06988b27a 100644 --- a/website/docs/r/appconfig_environment.html.markdown +++ b/website/docs/r/appconfig_environment.html.markdown @@ -61,6 +61,7 @@ In addition to all arguments above, the following attributes are exported: * `arn` - The Amazon Resource Name (ARN) of the AppConfig Environment. * `id` - The AppConfig environment ID and application ID separated by a colon (`:`). +* `environment_id` - The AppConfig environment ID. * `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](/docs/providers/aws/index.html#default_tags-configuration-block). ## Import From 64522c13505e2473f5451920fd90363328db1c05 Mon Sep 17 00:00:00 2001 From: changelogbot Date: Wed, 14 Jul 2021 16:19:52 +0000 Subject: [PATCH 380/398] Update CHANGELOG.md for #20111 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b17d94a7058..8485296a35a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ BUG FIXES: * resource/aws_cognito_user_pool_client: Retry on `ConcurrentModificationException` ([#20031](https://github.com/hashicorp/terraform-provider-aws/issues/20031)) * resource/aws_datasync_location_s3: Correctly parse S3 on Outposts location URI ([#19859](https://github.com/hashicorp/terraform-provider-aws/issues/19859)) * resource/aws_db_instance: Ignore allocated_storage for replica at creation time ([#12548](https://github.com/hashicorp/terraform-provider-aws/issues/12548)) +* resource/aws_elasticache_replication_group: Cannot set `cluster_mode.replicas_per_node_group` when member of Global Replication Group ([#20111](https://github.com/hashicorp/terraform-provider-aws/issues/20111)) ## 3.49.0 (July 08, 2021) From dc5410c8966b43416e1a1dc3dd9764418b20b0c7 Mon Sep 17 00:00:00 2001 From: Mikael Allison Date: Mon, 26 Apr 2021 02:00:10 +0100 Subject: [PATCH 381/398] Add SecurityHub Organization Configuration Resource Resource to enable security hub's auto-enroll feature when apart of an organization and a security hub admin account has been configured. By default the **Auto-Enable** feature is disabled. See: [Automatically enabling new organization accounts](https://docs.aws.amazon.com/securityhub/latest/userguide/accounts-orgs-auto-enable.html) [method]: https://docs.aws.amazon.com/sdk-for-go/api/service/securityhub/#SecurityHub.UpdateOrganizationConfiguration [input]: https://docs.aws.amazon.com/sdk-for-go/api/service/securityhub/#UpdateOrganizationConfigurationInput Refactored with amendments suggested by @ewbankkit --- .changelog/19108.txt | 3 + aws/provider.go | 1 + ..._securityhub_organization_configuration.go | 60 +++++++++++++ ...rityhub_organization_configuration_test.go | 84 +++++++++++++++++++ aws/resource_aws_securityhub_test.go | 3 + ...b_organization_admin_account.html.markdown | 3 + ...ityhub_organization_configuration.markdown | 54 ++++++++++++ 7 files changed, 208 insertions(+) create mode 100644 .changelog/19108.txt create mode 100644 aws/resource_aws_securityhub_organization_configuration.go create mode 100644 aws/resource_aws_securityhub_organization_configuration_test.go create mode 100644 website/docs/r/securityhub_organization_configuration.markdown diff --git a/.changelog/19108.txt b/.changelog/19108.txt new file mode 100644 index 00000000000..f6266443b27 --- /dev/null +++ b/.changelog/19108.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_securityhub_organization_configuration +``` diff --git a/aws/provider.go b/aws/provider.go index 745c64e4937..2a2671a4b72 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -992,6 +992,7 @@ func Provider() *schema.Provider { "aws_securityhub_invite_accepter": resourceAwsSecurityHubInviteAccepter(), "aws_securityhub_member": resourceAwsSecurityHubMember(), "aws_securityhub_organization_admin_account": resourceAwsSecurityHubOrganizationAdminAccount(), + "aws_securityhub_organization_configuration": resourceAwsSecurityHubOrganizationConfiguration(), "aws_securityhub_product_subscription": resourceAwsSecurityHubProductSubscription(), "aws_securityhub_standards_subscription": resourceAwsSecurityHubStandardsSubscription(), "aws_servicecatalog_portfolio": resourceAwsServiceCatalogPortfolio(), diff --git a/aws/resource_aws_securityhub_organization_configuration.go b/aws/resource_aws_securityhub_organization_configuration.go new file mode 100644 index 00000000000..cba3fd99539 --- /dev/null +++ b/aws/resource_aws_securityhub_organization_configuration.go @@ -0,0 +1,60 @@ +package aws + +import ( + "fmt" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/securityhub" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceAwsSecurityHubOrganizationConfiguration() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsSecurityHubOrganizationConfigurationUpdate, + Read: resourceAwsSecurityHubOrganizationConfigurationRead, + Update: resourceAwsSecurityHubOrganizationConfigurationUpdate, + Delete: schema.Noop, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "auto_enable": { + Type: schema.TypeBool, + Required: true, + }, + }, + } +} + +func resourceAwsSecurityHubOrganizationConfigurationUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).securityhubconn + + input := &securityhub.UpdateOrganizationConfigurationInput{ + AutoEnable: aws.Bool(d.Get("auto_enable").(bool)), + } + + _, err := conn.UpdateOrganizationConfiguration(input) + + if err != nil { + return fmt.Errorf("error updating Security Hub Organization Configuration (%s): %w", d.Id(), err) + } + + d.SetId(meta.(*AWSClient).accountid) + + return resourceAwsSecurityHubOrganizationConfigurationRead(d, meta) +} + +func resourceAwsSecurityHubOrganizationConfigurationRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).securityhubconn + + output, err := conn.DescribeOrganizationConfiguration(&securityhub.DescribeOrganizationConfigurationInput{}) + + if err != nil { + return fmt.Errorf("error reading Security Hub Organization Configuration: %w", err) + } + + d.Set("auto_enable", output.AutoEnable) + + return nil +} diff --git a/aws/resource_aws_securityhub_organization_configuration_test.go b/aws/resource_aws_securityhub_organization_configuration_test.go new file mode 100644 index 00000000000..1c202fab97d --- /dev/null +++ b/aws/resource_aws_securityhub_organization_configuration_test.go @@ -0,0 +1,84 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/service/securityhub" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func testAccAwsSecurityHubOrganizationConfiguration_basic(t *testing.T) { + resourceName := "aws_securityhub_organization_configuration.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, + CheckDestroy: nil, //lintignore:AT001 + Steps: []resource.TestStep{ + { + Config: testAccAwsSecurityHubOrganizationConfigurationConfig(true), + Check: resource.ComposeTestCheckFunc( + testAccAwsSecurityHubOrganizationConfigurationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "auto_enable", "true"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAwsSecurityHubOrganizationConfigurationConfig(false), + Check: resource.ComposeTestCheckFunc( + testAccAwsSecurityHubOrganizationConfigurationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "auto_enable", "false"), + ), + }, + }, + }) +} + +func testAccAwsSecurityHubOrganizationConfigurationExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + _, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + conn := testAccProvider.Meta().(*AWSClient).securityhubconn + + _, err := conn.DescribeOrganizationConfiguration(&securityhub.DescribeOrganizationConfigurationInput{}) + + return err + } +} + +func testAccAwsSecurityHubOrganizationConfigurationConfig(autoEnable bool) string { + return fmt.Sprintf(` +data "aws_partition" "current" {} + +resource "aws_organizations_organization" "test" { + aws_service_access_principals = ["securityhub.${data.aws_partition.current.dns_suffix}"] + feature_set = "ALL" +} + +resource "aws_securityhub_account" "test" {} + +data "aws_caller_identity" "current" {} + +resource "aws_securityhub_organization_admin_account" "test" { + admin_account_id = data.aws_caller_identity.current.account_id + + depends_on = [aws_organizations_organization.test, aws_securityhub_account.test] +} + +resource "aws_securityhub_organization_configuration" "test" { + auto_enable = %[1]t + + depends_on = [aws_securityhub_organization_admin_account.test] +} +`, autoEnable) +} diff --git a/aws/resource_aws_securityhub_test.go b/aws/resource_aws_securityhub_test.go index ee4601b8d31..b610200d736 100644 --- a/aws/resource_aws_securityhub_test.go +++ b/aws/resource_aws_securityhub_test.go @@ -40,6 +40,9 @@ func TestAccAWSSecurityHub_serial(t *testing.T) { "disappears": testAccAwsSecurityHubOrganizationAdminAccount_disappears, "MultiRegion": testAccAwsSecurityHubOrganizationAdminAccount_MultiRegion, }, + "OrganizationConfiguration": { + "basic": testAccAwsSecurityHubOrganizationConfiguration_basic, + }, "ProductSubscription": { "basic": testAccAWSSecurityHubProductSubscription_basic, }, diff --git a/website/docs/r/securityhub_organization_admin_account.html.markdown b/website/docs/r/securityhub_organization_admin_account.html.markdown index 3941c489671..fa69bf0f38d 100644 --- a/website/docs/r/securityhub_organization_admin_account.html.markdown +++ b/website/docs/r/securityhub_organization_admin_account.html.markdown @@ -25,6 +25,9 @@ resource "aws_securityhub_organization_admin_account" "example" { admin_account_id = "123456789012" } + +// Auto enable security hub in organization member accounts +resource "aws_securityhub_organization_configuration" "example" {} ``` ## Argument Reference diff --git a/website/docs/r/securityhub_organization_configuration.markdown b/website/docs/r/securityhub_organization_configuration.markdown new file mode 100644 index 00000000000..439456336b8 --- /dev/null +++ b/website/docs/r/securityhub_organization_configuration.markdown @@ -0,0 +1,54 @@ +--- +subcategory: "Security Hub" +layout: "aws" +page_title: "AWS: aws_securityhub_organization_configuration" +description: |- + Manages the Security Hub Organization Configuration +--- + +# Resource: aws_securityhub_organization_configuration + +Manages the Security Hub Organization Configuration. + +~> **NOTE:** This resource requires an [`aws_securityhub_organization_admin_account`](/docs/providers/aws/r/securityhub_organization_admin_account.html) to be configured (not necessarily with Terraform). More information about managing Security Hub in an organization can be found in the [Managing administrator and member accounts](https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-accounts.html) documentation + +~> **NOTE:** This is an advanced Terraform resource. Terraform will automatically assume management of the Security Hub Organization Configuration without import and perform no actions on removal from the Terraform configuration. + +## Example Usage + +```terraform +resource "aws_organizations_organization" "example" { + aws_service_access_principals = ["securityhub.amazonaws.com"] + feature_set = "ALL" +} + +resource "aws_securityhub_organization_admin_account" "example" { + depends_on = [aws_organizations_organization.example] + + admin_account_id = "123456789012" +} + +resource "aws_securityhub_organization_configuration" "example" { + auto_enable = true +} +``` + +## Argument Reference + +The following arguments are supported: + +* `auto_enable` - (Required) Whether to automatically enable Security Hub for new accounts in the organization. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - AWS Account ID. + +## Import + +An existing Security Hub enabled account can be imported using the AWS account ID, e.g. + +``` +$ terraform import aws_securityhub_organization_configuration.example 123456789012 +``` From 07d7cd4b8acae4bbf8cb4eff959aad72a5530da1 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 14 Jul 2021 12:59:28 -0400 Subject: [PATCH 382/398] Random junk file. --- a.txt | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 a.txt diff --git a/a.txt b/a.txt deleted file mode 100644 index 8bdec30e087..00000000000 --- a/a.txt +++ /dev/null @@ -1,2 +0,0 @@ -==> Checking that code complies with gofmt requirements... -TF_ACC=1 go test ./aws -v -count 1 -parallel 20 -run=TestAccAWSEcrReplicationConfiguration_basic -timeout 180m From a76209003a39187ecd7620fa48ba88d4affd9e6d Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 14 Jul 2021 13:10:19 -0400 Subject: [PATCH 383/398] Fix importlint error 'Imports of different types are not allowed in the same group'. --- aws/resource_aws_securityhub_organization_configuration.go | 1 + 1 file changed, 1 insertion(+) diff --git a/aws/resource_aws_securityhub_organization_configuration.go b/aws/resource_aws_securityhub_organization_configuration.go index cba3fd99539..52355360eb2 100644 --- a/aws/resource_aws_securityhub_organization_configuration.go +++ b/aws/resource_aws_securityhub_organization_configuration.go @@ -2,6 +2,7 @@ package aws import ( "fmt" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/securityhub" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" From 4d809190d7339eb3d22facb003fe7e896123ca04 Mon Sep 17 00:00:00 2001 From: Jacob Date: Wed, 14 Jul 2021 12:45:37 -0500 Subject: [PATCH 384/398] arns are also a part of the attributes (#20167) * arns are also a part of the attributes arns are also a part of the attributes. * Update website/docs/d/cognito_user_pools.markdown Co-authored-by: angie pinilla * Update cognito_user_pools.markdown Co-authored-by: angie pinilla --- website/docs/d/cognito_user_pools.markdown | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/website/docs/d/cognito_user_pools.markdown b/website/docs/d/cognito_user_pools.markdown index fae71154807..ceebad004a5 100644 --- a/website/docs/d/cognito_user_pools.markdown +++ b/website/docs/d/cognito_user_pools.markdown @@ -36,4 +36,5 @@ resource "aws_api_gateway_authorizer" "cognito" { ## Attributes Reference -* `ids` - The list of cognito user pool ids. +* `ids` - The set of cognito user pool ids. +* `arns` - The set of cognito user pool Amazon Resource Names (ARNs). From 69fd4700bab314e8bffa6f8eb99dfb6cc6581add Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 14 Jul 2021 14:02:31 -0400 Subject: [PATCH 385/398] Fix validate-terraform error 'Warning: Single line comments should begin with # (terraform_comment_syntax)'. --- .../docs/r/securityhub_organization_admin_account.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/securityhub_organization_admin_account.html.markdown b/website/docs/r/securityhub_organization_admin_account.html.markdown index fa69bf0f38d..1863444c03d 100644 --- a/website/docs/r/securityhub_organization_admin_account.html.markdown +++ b/website/docs/r/securityhub_organization_admin_account.html.markdown @@ -26,7 +26,7 @@ resource "aws_securityhub_organization_admin_account" "example" { admin_account_id = "123456789012" } -// Auto enable security hub in organization member accounts +# Auto enable security hub in organization member accounts resource "aws_securityhub_organization_configuration" "example" {} ``` From c764beb2ba8ca142e11575a77001fc7fbedd2543 Mon Sep 17 00:00:00 2001 From: changelogbot Date: Wed, 14 Jul 2021 18:02:42 +0000 Subject: [PATCH 386/398] Update CHANGELOG.md for #20183 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8485296a35a..b4ceec4419b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ FEATURES: * **New Resource:** `aws_appconfig_environment` ([#19307](https://github.com/hashicorp/terraform-provider-aws/issues/19307)) * **New Resource:** `aws_appconfig_hosted_configuration_version` ([#19324](https://github.com/hashicorp/terraform-provider-aws/issues/19324)) * **New Resource:** `aws_config_organization_conformance_pack` ([#17298](https://github.com/hashicorp/terraform-provider-aws/issues/17298)) +* **New Resource:** `aws_securityhub_organization_configuration` ([#19108](https://github.com/hashicorp/terraform-provider-aws/issues/19108)) * **New Resource:** `aws_securityhub_standards_control` ([#14714](https://github.com/hashicorp/terraform-provider-aws/issues/14714)) ENHANCEMENTS: From 212fe8c8cb911c7293ca4662721d506d8ef7c4b0 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Wed, 14 Jul 2021 12:08:20 -0700 Subject: [PATCH 387/398] Uses forked version of github.com/hashicorp/terraform-plugin-sdk/v2 to get timing instrumentation for sweepers --- go.mod | 2 ++ go.sum | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 4e23c51ff63..515d5538fd8 100644 --- a/go.mod +++ b/go.mod @@ -22,3 +22,5 @@ require ( github.com/pquerna/otp v1.3.0 gopkg.in/yaml.v2 v2.4.0 ) + +replace github.com/hashicorp/terraform-plugin-sdk/v2 => github.com/gdavison/terraform-plugin-sdk/v2 v2.0.2-0.20210714181518-b5a3dc95a675 diff --git a/go.sum b/go.sum index c0acb2cd1b6..ed33c528d7a 100644 --- a/go.sum +++ b/go.sum @@ -96,6 +96,8 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/gdavison/terraform-plugin-sdk/v2 v2.0.2-0.20210714181518-b5a3dc95a675 h1:2QEdOgyP5bC4Cjkf4DZ7rBcCXfLaf+ceTY95U3axacI= +github.com/gdavison/terraform-plugin-sdk/v2 v2.0.2-0.20210714181518-b5a3dc95a675/go.mod h1:grseeRo9g3yNkYW09iFlV8LG78jTa1ssBgouogQg/RU= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= @@ -208,8 +210,6 @@ github.com/hashicorp/terraform-json v0.12.0 h1:8czPgEEWWPROStjkWPUnTQDXmpmZPlkQA github.com/hashicorp/terraform-json v0.12.0/go.mod h1:pmbq9o4EuL43db5+0ogX10Yofv1nozM+wskr/bGFJpI= github.com/hashicorp/terraform-plugin-go v0.3.0 h1:AJqYzP52JFYl9NABRI7smXI1pNjgR5Q/y2WyVJ/BOZA= github.com/hashicorp/terraform-plugin-go v0.3.0/go.mod h1:dFHsQMaTLpON2gWhVWT96fvtlc/MF1vSy3OdMhWBzdM= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.7.0 h1:SuI59MqNjYDrL7EfqHX9V6P/24isgqYx/FdglwVs9bg= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.7.0/go.mod h1:grseeRo9g3yNkYW09iFlV8LG78jTa1ssBgouogQg/RU= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= From 8e82ec53f0a4352241da7ef568a967cd688eefb5 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Wed, 14 Jul 2021 12:25:45 -0700 Subject: [PATCH 388/398] Adds `instance_interruption_behavior` and deprecates `instance_interruption_behaviour` --- aws/resource_aws_spot_instance_request.go | 47 ++++- ...resource_aws_spot_instance_request_test.go | 191 ++++++++++++++++-- .../r/spot_instance_request.html.markdown | 3 +- 3 files changed, 218 insertions(+), 23 deletions(-) diff --git a/aws/resource_aws_spot_instance_request.go b/aws/resource_aws_spot_instance_request.go index 49dab4aee72..990b0605dfc 100644 --- a/aws/resource_aws_spot_instance_request.go +++ b/aws/resource_aws_spot_instance_request.go @@ -1,6 +1,7 @@ package aws import ( + "context" "fmt" "log" "math/big" @@ -10,6 +11,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "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" @@ -101,11 +103,22 @@ func resourceAwsSpotInstanceRequest() *schema.Resource { ValidateFunc: validation.IntDivisibleBy(60), } s["instance_interruption_behaviour"] = &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Default: ec2.InstanceInterruptionBehaviorTerminate, - ForceNew: true, - ValidateFunc: validation.StringInSlice(ec2.InstanceInterruptionBehavior_Values(), false), + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(ec2.InstanceInterruptionBehavior_Values(), false), + Deprecated: "Use the parameter \"instance_interruption_behavior\" instead.", + ConflictsWith: []string{"instance_interruption_behavior"}, + } + s["instance_interruption_behavior"] = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, // Only during `instance_interruption_behaviour` deprecation period + // Default: ec2.InstanceInterruptionBehaviorTerminate, + ForceNew: true, + ValidateFunc: validation.StringInSlice(ec2.InstanceInterruptionBehavior_Values(), false), + ConflictsWith: []string{"instance_interruption_behaviour"}, } s["valid_from"] = &schema.Schema{ Type: schema.TypeString, @@ -123,6 +136,25 @@ func resourceAwsSpotInstanceRequest() *schema.Resource { } return s }(), + + CustomizeDiff: customdiff.All( + SetTagsDiff, + // This function exists to apply a default value to `instance_interruption_behavior` while + // accounting for the deprecated parameter `instance_interruption_behaviour`. It can be removed + // in favor of setting a `Default` on the parameter once `instance_interruption_behaviour` is removed. + // https://github.com/hashicorp/terraform-provider-aws/issues/20101 + func(_ context.Context, diff *schema.ResourceDiff, meta interface{}) error { + if v, ok := diff.GetOk("instance_interruption_behavior"); ok && v != "" { + return nil + } + if v, ok := diff.GetOk("instance_interruption_behaviour"); ok && v != "" { + diff.SetNew("instance_interruption_behavior", v) + return nil + } + diff.SetNew("instance_interruption_behavior", ec2.InstanceInterruptionBehaviorTerminate) + return nil + }, + ), } } @@ -139,7 +171,7 @@ func resourceAwsSpotInstanceRequestCreate(d *schema.ResourceData, meta interface spotOpts := &ec2.RequestSpotInstancesInput{ SpotPrice: aws.String(d.Get("spot_price").(string)), Type: aws.String(d.Get("spot_type").(string)), - InstanceInterruptionBehavior: aws.String(d.Get("instance_interruption_behaviour").(string)), + InstanceInterruptionBehavior: aws.String(d.Get("instance_interruption_behavior").(string)), TagSpecifications: ec2TagSpecificationsFromKeyValueTags(tags, ec2.ResourceTypeSpotInstancesRequest), // Though the AWS API supports creating spot instance requests for multiple @@ -188,7 +220,7 @@ func resourceAwsSpotInstanceRequestCreate(d *schema.ResourceData, meta interface } // Placement GroupName can only be specified when instanceInterruptionBehavior is not set or set to 'terminate' - if v, exists := d.GetOkExists("instance_interruption_behaviour"); v.(string) == ec2.InstanceInterruptionBehaviorTerminate || !exists { + if v, exists := d.GetOkExists("instance_interruption_behavior"); v.(string) == ec2.InstanceInterruptionBehaviorTerminate || !exists { spotOpts.LaunchSpecification.Placement = instanceOpts.SpotPlacement } @@ -341,6 +373,7 @@ func resourceAwsSpotInstanceRequestRead(d *schema.ResourceData, meta interface{} return fmt.Errorf("error setting tags_all: %w", err) } + d.Set("instance_interruption_behavior", request.InstanceInterruptionBehavior) d.Set("instance_interruption_behaviour", request.InstanceInterruptionBehavior) d.Set("valid_from", aws.TimeValue(request.ValidFrom).Format(time.RFC3339)) d.Set("valid_until", aws.TimeValue(request.ValidUntil).Format(time.RFC3339)) diff --git a/aws/resource_aws_spot_instance_request_test.go b/aws/resource_aws_spot_instance_request_test.go index 47debe63a05..233f2daca36 100644 --- a/aws/resource_aws_spot_instance_request_test.go +++ b/aws/resource_aws_spot_instance_request_test.go @@ -25,12 +25,13 @@ func TestAccAWSSpotInstanceRequest_basic(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccAWSSpotInstanceRequestConfig(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSSpotInstanceRequestExists(resourceName, &sir), testAccCheckAWSSpotInstanceRequestAttributes(&sir), testCheckKeyPair(rName, &sir), resource.TestCheckResourceAttr(resourceName, "spot_bid_status", "fulfilled"), resource.TestCheckResourceAttr(resourceName, "spot_request_state", "active"), + resource.TestCheckResourceAttr(resourceName, "instance_interruption_behavior", "terminate"), resource.TestCheckResourceAttr(resourceName, "instance_interruption_behaviour", "terminate"), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), @@ -58,7 +59,7 @@ func TestAccAWSSpotInstanceRequest_tags(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccAWSSpotInstanceRequestTagsConfig1(rName, "key1", "value1"), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSSpotInstanceRequestExists(resourceName, &sir), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), @@ -72,7 +73,7 @@ func TestAccAWSSpotInstanceRequest_tags(t *testing.T) { }, { Config: testAccAWSSpotInstanceRequestTagsConfig2(rName, "key1", "value1updated", "key2", "value2"), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSSpotInstanceRequestExists(resourceName, &sir), resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), @@ -81,7 +82,7 @@ func TestAccAWSSpotInstanceRequest_tags(t *testing.T) { }, { Config: testAccAWSSpotInstanceRequestTagsConfig1(rName, "key2", "value2"), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSSpotInstanceRequestExists(resourceName, &sir), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), @@ -104,7 +105,7 @@ func TestAccAWSSpotInstanceRequest_withLaunchGroup(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccAWSSpotInstanceRequestConfig_withLaunchGroup(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSSpotInstanceRequestExists(resourceName, &sir), testAccCheckAWSSpotInstanceRequestAttributes(&sir), testCheckKeyPair(rName, &sir), @@ -136,7 +137,7 @@ func TestAccAWSSpotInstanceRequest_withBlockDuration(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccAWSSpotInstanceRequestConfig_withBlockDuration(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSSpotInstanceRequestExists(resourceName, &sir), testAccCheckAWSSpotInstanceRequestAttributes(&sir), testCheckKeyPair(rName, &sir), @@ -168,7 +169,7 @@ func TestAccAWSSpotInstanceRequest_vpc(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccAWSSpotInstanceRequestConfigVPC(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSSpotInstanceRequestExists(resourceName, &sir), testAccCheckAWSSpotInstanceRequestAttributes(&sir), testCheckKeyPair(rName, &sir), @@ -201,7 +202,7 @@ func TestAccAWSSpotInstanceRequest_validUntil(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccAWSSpotInstanceRequestConfigValidUntil(rName, validUntil), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSSpotInstanceRequestExists(resourceName, &sir), testAccCheckAWSSpotInstanceRequestAttributes(&sir), testCheckKeyPair(rName, &sir), @@ -233,7 +234,7 @@ func TestAccAWSSpotInstanceRequest_withoutSpotPrice(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccAWSSpotInstanceRequestConfig_withoutSpotPrice(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSSpotInstanceRequestExists(resourceName, &sir), testAccCheckAWSSpotInstanceRequestAttributesCheckSIRWithoutSpot(&sir), resource.TestCheckResourceAttr(resourceName, "spot_bid_status", "fulfilled"), @@ -263,7 +264,7 @@ func TestAccAWSSpotInstanceRequest_SubnetAndSGAndPublicIpAddress(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccAWSSpotInstanceRequestConfig_SubnetAndSGAndPublicIpAddress(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSSpotInstanceRequestExists(resourceName, &sir), testAccCheckAWSSpotInstanceRequest_InstanceAttributes(&sir, rName), resource.TestCheckResourceAttr(resourceName, "associate_public_ip_address", "true"), @@ -292,7 +293,7 @@ func TestAccAWSSpotInstanceRequest_NetworkInterfaceAttributes(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccAWSSpotInstanceRequestConfig_SubnetAndSGAndPublicIpAddress(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSSpotInstanceRequestExists(resourceName, &sir), testAccCheckAWSSpotInstanceRequest_InstanceAttributes(&sir, rName), testAccCheckAWSSpotInstanceRequest_NetworkInterfaceAttributes(&sir), @@ -322,7 +323,7 @@ func TestAccAWSSpotInstanceRequest_getPasswordData(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccAWSSpotInstanceRequestConfig_getPasswordData(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSSpotInstanceRequestExists(resourceName, &sir), resource.TestCheckResourceAttrSet(resourceName, "password_data"), ), @@ -350,7 +351,7 @@ func TestAccAWSSpotInstanceRequest_disappears(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccAWSSpotInstanceRequestConfig(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSSpotInstanceRequestExists(resourceName, &sir), testAccCheckResourceDisappears(testAccProvider, resourceAwsSpotInstanceRequest(), resourceName), ), @@ -587,10 +588,11 @@ func TestAccAWSSpotInstanceRequest_InterruptStop(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccAWSSpotInstanceRequestInterruptConfig("stop"), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSSpotInstanceRequestExists(resourceName, &sir), resource.TestCheckResourceAttr(resourceName, "spot_bid_status", "fulfilled"), resource.TestCheckResourceAttr(resourceName, "spot_request_state", "active"), + resource.TestCheckResourceAttr(resourceName, "instance_interruption_behavior", "stop"), resource.TestCheckResourceAttr(resourceName, "instance_interruption_behaviour", "stop"), ), }, @@ -616,10 +618,11 @@ func TestAccAWSSpotInstanceRequest_InterruptHibernate(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccAWSSpotInstanceRequestInterruptConfig("hibernate"), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSSpotInstanceRequestExists(resourceName, &sir), resource.TestCheckResourceAttr(resourceName, "spot_bid_status", "fulfilled"), resource.TestCheckResourceAttr(resourceName, "spot_request_state", "active"), + resource.TestCheckResourceAttr(resourceName, "instance_interruption_behavior", "hibernate"), resource.TestCheckResourceAttr(resourceName, "instance_interruption_behaviour", "hibernate"), ), }, @@ -633,6 +636,149 @@ func TestAccAWSSpotInstanceRequest_InterruptHibernate(t *testing.T) { }) } +func TestAccAWSSpotInstanceRequest_InterruptUpdate(t *testing.T) { + var sir1, sir2 ec2.SpotInstanceRequest + resourceName := "aws_spot_instance_request.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, ec2.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSpotInstanceRequestDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSpotInstanceRequestInterruptConfig("hibernate"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSSpotInstanceRequestExists(resourceName, &sir1), + resource.TestCheckResourceAttr(resourceName, "instance_interruption_behavior", "hibernate"), + resource.TestCheckResourceAttr(resourceName, "instance_interruption_behaviour", "hibernate"), + ), + }, + { + Config: testAccAWSSpotInstanceRequestInterruptConfig("terminate"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSSpotInstanceRequestExists(resourceName, &sir2), + testAccCheckSpotInstanceRequestRecreated(&sir1, &sir2), + resource.TestCheckResourceAttr(resourceName, "instance_interruption_behavior", "terminate"), + resource.TestCheckResourceAttr(resourceName, "instance_interruption_behaviour", "terminate"), + ), + }, + }, + }) +} + +func TestAccAWSSpotInstanceRequest_InterruptDeprecated(t *testing.T) { + var sir ec2.SpotInstanceRequest + resourceName := "aws_spot_instance_request.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, ec2.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSpotInstanceRequestDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSpotInstanceRequestInterruptConfig_Deprecated("hibernate"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSSpotInstanceRequestExists(resourceName, &sir), + resource.TestCheckResourceAttr(resourceName, "spot_bid_status", "fulfilled"), + resource.TestCheckResourceAttr(resourceName, "spot_request_state", "active"), + resource.TestCheckResourceAttr(resourceName, "instance_interruption_behavior", "hibernate"), + resource.TestCheckResourceAttr(resourceName, "instance_interruption_behaviour", "hibernate"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"wait_for_fulfillment"}, + }, + }, + }) +} + +func TestAccAWSSpotInstanceRequest_InterruptFixDeprecated(t *testing.T) { + var sir1, sir2 ec2.SpotInstanceRequest + resourceName := "aws_spot_instance_request.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, ec2.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSpotInstanceRequestDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSpotInstanceRequestInterruptConfig_Deprecated("hibernate"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSSpotInstanceRequestExists(resourceName, &sir1), + resource.TestCheckResourceAttr(resourceName, "instance_interruption_behavior", "hibernate"), + resource.TestCheckResourceAttr(resourceName, "instance_interruption_behaviour", "hibernate"), + ), + }, + { + Config: testAccAWSSpotInstanceRequestInterruptConfig("hibernate"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSSpotInstanceRequestExists(resourceName, &sir2), + testAccCheckSpotInstanceRequestNotRecreated(&sir1, &sir2), + resource.TestCheckResourceAttr(resourceName, "instance_interruption_behavior", "hibernate"), + resource.TestCheckResourceAttr(resourceName, "instance_interruption_behaviour", "hibernate"), + ), + }, + }, + }) +} + +func TestAccAWSSpotInstanceRequest_InterruptUpdateFromDeprecated(t *testing.T) { + var sir1, sir2 ec2.SpotInstanceRequest + resourceName := "aws_spot_instance_request.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, ec2.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSpotInstanceRequestDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSpotInstanceRequestInterruptConfig_Deprecated("hibernate"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSSpotInstanceRequestExists(resourceName, &sir1), + resource.TestCheckResourceAttr(resourceName, "instance_interruption_behavior", "hibernate"), + resource.TestCheckResourceAttr(resourceName, "instance_interruption_behaviour", "hibernate"), + ), + }, + { + Config: testAccAWSSpotInstanceRequestInterruptConfig("stop"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSSpotInstanceRequestExists(resourceName, &sir2), + testAccCheckSpotInstanceRequestRecreated(&sir1, &sir2), + resource.TestCheckResourceAttr(resourceName, "instance_interruption_behavior", "stop"), + resource.TestCheckResourceAttr(resourceName, "instance_interruption_behaviour", "stop"), + ), + }, + }, + }) +} + +func testAccCheckSpotInstanceRequestRecreated(before, after *ec2.SpotInstanceRequest) resource.TestCheckFunc { + return func(s *terraform.State) error { + if before, after := aws.StringValue(before.InstanceId), aws.StringValue(after.InstanceId); before == after { + return fmt.Errorf("Spot Instance (%s) not recreated", before) + } + + return nil + } +} + +func testAccCheckSpotInstanceRequestNotRecreated(before, after *ec2.SpotInstanceRequest) resource.TestCheckFunc { + return func(s *terraform.State) error { + if before, after := aws.StringValue(before.InstanceId), aws.StringValue(after.InstanceId); before != after { + return fmt.Errorf("Spot Instance (%s/%s) recreated", before, after) + } + + return nil + } +} + func testAccAWSSpotInstanceRequestConfigBase(rName string) string { return fmt.Sprintf(` resource "aws_key_pair" "test" { @@ -906,6 +1052,21 @@ func testAccAWSSpotInstanceRequestInterruptConfig(interruptionBehavior string) s testAccLatestAmazonLinuxHvmEbsAmiConfig(), testAccAvailableEc2InstanceTypeForRegion("c5.large", "c4.large"), fmt.Sprintf(` +resource "aws_spot_instance_request" "test" { + ami = data.aws_ami.amzn-ami-minimal-hvm-ebs.id + instance_type = data.aws_ec2_instance_type_offering.available.instance_type + spot_price = "0.07" + wait_for_fulfillment = true + instance_interruption_behavior = %[1]q +} +`, interruptionBehavior)) +} + +func testAccAWSSpotInstanceRequestInterruptConfig_Deprecated(interruptionBehavior string) string { + return composeConfig( + testAccLatestAmazonLinuxHvmEbsAmiConfig(), + testAccAvailableEc2InstanceTypeForRegion("c5.large", "c4.large"), + fmt.Sprintf(` resource "aws_spot_instance_request" "test" { ami = data.aws_ami.amzn-ami-minimal-hvm-ebs.id instance_type = data.aws_ec2_instance_type_offering.available.instance_type diff --git a/website/docs/r/spot_instance_request.html.markdown b/website/docs/r/spot_instance_request.html.markdown index 1c9c424c57f..35f61ec88fe 100644 --- a/website/docs/r/spot_instance_request.html.markdown +++ b/website/docs/r/spot_instance_request.html.markdown @@ -63,7 +63,8 @@ Spot Instance Requests support all the same arguments as * `block_duration_minutes` - (Optional) The required duration for the Spot instances, in minutes. This value must be a multiple of 60 (60, 120, 180, 240, 300, or 360). The duration period starts as soon as your Spot instance receives its instance ID. At the end of the duration period, Amazon EC2 marks the Spot instance for termination and provides a Spot instance termination notice, which gives the instance a two-minute warning before it terminates. Note that you can't specify an Availability Zone group or a launch group if you specify a duration. -* `instance_interruption_behaviour` - (Optional) Indicates whether a Spot instance stops or terminates when it is interrupted. Default is `terminate` as this is the current AWS behaviour. +* `instance_interruption_behavior` - (Optional) Indicates Spot instance behavior when it is interrupted. Valid values are `terminate`, `stop`, or `hibernate`. Default value is `terminate`. +* `instance_interruption_behaviour` - (Optional, **Deprecated**) Indicates Spot instance behavior when it is interrupted. Valid values are `terminate`, `stop`, or `hibernate`. Default value is `terminate`. Use the argument `instance_interruption_behavior` instead. * `valid_until` - (Optional) The end date and time of the request, in UTC [RFC3339](https://tools.ietf.org/html/rfc3339#section-5.8) format(for example, YYYY-MM-DDTHH:MM:SSZ). At this point, no new Spot instance requests are placed or enabled to fulfill the request. The default end date is 7 days from the current date. * `valid_from` - (Optional) The start date and time of the request, in UTC [RFC3339](https://tools.ietf.org/html/rfc3339#section-5.8) format(for example, YYYY-MM-DDTHH:MM:SSZ). The default is to start fulfilling the request immediately. * `tags` - (Optional) A map of tags to assign to the Spot Instance Request. These tags are not automatically applied to the launched Instance. If configured with a provider [`default_tags` configuration block](/docs/providers/aws/index.html#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. From 4301719f11fd24b67cbc88f6ce3808771bc41364 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Wed, 14 Jul 2021 12:32:57 -0700 Subject: [PATCH 389/398] Updates dependency direction between Client VPN and Directory Service directory --- aws/resource_aws_directory_service_directory_test.go | 1 + aws/resource_aws_ec2_client_vpn_endpoint_test.go | 1 - aws/resource_aws_ec2_client_vpn_network_association_test.go | 3 --- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/aws/resource_aws_directory_service_directory_test.go b/aws/resource_aws_directory_service_directory_test.go index 78d9bfaf646..447d6939ba2 100644 --- a/aws/resource_aws_directory_service_directory_test.go +++ b/aws/resource_aws_directory_service_directory_test.go @@ -21,6 +21,7 @@ func init() { F: testSweepDirectoryServiceDirectories, Dependencies: []string{ "aws_db_instance", + "aws_ec2_client_vpn_endpoint", "aws_fsx_windows_file_system", "aws_workspaces_directory", }, diff --git a/aws/resource_aws_ec2_client_vpn_endpoint_test.go b/aws/resource_aws_ec2_client_vpn_endpoint_test.go index c6b3642fea5..96ed94b411b 100644 --- a/aws/resource_aws_ec2_client_vpn_endpoint_test.go +++ b/aws/resource_aws_ec2_client_vpn_endpoint_test.go @@ -29,7 +29,6 @@ func init() { Name: "aws_ec2_client_vpn_endpoint", F: testSweepEc2ClientVpnEndpoints, Dependencies: []string{ - "aws_directory_service_directory", "aws_ec2_client_vpn_network_association", }, }) diff --git a/aws/resource_aws_ec2_client_vpn_network_association_test.go b/aws/resource_aws_ec2_client_vpn_network_association_test.go index e783c1c7d66..38af12b6a9b 100644 --- a/aws/resource_aws_ec2_client_vpn_network_association_test.go +++ b/aws/resource_aws_ec2_client_vpn_network_association_test.go @@ -19,9 +19,6 @@ func init() { resource.AddTestSweepers("aws_ec2_client_vpn_network_association", &resource.Sweeper{ Name: "aws_ec2_client_vpn_network_association", F: testSweepEc2ClientVpnNetworkAssociations, - Dependencies: []string{ - "aws_directory_service_directory", - }, }) } From 60dd248d792fca03c0a70d844410da2e96e490f8 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Wed, 14 Jul 2021 12:45:24 -0700 Subject: [PATCH 390/398] Adds `sweeper` GitHub label --- infrastructure/repository/labels-workflow.tf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/infrastructure/repository/labels-workflow.tf b/infrastructure/repository/labels-workflow.tf index 59f161cd5db..d9321d8f10f 100644 --- a/infrastructure/repository/labels-workflow.tf +++ b/infrastructure/repository/labels-workflow.tf @@ -17,12 +17,13 @@ variable "workflow_labels" { "terraform-0.14" = "60dea9", # color:nomad "terraform-0.15" = "60dea9", # color:nomad "prerelease-tf-testing" = "60dea9", # color:nomad - "documentation" = "f4ecff", # color:terraform secondary "technical-debt" = "d1ebff", # color:terraform accent "proposal" = "d1ebff", # color:terraform accent + "documentation" = "f4ecff", # color:terraform secondary "thinking" = "f4ecff", # color:terraform secondary "question" = "f4ecff", # color:terraform secondary "linter" = "f4ecff", # color:terraform secondary + "sweeper" = "f4ecff", # color:terraform secondary "size/XS" = "62d4dc", # color:lightest-darkest waypoint gradient "size/S" = "4ec3ce", # color:lightest-darkest waypoint gradient "size/M" = "3bb3c0", # color:lightest-darkest waypoint gradient From 3ed100abdc0683945af2aca5ecfdf3af892c55ce Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Wed, 14 Jul 2021 13:02:25 -0700 Subject: [PATCH 391/398] Adds issue labeler configuration --- .github/labeler-issue-triage.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/labeler-issue-triage.yml b/.github/labeler-issue-triage.yml index 17d21eb0dca..7d3ec587771 100644 --- a/.github/labeler-issue-triage.yml +++ b/.github/labeler-issue-triage.yml @@ -14,6 +14,8 @@ bug: - "(doesn't support update|failed to satisfy constraint: Member must not be null|Invalid address to set|panic:|produced an (invalid|unexpected) new value|Provider produced inconsistent (final plan|result after apply))" crash: - 'panic:' +sweeper: + - 'sweeper' # # AWS Per-Service Labeling # From 3f7d0f8a372b19ed5b35992b04c6aef1562b5473 Mon Sep 17 00:00:00 2001 From: Simon Davis Date: Wed, 14 Jul 2021 15:16:36 -0700 Subject: [PATCH 392/398] refer to AWS docs for list of supported Policy Types (#20182) --- website/docs/r/fms_policy.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/fms_policy.html.markdown b/website/docs/r/fms_policy.html.markdown index ff4e5334a68..caff43dcecd 100644 --- a/website/docs/r/fms_policy.html.markdown +++ b/website/docs/r/fms_policy.html.markdown @@ -70,7 +70,7 @@ The following arguments are supported: ## `security_service_policy_data` Configuration Block * `managed_service_data` (Optional) Details about the service that are specific to the service type, in JSON format. For service type `SHIELD_ADVANCED`, this is an empty string. Examples depending on `type` can be found in the [AWS Firewall Manager SecurityServicePolicyData API Reference](https://docs.aws.amazon.com/fms/2018-01-01/APIReference/API_SecurityServicePolicyData.html). -* `type` - (Required, Forces new resource) The service that the policy is using to protect the resources. Valid values are `WAFV2`, `WAF`, `SHIELD_ADVANCED`, `SECURITY_GROUPS_COMMON`, `SECURITY_GROUPS_CONTENT_AUDIT`, and `SECURITY_GROUPS_USAGE_AUDIT`. +* `type` - (Required, Forces new resource) The service that the policy is using to protect the resources. For the current list of supported types, please refer to the [AWS Firewall Manager SecurityServicePolicyData API Type Reference](https://docs.aws.amazon.com/fms/2018-01-01/APIReference/API_SecurityServicePolicyData.html#fms-Type-SecurityServicePolicyData-Type). ## Attributes Reference From 793334da89cc5060fcd6bd6d9d3cde16f8a1e441 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Jul 2021 08:14:53 -0700 Subject: [PATCH 393/398] build(deps): bump actions/stale from 3 to 4 (#20193) Bumps [actions/stale](https://github.com/actions/stale) from 3 to 4. - [Release notes](https://github.com/actions/stale/releases) - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/stale/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/stale dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index a7e83bc1a05..da93bf58c9d 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -7,7 +7,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v3 + - uses: actions/stale@v4 with: repo-token: ${{ secrets.GITHUB_TOKEN }} days-before-stale: 720 From 0ff25a21249db81d9165907b6e7cd6ec6a08ec85 Mon Sep 17 00:00:00 2001 From: changelogbot Date: Thu, 15 Jul 2021 15:16:39 +0000 Subject: [PATCH 394/398] Update CHANGELOG.md for #20193 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4ceec4419b..4764a64aad7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ ENHANCEMENTS: * resource/aws_iam_access_key: Add encrypted SES SMTP password ([#19579](https://github.com/hashicorp/terraform-provider-aws/issues/19579)) * resource/aws_s3_bucket: Add the delete_marker_replication_status argument for V2 replication configurations ([#19323](https://github.com/hashicorp/terraform-provider-aws/issues/19323)) * resource/aws_s3_bucket_object: Add `source_hash` argument to compliment `etag`'s encryption limitations ([#11522](https://github.com/hashicorp/terraform-provider-aws/issues/11522)) +* resource/aws_sagemaker_domain: Add support for `retention_policy` ([#18562](https://github.com/hashicorp/terraform-provider-aws/issues/18562)) * resource/aws_wafv2_web_acl: Support `scope_down_statement` on `managed_rule_group_statement` ([#19407](https://github.com/hashicorp/terraform-provider-aws/issues/19407)) BUG FIXES: From 19f18ba8a65552b2fcc1ecc537d41cb41f69addc Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 15 Jul 2021 11:26:17 -0400 Subject: [PATCH 395/398] r/aws_kms_key: Remove 'multi_region' attribute. Multi-Region CMKs may require their own resource type. --- .changelog/19967.txt | 4 --- aws/resource_aws_kms_key.go | 8 ------ aws/resource_aws_kms_key_test.go | 39 ---------------------------- website/docs/r/kms_key.html.markdown | 3 +-- 4 files changed, 1 insertion(+), 53 deletions(-) diff --git a/.changelog/19967.txt b/.changelog/19967.txt index 1c64698511d..98b176d6c44 100644 --- a/.changelog/19967.txt +++ b/.changelog/19967.txt @@ -1,7 +1,3 @@ -```release-note:enhancement -resource/aws_kms_key: Add `multi_region` argument. -``` - ```release-note:enhancement resource/aws_kms_key: Add plan time validation to `description`. ``` diff --git a/aws/resource_aws_kms_key.go b/aws/resource_aws_kms_key.go index 37b05687b69..586a7839fb9 100644 --- a/aws/resource_aws_kms_key.go +++ b/aws/resource_aws_kms_key.go @@ -70,12 +70,6 @@ func resourceAwsKmsKey() *schema.Resource { Optional: true, Default: true, }, - "multi_region": { - Type: schema.TypeBool, - Optional: true, - ForceNew: true, - Default: false, - }, "enable_key_rotation": { Type: schema.TypeBool, Optional: true, @@ -101,7 +95,6 @@ func resourceAwsKmsKeyCreate(d *schema.ResourceData, meta interface{}) error { req := &kms.CreateKeyInput{ CustomerMasterKeySpec: aws.String(d.Get("customer_master_key_spec").(string)), KeyUsage: aws.String(d.Get("key_usage").(string)), - MultiRegion: aws.Bool(d.Get("multi_region").(bool)), } if v, exists := d.GetOk("description"); exists { req.Description = aws.String(v.(string)) @@ -191,7 +184,6 @@ func resourceAwsKmsKeyRead(d *schema.ResourceData, meta interface{}) error { d.Set("key_usage", metadata.KeyUsage) d.Set("customer_master_key_spec", metadata.CustomerMasterKeySpec) d.Set("is_enabled", metadata.Enabled) - d.Set("multi_region", metadata.MultiRegion) pOut, err := retryOnAwsCode(kms.ErrCodeNotFoundException, func() (interface{}, error) { return conn.GetKeyPolicy(&kms.GetKeyPolicyInput{ diff --git a/aws/resource_aws_kms_key_test.go b/aws/resource_aws_kms_key_test.go index 3d33f1acadb..fce4b5b8a3f 100644 --- a/aws/resource_aws_kms_key_test.go +++ b/aws/resource_aws_kms_key_test.go @@ -87,7 +87,6 @@ func TestAccAWSKmsKey_basic(t *testing.T) { testAccCheckAWSKmsKeyExists(resourceName, &key), resource.TestCheckResourceAttr(resourceName, "customer_master_key_spec", "SYMMETRIC_DEFAULT"), resource.TestCheckResourceAttr(resourceName, "key_usage", "ENCRYPT_DECRYPT"), - resource.TestCheckResourceAttr(resourceName, "multi_region", "false"), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, @@ -320,34 +319,6 @@ func TestAccAWSKmsKey_tags(t *testing.T) { }) } -func TestAccAWSKmsKey_multiRegion(t *testing.T) { - var key kms.KeyMetadata - rName := fmt.Sprintf("tf-testacc-kms-key-%s", acctest.RandString(13)) - resourceName := "aws_kms_key.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ErrorCheck: testAccErrorCheck(t, kms.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSKmsKeyDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSKmsKeyMultiRegionConfig(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSKmsKeyExists(resourceName, &key), - resource.TestCheckResourceAttr(resourceName, "multi_region", "true"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"deletion_window_in_days"}, - }, - }, - }) -} - func testAccCheckAWSKmsKeyHasPolicy(name string, expectedPolicyText string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[name] @@ -676,13 +647,3 @@ resource "aws_kms_key" "test" { } `, rName) } - -func testAccAWSKmsKeyMultiRegionConfig(rName string) string { - return fmt.Sprintf(` -resource "aws_kms_key" "test" { - description = %[1]q - deletion_window_in_days = 7 - multi_region = true -} -`, rName) -} diff --git a/website/docs/r/kms_key.html.markdown b/website/docs/r/kms_key.html.markdown index 5dbcbdee834..0a063d31a6b 100644 --- a/website/docs/r/kms_key.html.markdown +++ b/website/docs/r/kms_key.html.markdown @@ -8,7 +8,7 @@ description: |- # Resource: aws_kms_key -Provides a KMS customer master key. +Provides a KMS single-Region customer master key (CMK). ## Example Usage @@ -35,7 +35,6 @@ Valid values: `SYMMETRIC_DEFAULT`, `RSA_2048`, `RSA_3072`, `RSA_4096`, `ECC_NIS * `deletion_window_in_days` - (Optional) Duration in days after which the key is deleted after destruction of the resource, must be between 7 and 30 days. Defaults to 30 days. * `is_enabled` - (Optional) Specifies whether the key is enabled. Defaults to true. * `enable_key_rotation` - (Optional) Specifies whether [key rotation](http://docs.aws.amazon.com/kms/latest/developerguide/rotate-keys.html) is enabled. Defaults to false. -* `multi_region` - (Optional) Creates a multi-Region primary key that you can replicate into other AWS Regions. You cannot change this value after you create the CMK. Defaults to false. * `tags` - (Optional) A map of tags to assign to the object. If configured with a provider [`default_tags` configuration block](https://www.terraform.io/docs/providers/aws/index.html#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. ## Attributes Reference From 3fec18681953486bf2dc5f591e653b5e7e5220c3 Mon Sep 17 00:00:00 2001 From: changelogbot Date: Thu, 15 Jul 2021 16:21:53 +0000 Subject: [PATCH 396/398] Update CHANGELOG.md (Manual Trigger) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4764a64aad7..2d600b8d8fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ ENHANCEMENTS: * resource/aws_guardduty_detector: Add `datasources` argument ([#19954](https://github.com/hashicorp/terraform-provider-aws/issues/19954)) * resource/aws_guardduty_organization_configuration: Add `datasources` argument ([#15241](https://github.com/hashicorp/terraform-provider-aws/issues/15241)) * resource/aws_iam_access_key: Add encrypted SES SMTP password ([#19579](https://github.com/hashicorp/terraform-provider-aws/issues/19579)) +* resource/aws_kms_key: Add plan time validation to `description`. ([#19967](https://github.com/hashicorp/terraform-provider-aws/issues/19967)) * resource/aws_s3_bucket: Add the delete_marker_replication_status argument for V2 replication configurations ([#19323](https://github.com/hashicorp/terraform-provider-aws/issues/19323)) * resource/aws_s3_bucket_object: Add `source_hash` argument to compliment `etag`'s encryption limitations ([#11522](https://github.com/hashicorp/terraform-provider-aws/issues/11522)) * resource/aws_sagemaker_domain: Add support for `retention_policy` ([#18562](https://github.com/hashicorp/terraform-provider-aws/issues/18562)) From 1f52c98e9b9b22252ee4957d3658a6c1513006f1 Mon Sep 17 00:00:00 2001 From: Simon Davis Date: Thu, 15 Jul 2021 10:17:40 -0700 Subject: [PATCH 397/398] change name of org scoped token to conform to new naming restrictions (#20202) --- .github/workflows/project.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/team_slack_bot.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/project.yml b/.github/workflows/project.yml index 7c9f0d5852a..8bc4c1ddb3d 100644 --- a/.github/workflows/project.yml +++ b/.github/workflows/project.yml @@ -12,4 +12,4 @@ jobs: with: project: AWS Provider Working Board column: Open Maintainer PR - repo-token: ${{ secrets.GITHUB_ACTIONS_TOKEN}} + repo-token: ${{ secrets.ORGSCOPED_GITHUB_TOKEN}} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 90969c0596b..e86bad8f7b2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,7 +24,7 @@ jobs: with: github_done_column_id: 11513756 github_release_name: ${{ github.event.release.tag_name }} - github_token: ${{ secrets.GITHUB_ACTIONS_TOKEN }} + github_token: ${{ secrets.ORGSCOPED_GITHUB_TOKEN }} changelog-newversion: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/team_slack_bot.yml b/.github/workflows/team_slack_bot.yml index 77900fc0f94..4dbfc3e4f46 100644 --- a/.github/workflows/team_slack_bot.yml +++ b/.github/workflows/team_slack_bot.yml @@ -13,7 +13,7 @@ jobs: - name: open-pr-stats uses: breathingdust/github-team-slackbot@v17 with: - github_token: ${{ secrets.GITHUB_ACTIONS_TOKEN}} + github_token: ${{ secrets.ORGSCOPED_GITHUB_TOKEN}} org: hashicorp repo: terraform-provider-aws team_slug: terraform-aws From 060fabcfed0e033b38fa4d24ed29e3f28ef00083 Mon Sep 17 00:00:00 2001 From: tf-release-bot Date: Thu, 15 Jul 2021 23:26:04 +0000 Subject: [PATCH 398/398] v3.50.0 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d600b8d8fd..947d5f13464 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## 3.50.0 (Unreleased) +## 3.50.0 (July 15, 2021) NOTES: