From 9880584ece3be33e3d28496fce6853d03abb8efc Mon Sep 17 00:00:00 2001 From: Farhan Angullia Date: Mon, 9 Aug 2021 17:45:47 +0800 Subject: [PATCH 1/7] added aws_shield_protection_group to resource map --- aws/provider.go | 1 + 1 file changed, 1 insertion(+) diff --git a/aws/provider.go b/aws/provider.go index 872662fcedc..2a57b50317d 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -1085,6 +1085,7 @@ func Provider() *schema.Provider { "aws_service_discovery_service": resourceAwsServiceDiscoveryService(), "aws_servicequotas_service_quota": resourceAwsServiceQuotasServiceQuota(), "aws_shield_protection": resourceAwsShieldProtection(), + "aws_shield_protection_group": resourceAwsShieldProtectionGroup(), "aws_signer_signing_job": resourceAwsSignerSigningJob(), "aws_signer_signing_profile": resourceAwsSignerSigningProfile(), "aws_signer_signing_profile_permission": resourceAwsSignerSigningProfilePermission(), From bd41c6a5065bb168652dc72dffc84cda1c3f58cd Mon Sep 17 00:00:00 2001 From: Farhan Angullia Date: Mon, 9 Aug 2021 17:47:06 +0800 Subject: [PATCH 2/7] added aws_shield_protection_group resource --- aws/resource_aws_shield_protection_group.go | 209 ++++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 aws/resource_aws_shield_protection_group.go diff --git a/aws/resource_aws_shield_protection_group.go b/aws/resource_aws_shield_protection_group.go new file mode 100644 index 00000000000..8952a3cfead --- /dev/null +++ b/aws/resource_aws_shield_protection_group.go @@ -0,0 +1,209 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/shield" + "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" +) + +func resourceAwsShieldProtectionGroup() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsShieldProtectionGroupCreate, + Read: resourceAwsShieldProtectionGroupRead, + Update: resourceAwsShieldProtectionGroupUpdate, + Delete: resourceAwsShieldProtectionGroupDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "aggregation": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(shield.ProtectionGroupAggregation_Values(), false), + }, + "members": { + Type: schema.TypeList, + Optional: true, + MinItems: 0, + MaxItems: 10000, + ConflictsWith: []string{"resource_type"}, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.All(validateArn, + validation.StringLenBetween(1, 2048), + ), + }, + }, + "pattern": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(shield.ProtectionGroupPattern_Values(), false), + }, + "protection_group_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 36), + ForceNew: true, + }, + "protection_group_arn": { + Type: schema.TypeString, + Computed: true, + }, + "resource_type": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"members"}, + ValidateFunc: validation.StringInSlice(shield.ProtectedResourceType_Values(), false), + }, + "tags": tagsSchema(), + "tags_all": tagsSchemaComputed(), + }, + CustomizeDiff: SetTagsDiff, + } +} + +func resourceAwsShieldProtectionGroupCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).shieldconn + + protectionGroupId := d.Get("protection_group_id").(string) + pattern := d.Get("pattern").(string) + + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig + tags := defaultTagsConfig.MergeTags(keyvaluetags.New(d.Get("tags").(map[string]interface{}))) + + input := &shield.CreateProtectionGroupInput{ + Aggregation: aws.String(d.Get("aggregation").(string)), + Pattern: aws.String(pattern), + ProtectionGroupId: aws.String(protectionGroupId), + Tags: tags.IgnoreAws().ShieldTags(), + } + + if v, ok := d.GetOk("members"); ok { + input.Members = expandStringList(v.([]interface{})) + } + + if v, ok := d.GetOk("resource_type"); ok { + input.ResourceType = aws.String(v.(string)) + } + + _, err := conn.CreateProtectionGroup(input) + if err != nil { + return fmt.Errorf("error creating Shield Protection Group: %s", err) + } + + d.SetId(protectionGroupId) + + return resourceAwsShieldProtectionGroupRead(d, meta) +} + +func resourceAwsShieldProtectionGroupRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).shieldconn + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + + input := &shield.DescribeProtectionGroupInput{ + ProtectionGroupId: aws.String(d.Id()), + } + + resp, err := conn.DescribeProtectionGroup(input) + + if err != nil { + if tfawserr.ErrCodeEquals(err, shield.ErrCodeResourceNotFoundException) { + log.Printf("[WARN] Shield Protection Group (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + return fmt.Errorf("error reading Shield Protection Group (%s): %s", d.Id(), err) + } + + arn := aws.StringValue(resp.ProtectionGroup.ProtectionGroupArn) + d.Set("protection_group_arn", arn) + d.Set("aggregation", resp.ProtectionGroup.Aggregation) + d.Set("protection_group_id", resp.ProtectionGroup.ProtectionGroupId) + d.Set("pattern", resp.ProtectionGroup.Pattern) + + if resp.ProtectionGroup.Members != nil { + d.Set("members", resp.ProtectionGroup.Members) + } + + if resp.ProtectionGroup.ResourceType != nil { + d.Set("resource_type", resp.ProtectionGroup.ResourceType) + } + + tags, err := keyvaluetags.ShieldListTags(conn, arn) + + if err != nil { + return fmt.Errorf("error listing tags for Shield Protection Group (%s): %s", arn, 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 +} + +func resourceAwsShieldProtectionGroupUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).shieldconn + + input := &shield.UpdateProtectionGroupInput{ + Aggregation: aws.String(d.Get("aggregation").(string)), + Pattern: aws.String(d.Get("pattern").(string)), + ProtectionGroupId: aws.String(d.Id()), + } + + if v, ok := d.GetOk("members"); ok { + input.Members = expandStringList(v.([]interface{})) + } + + if v, ok := d.GetOk("resource_type"); ok { + input.ResourceType = aws.String(v.(string)) + } + + _, err := conn.UpdateProtectionGroup(input) + if err != nil { + return fmt.Errorf("error updating Shield Protection Group: %s", err) + } + + if d.HasChange("tags_all") { + o, n := d.GetChange("tags_all") + if err := keyvaluetags.ShieldUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return fmt.Errorf("error updating tags: %s", err) + } + } + + return resourceAwsShieldProtectionGroupRead(d, meta) +} + +func resourceAwsShieldProtectionGroupDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).shieldconn + + input := &shield.DeleteProtectionGroupInput{ + ProtectionGroupId: aws.String(d.Id()), + } + + _, err := conn.DeleteProtectionGroup(input) + + if err != nil { + if tfawserr.ErrCodeEquals(err, shield.ErrCodeResourceNotFoundException) { + return nil + } + return fmt.Errorf("error deleting Shield Protection Group (%s): %s", d.Id(), err) + } + + return nil +} From 4e41964115ac9e30f5c76d1ada26df8aa6472b37 Mon Sep 17 00:00:00 2001 From: Farhan Angullia Date: Mon, 9 Aug 2021 17:47:22 +0800 Subject: [PATCH 3/7] added acc tests --- ...source_aws_shield_protection_group_test.go | 333 ++++++++++++++++++ 1 file changed, 333 insertions(+) create mode 100644 aws/resource_aws_shield_protection_group_test.go diff --git a/aws/resource_aws_shield_protection_group_test.go b/aws/resource_aws_shield_protection_group_test.go new file mode 100644 index 00000000000..0b144b440d8 --- /dev/null +++ b/aws/resource_aws_shield_protection_group_test.go @@ -0,0 +1,333 @@ +package aws + +import ( + "fmt" + "regexp" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/shield" + "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 TestAccAWSShieldProtectionGroup_basic(t *testing.T) { + resourceName := "aws_shield_protection_group.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPartitionHasServicePreCheck(shield.EndpointsID, t) + testAccPreCheckAWSShield(t) + }, + ErrorCheck: testAccErrorCheck(t, shield.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSShieldProtectionGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccShieldProtectionGroupConfig_basic_all(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSShieldProtectionGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "aggregation", shield.ProtectionGroupAggregationMax), + resource.TestCheckNoResourceAttr(resourceName, "members"), + resource.TestCheckResourceAttr(resourceName, "pattern", shield.ProtectionGroupPatternAll), + resource.TestCheckNoResourceAttr(resourceName, "resource_type"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSShieldProtectionGroup_disappears(t *testing.T) { + resourceName := "aws_shield_protection_group.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPartitionHasServicePreCheck(shield.EndpointsID, t) + testAccPreCheckAWSShield(t) + }, + ErrorCheck: testAccErrorCheck(t, shield.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSShieldProtectionGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccShieldProtectionGroupConfig_basic_all(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSShieldProtectionGroupExists(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsShieldProtectionGroup(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAWSShieldProtectionGroup_aggregation(t *testing.T) { + resourceName := "aws_shield_protection_group.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPartitionHasServicePreCheck(shield.EndpointsID, t) + testAccPreCheckAWSShield(t) + }, + ErrorCheck: testAccErrorCheck(t, shield.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSShieldProtectionGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccShieldProtectionGroupConfig_aggregation(rName, shield.ProtectionGroupAggregationMean), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSShieldProtectionGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "aggregation", shield.ProtectionGroupAggregationMean), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccShieldProtectionGroupConfig_aggregation(rName, shield.ProtectionGroupAggregationSum), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSShieldProtectionGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "aggregation", shield.ProtectionGroupAggregationSum), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSShieldProtectionGroup_members(t *testing.T) { + resourceName := "aws_shield_protection_group.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPartitionHasServicePreCheck(shield.EndpointsID, t) + testAccPreCheckAWSShield(t) + }, + ErrorCheck: testAccErrorCheck(t, shield.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSShieldProtectionGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccShieldProtectionGroupConfig_members(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSShieldProtectionGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "pattern", shield.ProtectionGroupPatternArbitrary), + resource.TestCheckResourceAttr(resourceName, "members.#", "1"), + testAccMatchResourceAttrRegionalARN(resourceName, "members.0", "ec2", regexp.MustCompile(`eip-allocation/eipalloc-.+`)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSShieldProtectionGroup_protectionGroupId(t *testing.T) { + resourceName := "aws_shield_protection_group.test" + testID1 := acctest.RandomWithPrefix("tf-acc-test") + testID2 := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPartitionHasServicePreCheck(shield.EndpointsID, t) + testAccPreCheckAWSShield(t) + }, + ErrorCheck: testAccErrorCheck(t, shield.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSShieldProtectionGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccShieldProtectionGroupConfig_basic_all(testID1), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSShieldProtectionGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "protection_group_id", testID1), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccShieldProtectionGroupConfig_basic_all(testID2), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSShieldProtectionGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "protection_group_id", testID2), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSShieldProtectionGroup_resourceType(t *testing.T) { + resourceName := "aws_shield_protection_group.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPartitionHasServicePreCheck(shield.EndpointsID, t) + testAccPreCheckAWSShield(t) + }, + ErrorCheck: testAccErrorCheck(t, shield.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSShieldProtectionGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccShieldProtectionGroupConfig_resourceType(rName, shield.ProtectedResourceTypeElasticIpAllocation), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSShieldProtectionGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "pattern", shield.ProtectionGroupPatternByResourceType), + resource.TestCheckResourceAttr(resourceName, "resource_type", shield.ProtectedResourceTypeElasticIpAllocation), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccShieldProtectionGroupConfig_resourceType(rName, shield.ProtectedResourceTypeApplicationLoadBalancer), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSShieldProtectionGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "pattern", shield.ProtectionGroupPatternByResourceType), + resource.TestCheckResourceAttr(resourceName, "resource_type", shield.ProtectedResourceTypeApplicationLoadBalancer), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckAWSShieldProtectionGroupDestroy(s *terraform.State) error { + shieldconn := testAccProvider.Meta().(*AWSClient).shieldconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_shield_protection_group" { + continue + } + + input := &shield.DescribeProtectionGroupInput{ + ProtectionGroupId: aws.String(rs.Primary.ID), + } + + resp, err := shieldconn.DescribeProtectionGroup(input) + + if tfawserr.ErrCodeEquals(err, shield.ErrCodeResourceNotFoundException) { + continue + } + + if err != nil { + return err + } + + if resp != nil && resp.ProtectionGroup != nil && aws.StringValue(resp.ProtectionGroup.ProtectionGroupId) == rs.Primary.ID { + return fmt.Errorf("The Shield protection group with ID %v still exists", rs.Primary.ID) + } + } + + return nil +} + +func testAccCheckAWSShieldProtectionGroupExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + conn := testAccProvider.Meta().(*AWSClient).shieldconn + + input := &shield.DescribeProtectionGroupInput{ + ProtectionGroupId: aws.String(rs.Primary.ID), + } + + _, err := conn.DescribeProtectionGroup(input) + + if err != nil { + return err + } + + return nil + } +} + +func testAccShieldProtectionGroupConfig_basic_all(rName string) string { + return fmt.Sprintf(` +resource "aws_shield_protection_group" "test" { + protection_group_id = "%s" + aggregation = "MAX" + pattern = "ALL" +} +`, rName) +} + +func testAccShieldProtectionGroupConfig_aggregation(rName string, aggregation string) string { + return fmt.Sprintf(` +resource "aws_shield_protection_group" "test" { + protection_group_id = "%[1]s" + aggregation = "%[2]s" + pattern = "ALL" +} +`, rName, aggregation) +} + +func testAccShieldProtectionGroupConfig_resourceType(rName string, resType string) string { + return fmt.Sprintf(` +resource "aws_shield_protection_group" "test" { + protection_group_id = "%[1]s" + aggregation = "MAX" + pattern = "BY_RESOURCE_TYPE" + resource_type = "%[2]s" +} +`, rName, resType) +} + +func testAccShieldProtectionGroupConfig_members(rName string) string { + return composeConfig(testAccShieldProtectionElasticIPAddressConfig(rName), fmt.Sprintf(` +resource "aws_shield_protection_group" "test" { + depends_on = [aws_shield_protection.acctest] + + protection_group_id = "%[1]s" + aggregation = "MAX" + pattern = "ARBITRARY" + members = ["arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:eip-allocation/${aws_eip.acctest.id}"] +} +`, rName)) +} From 20c7d4fda13440d502dff36b54ea5067b23ba6cb Mon Sep 17 00:00:00 2001 From: Farhan Angullia Date: Mon, 9 Aug 2021 17:47:45 +0800 Subject: [PATCH 4/7] added site docs for shield_protection_group --- .../r/shield_protection_group.html.markdown | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 website/docs/r/shield_protection_group.html.markdown diff --git a/website/docs/r/shield_protection_group.html.markdown b/website/docs/r/shield_protection_group.html.markdown new file mode 100644 index 00000000000..4fa71146b44 --- /dev/null +++ b/website/docs/r/shield_protection_group.html.markdown @@ -0,0 +1,87 @@ +--- +subcategory: "Shield" +layout: "aws" +page_title: "AWS: aws_shield_protection_group" +description: |- +Creates a grouping of protected resources so they can be handled as a collective. +--- + +# Resource: aws_shield_protection_group + +Creates a grouping of protected resources so they can be handled as a collective. +This resource grouping improves the accuracy of detection and reduces false positives. For more information see +[Managing AWS Shield Advanced protection groups](https://docs.aws.amazon.com/waf/latest/developerguide/manage-protection-group.html) + +## Example Usage + +### Create protection group for all resources + +```terraform +resource "aws_shield_protection_group" "example" { + protection_group_id = "example" + aggregation = "MAX" + pattern = "ALL" +} +``` + +### Create protection group for abritrary number of resources + +```terraform +data "aws_region" "current" {} +data "aws_caller_identity" "current" {} + +resource "aws_eip" "example" { + vpc = true +} + +resource "aws_shield_protection" "example" { + name = "example" + resource_arn = "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:eip-allocation/${aws_eip.example.id}" +} + +resource "aws_shield_protection_group" "example" { + depends_on = [aws_shield_protection.example] + + protection_group_id = "example" + aggregation = "MEAN" + pattern = "ARBITRARY" + members = ["arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:eip-allocation/${aws_eip.example.id}"] +} +``` + +### Create protection group for a type of resource + +```terraform +resource "aws_shield_protection_group" "example" { + protection_group_id = "example" + aggregation = "SUM" + pattern = "BY_RESOURCE_TYPE" + resource_type = "ELASTIC_IP_ALLOCATION" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `aggregation` - (Required) Defines how AWS Shield combines resource data for the group in order to detect, mitigate, and report events. +* `members` - (Optional) The Amazon Resource Names (ARNs) of the resources to include in the protection group. You must set this when you set `pattern` to ARBITRARY and you must not set it for any other `pattern` setting. +* `pattern` - (Required) The criteria to use to choose the protected resources for inclusion in the group. +* `protection_group_id` - (Required) The name of the protection group. +* `resource_type` - (Optional) The resource type to include in the protection group. You must set this when you set `pattern` to BY_RESOURCE_TYPE and you must not set it for any other `pattern` setting. +* `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. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `protection_group_arn` - The ARN (Amazon Resource Name) of the protection group. +* `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 + +Shield protection group resources can be imported by specifying their name. + +``` +$ terraform import aws_shield_protection_group.example example +``` From db789a6c5545b5430b74e6ed213160f2f75343a7 Mon Sep 17 00:00:00 2001 From: Farhan Angullia Date: Mon, 9 Aug 2021 17:58:13 +0800 Subject: [PATCH 5/7] fixed spelling in site docs --- website/docs/r/shield_protection_group.html.markdown | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/docs/r/shield_protection_group.html.markdown b/website/docs/r/shield_protection_group.html.markdown index 4fa71146b44..5228b4f81ed 100644 --- a/website/docs/r/shield_protection_group.html.markdown +++ b/website/docs/r/shield_protection_group.html.markdown @@ -3,7 +3,7 @@ subcategory: "Shield" layout: "aws" page_title: "AWS: aws_shield_protection_group" description: |- -Creates a grouping of protected resources so they can be handled as a collective. + Creates a grouping of protected resources so they can be handled as a collective. --- # Resource: aws_shield_protection_group @@ -24,7 +24,7 @@ resource "aws_shield_protection_group" "example" { } ``` -### Create protection group for abritrary number of resources +### Create protection group for arbitrary number of resources ```terraform data "aws_region" "current" {} @@ -80,7 +80,7 @@ In addition to all arguments above, the following attributes are exported: ## Import -Shield protection group resources can be imported by specifying their name. +Shield protection group resources can be imported by specifying their protection group id. ``` $ terraform import aws_shield_protection_group.example example From 45ed1da692399dfbd4157ee6c401e84dafe3a92a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 11 Aug 2021 09:48:13 -0400 Subject: [PATCH 6/7] Add CHANGELOG entry. --- .changelog/20491.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/20491.txt diff --git a/.changelog/20491.txt b/.changelog/20491.txt new file mode 100644 index 00000000000..ed38fde47df --- /dev/null +++ b/.changelog/20491.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_shield_protection_group +``` \ No newline at end of file From 6d33716fde7f9cae08ae15fee36ce4488e600365 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 11 Aug 2021 09:59:34 -0400 Subject: [PATCH 7/7] r/aws_shield_protection_group: Prefer '%w' in 'fmt.Errorf' (#12991). --- aws/resource_aws_shield_protection_group.go | 50 +++++++++++---------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/aws/resource_aws_shield_protection_group.go b/aws/resource_aws_shield_protection_group.go index 8952a3cfead..4238ba26d57 100644 --- a/aws/resource_aws_shield_protection_group.go +++ b/aws/resource_aws_shield_protection_group.go @@ -71,17 +71,14 @@ func resourceAwsShieldProtectionGroup() *schema.Resource { func resourceAwsShieldProtectionGroupCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).shieldconn - - protectionGroupId := d.Get("protection_group_id").(string) - pattern := d.Get("pattern").(string) - defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig tags := defaultTagsConfig.MergeTags(keyvaluetags.New(d.Get("tags").(map[string]interface{}))) + protectionGroupID := d.Get("protection_group_id").(string) input := &shield.CreateProtectionGroupInput{ Aggregation: aws.String(d.Get("aggregation").(string)), - Pattern: aws.String(pattern), - ProtectionGroupId: aws.String(protectionGroupId), + Pattern: aws.String(d.Get("pattern").(string)), + ProtectionGroupId: aws.String(protectionGroupID), Tags: tags.IgnoreAws().ShieldTags(), } @@ -93,12 +90,14 @@ func resourceAwsShieldProtectionGroupCreate(d *schema.ResourceData, meta interfa input.ResourceType = aws.String(v.(string)) } + log.Printf("[DEBUG] Creating Shield Protection Group: %s", input) _, err := conn.CreateProtectionGroup(input) + if err != nil { - return fmt.Errorf("error creating Shield Protection Group: %s", err) + return fmt.Errorf("error creating Shield Protection Group (%s): %w", protectionGroupID, err) } - d.SetId(protectionGroupId) + d.SetId(protectionGroupID) return resourceAwsShieldProtectionGroupRead(d, meta) } @@ -114,13 +113,14 @@ func resourceAwsShieldProtectionGroupRead(d *schema.ResourceData, meta interface resp, err := conn.DescribeProtectionGroup(input) + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, shield.ErrCodeResourceNotFoundException) { + log.Printf("[WARN] Shield Protection Group (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + if err != nil { - if tfawserr.ErrCodeEquals(err, shield.ErrCodeResourceNotFoundException) { - log.Printf("[WARN] Shield Protection Group (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } - return fmt.Errorf("error reading Shield Protection Group (%s): %s", d.Id(), err) + return fmt.Errorf("error reading Shield Protection Group (%s): %w", d.Id(), err) } arn := aws.StringValue(resp.ProtectionGroup.ProtectionGroupArn) @@ -140,7 +140,7 @@ func resourceAwsShieldProtectionGroupRead(d *schema.ResourceData, meta interface tags, err := keyvaluetags.ShieldListTags(conn, arn) if err != nil { - return fmt.Errorf("error listing tags for Shield Protection Group (%s): %s", arn, err) + return fmt.Errorf("error listing tags for Shield Protection Group (%s): %w", arn, err) } tags = tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig) @@ -174,15 +174,17 @@ func resourceAwsShieldProtectionGroupUpdate(d *schema.ResourceData, meta interfa input.ResourceType = aws.String(v.(string)) } + log.Printf("[DEBUG] Updating Shield Protection Group: %s", input) _, err := conn.UpdateProtectionGroup(input) + if err != nil { - return fmt.Errorf("error updating Shield Protection Group: %s", err) + return fmt.Errorf("error updating Shield Protection Group (%s): %w", d.Id(), err) } if d.HasChange("tags_all") { o, n := d.GetChange("tags_all") if err := keyvaluetags.ShieldUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { - return fmt.Errorf("error updating tags: %s", err) + return fmt.Errorf("error updating tags: %w", err) } } @@ -192,17 +194,17 @@ func resourceAwsShieldProtectionGroupUpdate(d *schema.ResourceData, meta interfa func resourceAwsShieldProtectionGroupDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).shieldconn - input := &shield.DeleteProtectionGroupInput{ + log.Printf("[DEBUG] Deletinh Shield Protection Group: %s", d.Id()) + _, err := conn.DeleteProtectionGroup(&shield.DeleteProtectionGroupInput{ ProtectionGroupId: aws.String(d.Id()), - } + }) - _, err := conn.DeleteProtectionGroup(input) + if tfawserr.ErrCodeEquals(err, shield.ErrCodeResourceNotFoundException) { + return nil + } if err != nil { - if tfawserr.ErrCodeEquals(err, shield.ErrCodeResourceNotFoundException) { - return nil - } - return fmt.Errorf("error deleting Shield Protection Group (%s): %s", d.Id(), err) + return fmt.Errorf("error deleting Shield Protection Group (%s): %w", d.Id(), err) } return nil