From bb263a8259a94286846a28c8e0c16b43d03067e4 Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Fri, 28 Aug 2020 11:01:47 -0400 Subject: [PATCH 1/7] update redacted_fields arg and add apply-time error handling --- aws/resource_aws_wafv2_rule_group.go | 20 +- ...aws_wafv2_web_acl_logging_configuration.go | 208 +++++++++- ...afv2_web_acl_logging_configuration_test.go | 373 +++++++++++++++++- aws/wafv2_helper.go | 268 +++++++++---- website/docs/r/wafv2_rule_group.html.markdown | 5 +- website/docs/r/wafv2_web_acl.html.markdown | 11 +- ...eb_acl_logging_configuration.html.markdown | 10 +- 7 files changed, 777 insertions(+), 118 deletions(-) diff --git a/aws/resource_aws_wafv2_rule_group.go b/aws/resource_aws_wafv2_rule_group.go index b523161891a..bea165b1b04 100644 --- a/aws/resource_aws_wafv2_rule_group.go +++ b/aws/resource_aws_wafv2_rule_group.go @@ -120,10 +120,16 @@ func resourceAwsWafv2RuleGroupCreate(d *schema.ResourceData, meta interface{}) e Name: aws.String(d.Get("name").(string)), Scope: aws.String(d.Get("scope").(string)), Capacity: aws.Int64(int64(d.Get("capacity").(int))), - Rules: expandWafv2Rules(d.Get("rule").(*schema.Set).List()), VisibilityConfig: expandWafv2VisibilityConfig(d.Get("visibility_config").([]interface{})), } + rules, err := expandWafv2Rules(d.Get("rule").(*schema.Set).List()) + if err != nil { + return err + } + + params.Rules = rules + if v, ok := d.GetOk("description"); ok { params.Description = aws.String(v.(string)) } @@ -132,7 +138,7 @@ func resourceAwsWafv2RuleGroupCreate(d *schema.ResourceData, meta interface{}) e params.Tags = keyvaluetags.New(v).IgnoreAws().Wafv2Tags() } - err := resource.Retry(5*time.Minute, func() *resource.RetryError { + err = resource.Retry(5*time.Minute, func() *resource.RetryError { var err error resp, err = conn.CreateRuleGroup(params) if err != nil { @@ -222,15 +228,21 @@ func resourceAwsWafv2RuleGroupUpdate(d *schema.ResourceData, meta interface{}) e Name: aws.String(d.Get("name").(string)), Scope: aws.String(d.Get("scope").(string)), LockToken: aws.String(d.Get("lock_token").(string)), - Rules: expandWafv2Rules(d.Get("rule").(*schema.Set).List()), VisibilityConfig: expandWafv2VisibilityConfig(d.Get("visibility_config").([]interface{})), } + rules, err := expandWafv2Rules(d.Get("rule").(*schema.Set).List()) + if err != nil { + return err + } + + u.Rules = rules + if v, ok := d.GetOk("description"); ok { u.Description = aws.String(v.(string)) } - err := resource.Retry(5*time.Minute, func() *resource.RetryError { + err = resource.Retry(5*time.Minute, func() *resource.RetryError { _, err := conn.UpdateRuleGroup(u) if err != nil { if isAWSErr(err, wafv2.ErrCodeWAFUnavailableEntityException, "") { diff --git a/aws/resource_aws_wafv2_web_acl_logging_configuration.go b/aws/resource_aws_wafv2_web_acl_logging_configuration.go index a1285ec62c5..84ced141911 100644 --- a/aws/resource_aws_wafv2_web_acl_logging_configuration.go +++ b/aws/resource_aws_wafv2_web_acl_logging_configuration.go @@ -3,6 +3,7 @@ package aws import ( "fmt" "log" + "regexp" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/wafv2" @@ -34,11 +35,66 @@ func resourceAwsWafv2WebACLLoggingConfiguration() *schema.Resource { Description: "AWS Kinesis Firehose Delivery Stream ARNs", }, "redacted_fields": { - Type: schema.TypeSet, - Optional: true, - MaxItems: 100, - Elem: wafv2FieldToMatchBaseSchema(), - Description: "Parts of the request to exclude from logs", + // To allow this argument and its nested fields with Empty Schemas (e.g. "method") + // to be correctly interpreted, this argument must be of type List, + // otherwise, at apply-time a field configured as an empty block + // (e.g. body {}) will result in a nil redacted_fields attribute + Type: schema.TypeList, + Optional: true, + MaxItems: 100, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + // TODO: remove attributes marked as Deprecated + // as they are not supported by the WAFv2 API + // in the context of WebACL Logging Configurations + "all_query_arguments": wafv2EmptySchemaDeprecated(), + "body": wafv2EmptySchemaDeprecated(), + "method": wafv2EmptySchema(), + "query_string": wafv2EmptySchema(), + "single_header": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 40), + // The value is returned in lower case by the API. + // Trying to solve it with StateFunc and/or DiffSuppressFunc resulted in hash problem of the rule field or didn't work. + validation.StringMatch(regexp.MustCompile(`^[a-z0-9-_]+$`), "must contain only lowercase alphanumeric characters, underscores, and hyphens"), + ), + }, + }, + }, + }, + "single_query_argument": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 30), + // The value is returned in lower case by the API. + // Trying to solve it with StateFunc and/or DiffSuppressFunc resulted in hash problem of the rule field or didn't work. + validation.StringMatch(regexp.MustCompile(`^[a-z0-9-_]+$`), "must contain only lowercase alphanumeric characters, underscores, and hyphens"), + ), + }, + }, + }, + Deprecated: "Not supported by WAFv2 API", + }, + "uri_path": wafv2EmptySchema(), + }, + }, + Description: "Parts of the request to exclude from logs", + DiffSuppressFunc: suppressEquivalentRedactedFields, }, "resource_arn": { Type: schema.TypeString, @@ -51,6 +107,62 @@ func resourceAwsWafv2WebACLLoggingConfiguration() *schema.Resource { } } +// suppressEquivalentRedactedFields is required to +// handle shifts in List ordering returned from the API +func suppressEquivalentRedactedFields(k, old, new string, d *schema.ResourceData) bool { + o, n := d.GetChange("redacted_fields") + if o != nil && n != nil { + oldFields := o.([]interface{}) + newFields := n.([]interface{}) + if len(oldFields) != len(newFields) { + // account for case where the empty block {} is used as input + return !d.IsNewResource() && len(oldFields) == 0 && len(newFields) == 1 && newFields[0] == nil + } + + for _, oldField := range oldFields { + om := oldField.(map[string]interface{}) + found := false + for _, newField := range newFields { + nm := newField.(map[string]interface{}) + if len(om) != len(nm) { + continue + } + for k, newVal := range nm { + if oldVal, ok := om[k]; ok { + if k == "method" || k == "query_string" || k == "uri_path" { + if len(oldVal.([]interface{})) == len(newVal.([]interface{})) { + found = true + break + } + } else if k == "single_header" { + oldHeader := oldVal.([]interface{}) + newHeader := newVal.([]interface{}) + if len(oldHeader) > 0 && oldHeader[0] != nil { + if len(newHeader) > 0 && newHeader[0] != nil { + oldName := oldVal.([]interface{})[0].(map[string]interface{})["name"].(string) + newName := newVal.([]interface{})[0].(map[string]interface{})["name"].(string) + if oldName == newName { + found = true + break + } + } + } + } + } + } + if found { + break + } + } + if !found { + return false + } + } + return true + } + return false +} + func resourceAwsWafv2WebACLLoggingConfigurationPut(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).wafv2conn @@ -60,8 +172,12 @@ func resourceAwsWafv2WebACLLoggingConfigurationPut(d *schema.ResourceData, meta ResourceArn: aws.String(resourceArn), } - if v, ok := d.GetOk("redacted_fields"); ok && v.(*schema.Set).Len() > 0 { - config.RedactedFields = expandWafv2RedactedFields(v.(*schema.Set).List()) + if v, ok := d.GetOk("redacted_fields"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + fields, err := expandWafv2RedactedFields(v.([]interface{})) + if err != nil { + return err + } + config.RedactedFields = fields } else { config.RedactedFields = []*wafv2.FieldToMatch{} } @@ -126,18 +242,84 @@ func resourceAwsWafv2WebACLLoggingConfigurationDelete(d *schema.ResourceData, me return nil } +func expandWafv2RedactedFields(fields []interface{}) ([]*wafv2.FieldToMatch, error) { + redactedFields := make([]*wafv2.FieldToMatch, 0, len(fields)) + for _, field := range fields { + f, err := expandWafv2RedactedField(field) + if err != nil { + return nil, err + } + redactedFields = append(redactedFields, f) + } + return redactedFields, nil +} + +func expandWafv2RedactedField(field interface{}) (*wafv2.FieldToMatch, error) { + m := field.(map[string]interface{}) + + f := &wafv2.FieldToMatch{} + + // While the FieldToMatch struct allows more than 1 of its fields to be set, + // the WAFv2 API does not. In addition, in the context of Logging Configuration requests, + // the WAFv2 API only supports the following redacted fields. + // Reference: https://github.com/terraform-providers/terraform-provider-aws/issues/14244 + nFields := 0 + for _, v := range m { + if len(v.([]interface{})) > 0 { + nFields++ + } + if nFields > 1 { + return nil, fmt.Errorf(`error expanding redacted_field: only one of "method", "query_string", + "single_header", or "uri_path" is valid`) + } + } + + if v, ok := m["method"]; ok && len(v.([]interface{})) > 0 { + f.Method = &wafv2.Method{} + } else if v, ok := m["query_string"]; ok && len(v.([]interface{})) > 0 { + f.QueryString = &wafv2.QueryString{} + } else if v, ok := m["single_header"]; ok && len(v.([]interface{})) > 0 { + f.SingleHeader = expandWafv2SingleHeader(m["single_header"].([]interface{})) + } else if v, ok := m["uri_path"]; ok && len(v.([]interface{})) > 0 { + f.UriPath = &wafv2.UriPath{} + } + + return f, nil +} + func flattenWafv2RedactedFields(fields []*wafv2.FieldToMatch) []map[string]interface{} { redactedFields := make([]map[string]interface{}, 0, len(fields)) for _, field := range fields { - redactedFields = append(redactedFields, flattenWafv2FieldToMatch(field).([]interface{})[0].(map[string]interface{})) + redactedFields = append(redactedFields, flattenWafv2RedactedField(field)) } return redactedFields } -func expandWafv2RedactedFields(fields []interface{}) []*wafv2.FieldToMatch { - redactedFields := make([]*wafv2.FieldToMatch, 0, len(fields)) - for _, field := range fields { - redactedFields = append(redactedFields, expandWafv2FieldToMatch([]interface{}{field})) +func flattenWafv2RedactedField(f *wafv2.FieldToMatch) map[string]interface{} { + m := map[string]interface{}{} + + if f == nil { + return m } - return redactedFields + + // In the context of Logging Configuration requests, + // the WAFv2 API only supports the following redacted fields. + // Reference: https://github.com/terraform-providers/terraform-provider-aws/issues/14244 + if f.Method != nil { + m["method"] = make([]map[string]interface{}, 1) + } + + if f.QueryString != nil { + m["query_string"] = make([]map[string]interface{}, 1) + } + + if f.SingleHeader != nil { + m["single_header"] = flattenWafv2SingleHeader(f.SingleHeader) + } + + if f.UriPath != nil { + m["uri_path"] = make([]map[string]interface{}, 1) + } + + return m } diff --git a/aws/resource_aws_wafv2_web_acl_logging_configuration_test.go b/aws/resource_aws_wafv2_web_acl_logging_configuration_test.go index bce42080a91..fdc96d6e70c 100644 --- a/aws/resource_aws_wafv2_web_acl_logging_configuration_test.go +++ b/aws/resource_aws_wafv2_web_acl_logging_configuration_test.go @@ -40,7 +40,7 @@ func TestAccAwsWafv2WebACLLoggingConfiguration_basic(t *testing.T) { }) } -func TestAccAwsWafv2WebACLLoggingConfiguration_update(t *testing.T) { +func TestAccAwsWafv2WebACLLoggingConfiguration_updateSingleHeaderRedactedField(t *testing.T) { var v wafv2.LoggingConfiguration rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_wafv2_web_acl_logging_configuration.test" @@ -61,7 +61,7 @@ func TestAccAwsWafv2WebACLLoggingConfiguration_update(t *testing.T) { ), }, { - Config: testAccAwsWafv2WebACLLoggingConfiguration_updateTwoRedactedFields(rName), + Config: testAccAwsWafv2WebACLLoggingConfiguration_updateTwoSingleHeaderRedactedFields(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), @@ -76,13 +76,206 @@ func TestAccAwsWafv2WebACLLoggingConfiguration_update(t *testing.T) { ), }, { - Config: testAccAwsWafv2WebACLLoggingConfiguration_updateOneRedactedField(rName), + Config: testAccAwsWafv2WebACLLoggingConfiguration_updateSingleHeaderRedactedField(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "1"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + "single_header.0.name": "user-agent", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +// Reference: https://github.com/terraform-providers/terraform-provider-aws/issues/14248 +func TestAccAwsWafv2WebACLLoggingConfiguration_updateMethodRedactedField(t *testing.T) { + var v wafv2.LoggingConfiguration + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_web_acl_logging_configuration.test" + webACLResourceName := "aws_wafv2_web_acl.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2WebACLLoggingConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2WebACLLoggingConfiguration_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "0"), + ), + }, + { + Config: testAccAwsWafv2WebACLLoggingConfiguration_updateRedactedField(rName, "method"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "1"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + "method.#": "1", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +// Reference: https://github.com/terraform-providers/terraform-provider-aws/issues/14248 +func TestAccAwsWafv2WebACLLoggingConfiguration_updateQueryStringRedactedField(t *testing.T) { + var v wafv2.LoggingConfiguration + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_web_acl_logging_configuration.test" + webACLResourceName := "aws_wafv2_web_acl.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2WebACLLoggingConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2WebACLLoggingConfiguration_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "0"), + ), + }, + { + Config: testAccAwsWafv2WebACLLoggingConfiguration_updateRedactedField(rName, "query_string"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "1"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + "query_string.#": "1", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +// Reference: https://github.com/terraform-providers/terraform-provider-aws/issues/14248 +func TestAccAwsWafv2WebACLLoggingConfiguration_updateUriPathRedactedField(t *testing.T) { + var v wafv2.LoggingConfiguration + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_web_acl_logging_configuration.test" + webACLResourceName := "aws_wafv2_web_acl.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2WebACLLoggingConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2WebACLLoggingConfiguration_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "0"), + ), + }, + { + Config: testAccAwsWafv2WebACLLoggingConfiguration_updateRedactedField(rName, "uri_path"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "1"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + "uri_path.#": "1", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +// Reference: https://github.com/terraform-providers/terraform-provider-aws/issues/14248 +func TestAccAwsWafv2WebACLLoggingConfiguration_updateMultipleRedactedFields(t *testing.T) { + var v wafv2.LoggingConfiguration + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_web_acl_logging_configuration.test" + webACLResourceName := "aws_wafv2_web_acl.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2WebACLLoggingConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2WebACLLoggingConfiguration_updateRedactedField(rName, "uri_path"), Check: resource.ComposeTestCheckFunc( testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "1"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + "uri_path.#": "1", + }), + ), + }, + { + Config: testAccAwsWafv2WebACLLoggingConfiguration_updateTwoRedactedFields(rName, "uri_path", "method"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "2"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + "uri_path.#": "1", + }), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + "method.#": "1", + }), + ), + }, + { + Config: testAccAwsWafv2WebACLLoggingConfiguration_updateThreeRedactedFields(rName, "uri_path", "query_string"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "3"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + "uri_path.#": "1", + }), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + "query_string.#": "1", + }), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ "single_header.0.name": "user-agent", }), ), @@ -201,6 +394,76 @@ func TestAccAwsWafv2WebACLLoggingConfiguration_disappears(t *testing.T) { }) } +func TestAccAwsWafv2WebACLLoggingConfiguration_emptyRedactedFields(t *testing.T) { + var v wafv2.LoggingConfiguration + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_web_acl_logging_configuration.test" + webACLResourceName := "aws_wafv2_web_acl.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2WebACLLoggingConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2WebACLLoggingConfiguration_emptyRedactedField(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAwsWafv2WebACLLoggingConfiguration_updateEmptyRedactedFields(t *testing.T) { + var v wafv2.LoggingConfiguration + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_web_acl_logging_configuration.test" + webACLResourceName := "aws_wafv2_web_acl.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2WebACLLoggingConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2WebACLLoggingConfiguration_emptyRedactedField(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "0"), + ), + }, + { + Config: testAccAwsWafv2WebACLLoggingConfiguration_updateRedactedField(rName, "uri_path"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "1"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + "uri_path.#": "1", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAwsWafv2WebACLLoggingConfiguration_disappears_WebAcl(t *testing.T) { var v wafv2.LoggingConfiguration rName := acctest.RandomWithPrefix("tf-acc-test") @@ -295,8 +558,7 @@ data "aws_partition" "current" {} data "aws_caller_identity" "current" {} resource "aws_iam_role" "firehose" { - name = "%[1]s" - + name = "%[1]s" assume_role_policy = < 0 { - f.AllQueryArguments = &wafv2.AllQueryArguments{} + // While the FieldToMatch struct allows more than 1 of its fields to be set, + // the WAFv2 API does not. + // Reference: https://github.com/terraform-providers/terraform-provider-aws/issues/14248 + nFields := 0 + for _, v := range m { + if len(v.([]interface{})) > 0 { + nFields++ + } + if nFields > 1 { + return nil, fmt.Errorf(`error expanding field_to_match: only one of "all_query_arguments", "body", method", + "query_string", "single_header", "single_query_argument", or "uri_path" is valid`) + } } - if v, ok := m["body"]; ok && len(v.([]interface{})) > 0 { + if v, ok := m["all_query_arguments"]; ok && len(v.([]interface{})) > 0 { + f.AllQueryArguments = &wafv2.AllQueryArguments{} + } else if v, ok := m["body"]; ok && len(v.([]interface{})) > 0 { f.Body = &wafv2.Body{} - } - - if v, ok := m["method"]; ok && len(v.([]interface{})) > 0 { + } else if v, ok := m["method"]; ok && len(v.([]interface{})) > 0 { f.Method = &wafv2.Method{} - } - - if v, ok := m["query_string"]; ok && len(v.([]interface{})) > 0 { + } else if v, ok := m["query_string"]; ok && len(v.([]interface{})) > 0 { f.QueryString = &wafv2.QueryString{} - } - - if v, ok := m["single_header"]; ok && len(v.([]interface{})) > 0 { + } else if v, ok := m["single_header"]; ok && len(v.([]interface{})) > 0 { f.SingleHeader = expandWafv2SingleHeader(m["single_header"].([]interface{})) - } - - if v, ok := m["single_query_argument"]; ok && len(v.([]interface{})) > 0 { + } else if v, ok := m["single_query_argument"]; ok && len(v.([]interface{})) > 0 { f.SingleQueryArgument = expandWafv2SingleQueryArgument(m["single_query_argument"].([]interface{})) - } - - if v, ok := m["uri_path"]; ok && len(v.([]interface{})) > 0 { + } else if v, ok := m["uri_path"]; ok && len(v.([]interface{})) > 0 { f.UriPath = &wafv2.UriPath{} } - return f + return f, nil } func expandWafv2ForwardedIPConfig(l []interface{}) *wafv2.ForwardedIPConfig { @@ -741,90 +814,127 @@ func expandWafv2GeoMatchStatement(l []interface{}) *wafv2.GeoMatchStatement { return statement } -func expandWafv2NotStatement(l []interface{}) *wafv2.NotStatement { +func expandWafv2NotStatement(l []interface{}) (*wafv2.NotStatement, error) { if len(l) == 0 || l[0] == nil { - return nil + return nil, nil } m := l[0].(map[string]interface{}) s := m["statement"].([]interface{}) if len(s) == 0 || s[0] == nil { - return nil + return nil, nil } m = s[0].(map[string]interface{}) - return &wafv2.NotStatement{ - Statement: expandWafv2Statement(m), + statement, err := expandWafv2Statement(m) + if err != nil { + return nil, err } + return &wafv2.NotStatement{Statement: statement}, nil } -func expandWafv2OrStatement(l []interface{}) *wafv2.OrStatement { +func expandWafv2OrStatement(l []interface{}) (*wafv2.OrStatement, error) { if len(l) == 0 || l[0] == nil { - return nil + return nil, nil } m := l[0].(map[string]interface{}) - return &wafv2.OrStatement{ - Statements: expandWafv2Statements(m["statement"].([]interface{})), + s, err := expandWafv2Statements(m["statement"].([]interface{})) + if err != nil { + return nil, err } + + return &wafv2.OrStatement{Statements: s}, nil } -func expandWafv2RegexPatternSetReferenceStatement(l []interface{}) *wafv2.RegexPatternSetReferenceStatement { +func expandWafv2RegexPatternSetReferenceStatement(l []interface{}) (*wafv2.RegexPatternSetReferenceStatement, error) { if len(l) == 0 || l[0] == nil { - return nil + return nil, nil } m := l[0].(map[string]interface{}) - return &wafv2.RegexPatternSetReferenceStatement{ + s := &wafv2.RegexPatternSetReferenceStatement{ ARN: aws.String(m["arn"].(string)), - FieldToMatch: expandWafv2FieldToMatch(m["field_to_match"].([]interface{})), TextTransformations: expandWafv2TextTransformations(m["text_transformation"].(*schema.Set).List()), } + + fieldToMatch, err := expandWafv2FieldToMatch(m["field_to_match"].([]interface{})) + if err != nil { + return nil, err + } + + s.FieldToMatch = fieldToMatch + + return s, nil } -func expandWafv2SizeConstraintStatement(l []interface{}) *wafv2.SizeConstraintStatement { +func expandWafv2SizeConstraintStatement(l []interface{}) (*wafv2.SizeConstraintStatement, error) { if len(l) == 0 || l[0] == nil { - return nil + return nil, nil } m := l[0].(map[string]interface{}) - return &wafv2.SizeConstraintStatement{ + s := &wafv2.SizeConstraintStatement{ ComparisonOperator: aws.String(m["comparison_operator"].(string)), - FieldToMatch: expandWafv2FieldToMatch(m["field_to_match"].([]interface{})), Size: aws.Int64(int64(m["size"].(int))), TextTransformations: expandWafv2TextTransformations(m["text_transformation"].(*schema.Set).List()), } + + fieldToMatch, err := expandWafv2FieldToMatch(m["field_to_match"].([]interface{})) + if err != nil { + return nil, err + } + + s.FieldToMatch = fieldToMatch + + return s, nil } -func expandWafv2SqliMatchStatement(l []interface{}) *wafv2.SqliMatchStatement { +func expandWafv2SqliMatchStatement(l []interface{}) (*wafv2.SqliMatchStatement, error) { if len(l) == 0 || l[0] == nil { - return nil + return nil, nil } m := l[0].(map[string]interface{}) - return &wafv2.SqliMatchStatement{ - FieldToMatch: expandWafv2FieldToMatch(m["field_to_match"].([]interface{})), + s := &wafv2.SqliMatchStatement{ TextTransformations: expandWafv2TextTransformations(m["text_transformation"].(*schema.Set).List()), } + + fieldToMatch, err := expandWafv2FieldToMatch(m["field_to_match"].([]interface{})) + if err != nil { + return nil, err + } + + s.FieldToMatch = fieldToMatch + + return s, nil } -func expandWafv2XssMatchStatement(l []interface{}) *wafv2.XssMatchStatement { +func expandWafv2XssMatchStatement(l []interface{}) (*wafv2.XssMatchStatement, error) { if len(l) == 0 || l[0] == nil { - return nil + return nil, nil } m := l[0].(map[string]interface{}) - return &wafv2.XssMatchStatement{ - FieldToMatch: expandWafv2FieldToMatch(m["field_to_match"].([]interface{})), + s := &wafv2.XssMatchStatement{ TextTransformations: expandWafv2TextTransformations(m["text_transformation"].(*schema.Set).List()), } + + fieldToMatch, err := expandWafv2FieldToMatch(m["field_to_match"].([]interface{})) + if err != nil { + return nil, err + } + + s.FieldToMatch = fieldToMatch + + return s, nil } func flattenWafv2Rules(r []*wafv2.Rule) interface{} { diff --git a/website/docs/r/wafv2_rule_group.html.markdown b/website/docs/r/wafv2_rule_group.html.markdown index ca47b030c7e..c6cfd3208a3 100644 --- a/website/docs/r/wafv2_rule_group.html.markdown +++ b/website/docs/r/wafv2_rule_group.html.markdown @@ -307,7 +307,7 @@ Each `rule` supports the following arguments: The `action` block supports the following arguments: -~> **NOTE**: One of `allow`, `block`, or `count`, expressed as an empty configuration block `{}`, is required when specifying an `action` +~> **NOTE:** One of `allow`, `block`, or `count`, expressed as an empty configuration block `{}`, is required when specifying an `action` * `allow` - (Optional) Instructs AWS WAF to allow the web request. * `block` - (Optional) Instructs AWS WAF to block the web request. @@ -429,7 +429,8 @@ The part of a web request that you want AWS WAF to inspect. Include the single ` The `field_to_match` block supports the following arguments: -~> **NOTE**: An empty configuration block `{}` should be used when specifying `all_query_arguments`, `body`, `method`, or `query_string` attributes +~> **NOTE:** Only one of `all_query_arguments`, `body`, `method`, `query_string`, `single_header`, `single_query_argument`, or `uri_path` can be specified. +An empty configuration block `{}` should be used when specifying `all_query_arguments`, `body`, `method`, or `query_string` attributes. * `all_query_arguments` - (Optional) Inspect all query arguments. * `body` - (Optional) Inspect the request body, which immediately follows the request headers. diff --git a/website/docs/r/wafv2_web_acl.html.markdown b/website/docs/r/wafv2_web_acl.html.markdown index f3a91cb8d71..77a0d8c6a72 100644 --- a/website/docs/r/wafv2_web_acl.html.markdown +++ b/website/docs/r/wafv2_web_acl.html.markdown @@ -267,14 +267,14 @@ The following arguments are supported: The `default_action` block supports the following arguments: -~> **NOTE**: One of `allow` or `block`, expressed as an empty configuration block `{}`, is required when specifying a `default_action` +~> **NOTE:** One of `allow` or `block`, expressed as an empty configuration block `{}`, is required when specifying a `default_action` * `allow` - (Optional) Specifies that AWS WAF should allow requests by default. * `block` - (Optional) Specifies that AWS WAF should block requests by default. ### Rules -~> **NOTE**: One of `action` or `override_action` is required when specifying a rule +~> **NOTE:** One of `action` or `override_action` is required when specifying a rule Each `rule` supports the following arguments: @@ -289,7 +289,7 @@ Each `rule` supports the following arguments: The `action` block supports the following arguments: -~> **NOTE**: One of `allow`, `block`, or `count`, expressed as an empty configuration block `{}`, is required when specifying an `action` +~> **NOTE:** One of `allow`, `block`, or `count`, expressed as an empty configuration block `{}`, is required when specifying an `action` * `allow` - (Optional) Instructs AWS WAF to allow the web request. Configure as an empty block `{}`. * `block` - (Optional) Instructs AWS WAF to block the web request. Configure as an empty block `{}`. @@ -299,7 +299,7 @@ The `action` block supports the following arguments: The `override_action` block supports the following arguments: -~> **NOTE**: One of `count` or `none`, expressed as an empty configuration block `{}`, is required when specifying an `override_action` +~> **NOTE:** One of `count` or `none`, expressed as an empty configuration block `{}`, is required when specifying an `override_action` * `count` - (Optional) Override the rule action setting to count (i.e. only count matches). Configured as an empty block `{}`. * `none` - (Optional) Don't override the rule action setting. Configured as an empty block `{}`. @@ -466,7 +466,8 @@ The part of a web request that you want AWS WAF to inspect. Include the single ` The `field_to_match` block supports the following arguments: -~> **NOTE**: An empty configuration block `{}` should be used when specifying `all_query_arguments`, `body`, `method`, `query_string`, or `uri_path` attributes +~> **NOTE:** Only one of `all_query_arguments`, `body`, `method`, `query_string`, `single_header`, `single_query_argument`, or `uri_path` can be specified. +An empty configuration block `{}` should be used when specifying `all_query_arguments`, `body`, `method`, or `query_string` attributes. * `all_query_arguments` - (Optional) Inspect all query arguments. * `body` - (Optional) Inspect the request body, which immediately follows the request headers. diff --git a/website/docs/r/wafv2_web_acl_logging_configuration.html.markdown b/website/docs/r/wafv2_web_acl_logging_configuration.html.markdown index 62955499241..b2e926ef7ab 100644 --- a/website/docs/r/wafv2_web_acl_logging_configuration.html.markdown +++ b/website/docs/r/wafv2_web_acl_logging_configuration.html.markdown @@ -38,12 +38,14 @@ The following arguments are supported: The `redacted_fields` block supports the following arguments: -* `all_query_arguments` - (Optional) Redact all query arguments. -* `body` - (Optional) Redact the request body, which immediately follows the request headers. +~> **NOTE:** Only one of `method`, `query_string`, `single_header` or `uri_path` can be specified. + +* `all_query_arguments` - (Optional, **DEPRECATED**) Redact all query arguments. +* `body` - (Optional, **DEPRECATED**) Redact the request body, which immediately follows the request headers. * `method` - (Optional) Redact the HTTP method. The method indicates the type of operation that the request is asking the origin to perform. * `query_string` - (Optional) Redact the query string. This is the part of a URL that appears after a `?` character, if any. * `single_header` - (Optional) Redact a single header. See [Single Header](#single-header) below for details. -* `single_query_argument` - (Optional) Redact a single query argument. See [Single Query Argument](#single-query-argument) below for details. +* `single_query_argument` - (Optional, **DEPRECATED**) Redact a single query argument. See [Single Query Argument](#single-query-argument) below for details. * `uri_path` - (Optional) Redact the request URI path. This is the part of a web request that identifies a resource, for example, `/images/daily-ad.jpg`. ### Single Header @@ -54,7 +56,7 @@ The `single_header` block supports the following arguments: * `name` - (Optional) The name of the query header to redact. This setting must be provided as lower case characters. -### Single Query Argument +### Single Query Argument (**DEPRECATED**) Redact a single query argument. Provide the name of the query argument to redact, such as `UserName` or `SalesRegion` (provided as lowercase strings). From 541ae0d102ce163476c5d4e85f26326c1694b82a Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Thu, 3 Dec 2020 11:13:41 -0500 Subject: [PATCH 2/7] mark update field suppress func needed --- ...aws_wafv2_web_acl_logging_configuration.go | 103 +++++++++++++----- ...afv2_web_acl_logging_configuration_test.go | 2 +- 2 files changed, 74 insertions(+), 31 deletions(-) diff --git a/aws/resource_aws_wafv2_web_acl_logging_configuration.go b/aws/resource_aws_wafv2_web_acl_logging_configuration.go index 84ced141911..1b526176fa8 100644 --- a/aws/resource_aws_wafv2_web_acl_logging_configuration.go +++ b/aws/resource_aws_wafv2_web_acl_logging_configuration.go @@ -1,6 +1,7 @@ package aws import ( + "bytes" "fmt" "log" "regexp" @@ -8,6 +9,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/wafv2" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) func resourceAwsWafv2WebACLLoggingConfiguration() *schema.Resource { @@ -41,6 +43,30 @@ func resourceAwsWafv2WebACLLoggingConfiguration() *schema.Resource { // (e.g. body {}) will result in a nil redacted_fields attribute Type: schema.TypeList, Optional: true, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + o, n := d.GetChange("redacted_fields") + oList := o.([]interface{}) + nList := n.([]interface{}) + if len(oList) == 0 && len(nList) == 0 { + return true + } + if len(oList) == 0 && len(nList) != 0 { + if nList[0] == nil { + return true + } + return false + } + if len(oList) != 0 && len(nList) == 0 { + if oList[0] == nil { + return true + } + return false + } + + oldSet := schema.NewSet(redactedFieldsHash, oList) + newSet := schema.NewSet(redactedFieldsHash, nList) + return oldSet.Equal(newSet) + }, MaxItems: 100, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -173,11 +199,7 @@ func resourceAwsWafv2WebACLLoggingConfigurationPut(d *schema.ResourceData, meta } if v, ok := d.GetOk("redacted_fields"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - fields, err := expandWafv2RedactedFields(v.([]interface{})) - if err != nil { - return err - } - config.RedactedFields = fields + config.RedactedFields = expandWafv2RedactedFields(v.([]interface{})) } else { config.RedactedFields = []*wafv2.FieldToMatch{} } @@ -242,19 +264,15 @@ func resourceAwsWafv2WebACLLoggingConfigurationDelete(d *schema.ResourceData, me return nil } -func expandWafv2RedactedFields(fields []interface{}) ([]*wafv2.FieldToMatch, error) { +func expandWafv2RedactedFields(fields []interface{}) []*wafv2.FieldToMatch { redactedFields := make([]*wafv2.FieldToMatch, 0, len(fields)) for _, field := range fields { - f, err := expandWafv2RedactedField(field) - if err != nil { - return nil, err - } - redactedFields = append(redactedFields, f) + redactedFields = append(redactedFields, expandWafv2RedactedField(field)) } - return redactedFields, nil + return redactedFields } -func expandWafv2RedactedField(field interface{}) (*wafv2.FieldToMatch, error) { +func expandWafv2RedactedField(field interface{}) *wafv2.FieldToMatch { m := field.(map[string]interface{}) f := &wafv2.FieldToMatch{} @@ -263,32 +281,27 @@ func expandWafv2RedactedField(field interface{}) (*wafv2.FieldToMatch, error) { // the WAFv2 API does not. In addition, in the context of Logging Configuration requests, // the WAFv2 API only supports the following redacted fields. // Reference: https://github.com/terraform-providers/terraform-provider-aws/issues/14244 - nFields := 0 - for _, v := range m { - if len(v.([]interface{})) > 0 { - nFields++ - } - if nFields > 1 { - return nil, fmt.Errorf(`error expanding redacted_field: only one of "method", "query_string", - "single_header", or "uri_path" is valid`) - } - } - if v, ok := m["method"]; ok && len(v.([]interface{})) > 0 { f.Method = &wafv2.Method{} - } else if v, ok := m["query_string"]; ok && len(v.([]interface{})) > 0 { + } + + if v, ok := m["query_string"]; ok && len(v.([]interface{})) > 0 { f.QueryString = &wafv2.QueryString{} - } else if v, ok := m["single_header"]; ok && len(v.([]interface{})) > 0 { + } + + if v, ok := m["single_header"]; ok && len(v.([]interface{})) > 0 { f.SingleHeader = expandWafv2SingleHeader(m["single_header"].([]interface{})) - } else if v, ok := m["uri_path"]; ok && len(v.([]interface{})) > 0 { + } + + if v, ok := m["uri_path"]; ok && len(v.([]interface{})) > 0 { f.UriPath = &wafv2.UriPath{} } - return f, nil + return f } -func flattenWafv2RedactedFields(fields []*wafv2.FieldToMatch) []map[string]interface{} { - redactedFields := make([]map[string]interface{}, 0, len(fields)) +func flattenWafv2RedactedFields(fields []*wafv2.FieldToMatch) []interface{} { + redactedFields := make([]interface{}, 0, len(fields)) for _, field := range fields { redactedFields = append(redactedFields, flattenWafv2RedactedField(field)) } @@ -323,3 +336,33 @@ func flattenWafv2RedactedField(f *wafv2.FieldToMatch) map[string]interface{} { return m } + +// redactedFieldsHash takes a map[string]interface{} as input and generates a +// unique hashcode, taking into account keys defined in the resource's schema +// are present even if not explicitly configured +func redactedFieldsHash(v interface{}) int { + var buf bytes.Buffer + m, ok := v.(map[string]interface{}) + if !ok { + return 0 + } + if v, ok := m["method"].([]interface{}); ok && len(v) > 0 { + buf.WriteString("method-") + } + if v, ok := m["query_string"].([]interface{}); ok && len(v) > 0 { + buf.WriteString("query_string-") + } + if v, ok := m["uri_path"].([]interface{}); ok && len(v) > 0 { + buf.WriteString("uri_path-") + } + if v, ok := m["single_header"].([]interface{}); ok && len(v) > 0 { + sh, ok := v[0].(map[string]interface{}) + if ok { + if name, ok := sh["name"].(string); ok { + buf.WriteString(fmt.Sprintf("%s-", name)) + } + } + } + + return hashcode.String(buf.String()) +} diff --git a/aws/resource_aws_wafv2_web_acl_logging_configuration_test.go b/aws/resource_aws_wafv2_web_acl_logging_configuration_test.go index fdc96d6e70c..4c09913e8f4 100644 --- a/aws/resource_aws_wafv2_web_acl_logging_configuration_test.go +++ b/aws/resource_aws_wafv2_web_acl_logging_configuration_test.go @@ -263,7 +263,7 @@ func TestAccAwsWafv2WebACLLoggingConfiguration_updateMultipleRedactedFields(t *t ), }, { - Config: testAccAwsWafv2WebACLLoggingConfiguration_updateThreeRedactedFields(rName, "uri_path", "query_string"), + Config: testAccAwsWafv2WebACLLoggingConfiguration_updateThreeRedactedFields(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), From 82b90d44e88821d0404c8f4e59bd0dc7e1ff0a6a Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Thu, 3 Dec 2020 11:46:44 -0500 Subject: [PATCH 3/7] mark update field suppress func needed --- ...aws_wafv2_web_acl_logging_configuration.go | 116 +++++------------- 1 file changed, 31 insertions(+), 85 deletions(-) diff --git a/aws/resource_aws_wafv2_web_acl_logging_configuration.go b/aws/resource_aws_wafv2_web_acl_logging_configuration.go index 1b526176fa8..1ed6e8e0cca 100644 --- a/aws/resource_aws_wafv2_web_acl_logging_configuration.go +++ b/aws/resource_aws_wafv2_web_acl_logging_configuration.go @@ -10,6 +10,7 @@ import ( "github.com/aws/aws-sdk-go/service/wafv2" "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" ) func resourceAwsWafv2WebACLLoggingConfiguration() *schema.Resource { @@ -41,33 +42,9 @@ func resourceAwsWafv2WebACLLoggingConfiguration() *schema.Resource { // to be correctly interpreted, this argument must be of type List, // otherwise, at apply-time a field configured as an empty block // (e.g. body {}) will result in a nil redacted_fields attribute - Type: schema.TypeList, - Optional: true, - DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - o, n := d.GetChange("redacted_fields") - oList := o.([]interface{}) - nList := n.([]interface{}) - if len(oList) == 0 && len(nList) == 0 { - return true - } - if len(oList) == 0 && len(nList) != 0 { - if nList[0] == nil { - return true - } - return false - } - if len(oList) != 0 && len(nList) == 0 { - if oList[0] == nil { - return true - } - return false - } - - oldSet := schema.NewSet(redactedFieldsHash, oList) - newSet := schema.NewSet(redactedFieldsHash, nList) - return oldSet.Equal(newSet) - }, - MaxItems: 100, + Type: schema.TypeList, + Optional: true, + MaxItems: 100, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ // TODO: remove attributes marked as Deprecated @@ -111,6 +88,7 @@ func resourceAwsWafv2WebACLLoggingConfiguration() *schema.Resource { // Trying to solve it with StateFunc and/or DiffSuppressFunc resulted in hash problem of the rule field or didn't work. validation.StringMatch(regexp.MustCompile(`^[a-z0-9-_]+$`), "must contain only lowercase alphanumeric characters, underscores, and hyphens"), ), + Deprecated: "Not supported by WAFv2 API", }, }, }, @@ -119,8 +97,8 @@ func resourceAwsWafv2WebACLLoggingConfiguration() *schema.Resource { "uri_path": wafv2EmptySchema(), }, }, - Description: "Parts of the request to exclude from logs", - DiffSuppressFunc: suppressEquivalentRedactedFields, + Description: "Parts of the request to exclude from logs", + DiffSuppressFunc: suppressRedactedFieldsDiff, }, "resource_arn": { Type: schema.TypeString, @@ -133,62 +111,6 @@ func resourceAwsWafv2WebACLLoggingConfiguration() *schema.Resource { } } -// suppressEquivalentRedactedFields is required to -// handle shifts in List ordering returned from the API -func suppressEquivalentRedactedFields(k, old, new string, d *schema.ResourceData) bool { - o, n := d.GetChange("redacted_fields") - if o != nil && n != nil { - oldFields := o.([]interface{}) - newFields := n.([]interface{}) - if len(oldFields) != len(newFields) { - // account for case where the empty block {} is used as input - return !d.IsNewResource() && len(oldFields) == 0 && len(newFields) == 1 && newFields[0] == nil - } - - for _, oldField := range oldFields { - om := oldField.(map[string]interface{}) - found := false - for _, newField := range newFields { - nm := newField.(map[string]interface{}) - if len(om) != len(nm) { - continue - } - for k, newVal := range nm { - if oldVal, ok := om[k]; ok { - if k == "method" || k == "query_string" || k == "uri_path" { - if len(oldVal.([]interface{})) == len(newVal.([]interface{})) { - found = true - break - } - } else if k == "single_header" { - oldHeader := oldVal.([]interface{}) - newHeader := newVal.([]interface{}) - if len(oldHeader) > 0 && oldHeader[0] != nil { - if len(newHeader) > 0 && newHeader[0] != nil { - oldName := oldVal.([]interface{})[0].(map[string]interface{})["name"].(string) - newName := newVal.([]interface{})[0].(map[string]interface{})["name"].(string) - if oldName == newName { - found = true - break - } - } - } - } - } - } - if found { - break - } - } - if !found { - return false - } - } - return true - } - return false -} - func resourceAwsWafv2WebACLLoggingConfigurationPut(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).wafv2conn @@ -366,3 +288,27 @@ func redactedFieldsHash(v interface{}) int { return hashcode.String(buf.String()) } + +func suppressRedactedFieldsDiff(k, old, new string, d *schema.ResourceData) bool { + o, n := d.GetChange("redacted_fields") + oList := o.([]interface{}) + nList := n.([]interface{}) + + if len(oList) == 0 && len(nList) == 0 { + return true + } + + if len(oList) == 0 && len(nList) != 0 { + // account for empty block + return nList[0] == nil + } + + if len(oList) != 0 && len(nList) == 0 { + // account for empty block + return oList[0] == nil + } + + oldSet := schema.NewSet(redactedFieldsHash, oList) + newSet := schema.NewSet(redactedFieldsHash, nList) + return oldSet.Equal(newSet) +} \ No newline at end of file From 2ab6b80cedbfcab8e486ce85848a274dbfa3fbcc Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Thu, 18 Mar 2021 13:01:12 -0400 Subject: [PATCH 4/7] add additional deprecation message; update documentation; add changelog entries --- .changelog/14319.txt | 7 +++++++ aws/resource_aws_wafv2_web_acl_logging_configuration.go | 1 + .../r/wafv2_web_acl_logging_configuration.html.markdown | 6 +++--- 3 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 .changelog/14319.txt diff --git a/.changelog/14319.txt b/.changelog/14319.txt new file mode 100644 index 00000000000..71d707d9f4d --- /dev/null +++ b/.changelog/14319.txt @@ -0,0 +1,7 @@ +```release-note:bug +resource/aws_wafv2_web_acl_logging_configuration: Ensure `redacted_fields` are applied to the resource +``` + +```release-note:note +resource/aws_wafv2_web_acl_logging_configuration: The `redacted_fields` configuration block `all_query_arguments`, `body`, and `single_query_argument` arguments have been deprecated to match the WAF API documentation +``` \ No newline at end of file diff --git a/aws/resource_aws_wafv2_web_acl_logging_configuration.go b/aws/resource_aws_wafv2_web_acl_logging_configuration.go index 1ed6e8e0cca..7b196ee4a51 100644 --- a/aws/resource_aws_wafv2_web_acl_logging_configuration.go +++ b/aws/resource_aws_wafv2_web_acl_logging_configuration.go @@ -69,6 +69,7 @@ func resourceAwsWafv2WebACLLoggingConfiguration() *schema.Resource { // Trying to solve it with StateFunc and/or DiffSuppressFunc resulted in hash problem of the rule field or didn't work. validation.StringMatch(regexp.MustCompile(`^[a-z0-9-_]+$`), "must contain only lowercase alphanumeric characters, underscores, and hyphens"), ), + Deprecated: "Not supported by WAFv2 API", }, }, }, diff --git a/website/docs/r/wafv2_web_acl_logging_configuration.html.markdown b/website/docs/r/wafv2_web_acl_logging_configuration.html.markdown index b2e926ef7ab..56537095307 100644 --- a/website/docs/r/wafv2_web_acl_logging_configuration.html.markdown +++ b/website/docs/r/wafv2_web_acl_logging_configuration.html.markdown @@ -42,11 +42,11 @@ The `redacted_fields` block supports the following arguments: * `all_query_arguments` - (Optional, **DEPRECATED**) Redact all query arguments. * `body` - (Optional, **DEPRECATED**) Redact the request body, which immediately follows the request headers. -* `method` - (Optional) Redact the HTTP method. The method indicates the type of operation that the request is asking the origin to perform. -* `query_string` - (Optional) Redact the query string. This is the part of a URL that appears after a `?` character, if any. +* `method` - (Optional) Redact the HTTP method. Must be specified as an empty configuration block `{}`. The method indicates the type of operation that the request is asking the origin to perform. +* `query_string` - (Optional) Redact the query string. Must be specified as an empty configuration block `{}`. This is the part of a URL that appears after a `?` character, if any. * `single_header` - (Optional) Redact a single header. See [Single Header](#single-header) below for details. * `single_query_argument` - (Optional, **DEPRECATED**) Redact a single query argument. See [Single Query Argument](#single-query-argument) below for details. -* `uri_path` - (Optional) Redact the request URI path. This is the part of a web request that identifies a resource, for example, `/images/daily-ad.jpg`. +* `uri_path` - (Optional) Redact the request URI path. Must be specified as an empty configuration block `{}`. This is the part of a web request that identifies a resource, for example, `/images/daily-ad.jpg`. ### Single Header From 405337c1f0ae4a7cfa0d9c414ba14ada6b43a30d Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Thu, 18 Mar 2021 13:31:52 -0400 Subject: [PATCH 5/7] changes after rebase --- ...aws_wafv2_web_acl_logging_configuration.go | 10 +- ...afv2_web_acl_logging_configuration_test.go | 67 ++--- aws/wafv2_helper.go | 256 ++++++------------ 3 files changed, 120 insertions(+), 213 deletions(-) diff --git a/aws/resource_aws_wafv2_web_acl_logging_configuration.go b/aws/resource_aws_wafv2_web_acl_logging_configuration.go index 7b196ee4a51..b9b0e65f909 100644 --- a/aws/resource_aws_wafv2_web_acl_logging_configuration.go +++ b/aws/resource_aws_wafv2_web_acl_logging_configuration.go @@ -42,9 +42,9 @@ func resourceAwsWafv2WebACLLoggingConfiguration() *schema.Resource { // to be correctly interpreted, this argument must be of type List, // otherwise, at apply-time a field configured as an empty block // (e.g. body {}) will result in a nil redacted_fields attribute - Type: schema.TypeList, - Optional: true, - MaxItems: 100, + Type: schema.TypeList, + Optional: true, + MaxItems: 100, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ // TODO: remove attributes marked as Deprecated @@ -98,7 +98,7 @@ func resourceAwsWafv2WebACLLoggingConfiguration() *schema.Resource { "uri_path": wafv2EmptySchema(), }, }, - Description: "Parts of the request to exclude from logs", + Description: "Parts of the request to exclude from logs", DiffSuppressFunc: suppressRedactedFieldsDiff, }, "resource_arn": { @@ -312,4 +312,4 @@ func suppressRedactedFieldsDiff(k, old, new string, d *schema.ResourceData) bool oldSet := schema.NewSet(redactedFieldsHash, oList) newSet := schema.NewSet(redactedFieldsHash, nList) return oldSet.Equal(newSet) -} \ No newline at end of file +} diff --git a/aws/resource_aws_wafv2_web_acl_logging_configuration_test.go b/aws/resource_aws_wafv2_web_acl_logging_configuration_test.go index 4c09913e8f4..a9b16c2a7d4 100644 --- a/aws/resource_aws_wafv2_web_acl_logging_configuration_test.go +++ b/aws/resource_aws_wafv2_web_acl_logging_configuration_test.go @@ -82,7 +82,7 @@ func TestAccAwsWafv2WebACLLoggingConfiguration_updateSingleHeaderRedactedField(t resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "1"), - tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ "single_header.0.name": "user-agent", }), ), @@ -124,7 +124,7 @@ func TestAccAwsWafv2WebACLLoggingConfiguration_updateMethodRedactedField(t *test resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "1"), - tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ "method.#": "1", }), ), @@ -166,7 +166,7 @@ func TestAccAwsWafv2WebACLLoggingConfiguration_updateQueryStringRedactedField(t resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "1"), - tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ "query_string.#": "1", }), ), @@ -208,7 +208,7 @@ func TestAccAwsWafv2WebACLLoggingConfiguration_updateUriPathRedactedField(t *tes resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "1"), - tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ "uri_path.#": "1", }), ), @@ -242,22 +242,21 @@ func TestAccAwsWafv2WebACLLoggingConfiguration_updateMultipleRedactedFields(t *t resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "1"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ - tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ "uri_path.#": "1", }), ), }, { - Config: testAccAwsWafv2WebACLLoggingConfiguration_updateTwoRedactedFields(rName, "uri_path", "method"), + Config: testAccAwsWafv2WebACLLoggingConfiguration_updateTwoRedactedFields(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "2"), - tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ "uri_path.#": "1", }), - tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ "method.#": "1", }), ), @@ -269,13 +268,13 @@ func TestAccAwsWafv2WebACLLoggingConfiguration_updateMultipleRedactedFields(t *t resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "3"), - tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ "uri_path.#": "1", }), - tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ "query_string.#": "1", }), - tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ "single_header.0.name": "user-agent", }), ), @@ -450,7 +449,7 @@ func TestAccAwsWafv2WebACLLoggingConfiguration_updateEmptyRedactedFields(t *test resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "1"), - tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ "uri_path.#": "1", }), ), @@ -558,7 +557,8 @@ data "aws_partition" "current" {} data "aws_caller_identity" "current" {} resource "aws_iam_role" "firehose" { - name = "%[1]s" + name = "%[1]s" + assume_role_policy = < 0 { - nFields++ - } - if nFields > 1 { - return nil, fmt.Errorf(`error expanding field_to_match: only one of "all_query_arguments", "body", method", - "query_string", "single_header", "single_query_argument", or "uri_path" is valid`) - } - } - if v, ok := m["all_query_arguments"]; ok && len(v.([]interface{})) > 0 { f.AllQueryArguments = &wafv2.AllQueryArguments{} - } else if v, ok := m["body"]; ok && len(v.([]interface{})) > 0 { + } + + if v, ok := m["body"]; ok && len(v.([]interface{})) > 0 { f.Body = &wafv2.Body{} - } else if v, ok := m["method"]; ok && len(v.([]interface{})) > 0 { + } + + if v, ok := m["method"]; ok && len(v.([]interface{})) > 0 { f.Method = &wafv2.Method{} - } else if v, ok := m["query_string"]; ok && len(v.([]interface{})) > 0 { + } + + if v, ok := m["query_string"]; ok && len(v.([]interface{})) > 0 { f.QueryString = &wafv2.QueryString{} - } else if v, ok := m["single_header"]; ok && len(v.([]interface{})) > 0 { + } + + if v, ok := m["single_header"]; ok && len(v.([]interface{})) > 0 { f.SingleHeader = expandWafv2SingleHeader(m["single_header"].([]interface{})) - } else if v, ok := m["single_query_argument"]; ok && len(v.([]interface{})) > 0 { + } + + if v, ok := m["single_query_argument"]; ok && len(v.([]interface{})) > 0 { f.SingleQueryArgument = expandWafv2SingleQueryArgument(m["single_query_argument"].([]interface{})) - } else if v, ok := m["uri_path"]; ok && len(v.([]interface{})) > 0 { + } + + if v, ok := m["uri_path"]; ok && len(v.([]interface{})) > 0 { f.UriPath = &wafv2.UriPath{} } - return f, nil + return f } func expandWafv2ForwardedIPConfig(l []interface{}) *wafv2.ForwardedIPConfig { @@ -814,127 +753,90 @@ func expandWafv2GeoMatchStatement(l []interface{}) *wafv2.GeoMatchStatement { return statement } -func expandWafv2NotStatement(l []interface{}) (*wafv2.NotStatement, error) { +func expandWafv2NotStatement(l []interface{}) *wafv2.NotStatement { if len(l) == 0 || l[0] == nil { - return nil, nil + return nil } m := l[0].(map[string]interface{}) s := m["statement"].([]interface{}) if len(s) == 0 || s[0] == nil { - return nil, nil + return nil } m = s[0].(map[string]interface{}) - statement, err := expandWafv2Statement(m) - if err != nil { - return nil, err + return &wafv2.NotStatement{ + Statement: expandWafv2Statement(m), } - return &wafv2.NotStatement{Statement: statement}, nil } -func expandWafv2OrStatement(l []interface{}) (*wafv2.OrStatement, error) { +func expandWafv2OrStatement(l []interface{}) *wafv2.OrStatement { if len(l) == 0 || l[0] == nil { - return nil, nil + return nil } m := l[0].(map[string]interface{}) - s, err := expandWafv2Statements(m["statement"].([]interface{})) - if err != nil { - return nil, err + return &wafv2.OrStatement{ + Statements: expandWafv2Statements(m["statement"].([]interface{})), } - - return &wafv2.OrStatement{Statements: s}, nil } -func expandWafv2RegexPatternSetReferenceStatement(l []interface{}) (*wafv2.RegexPatternSetReferenceStatement, error) { +func expandWafv2RegexPatternSetReferenceStatement(l []interface{}) *wafv2.RegexPatternSetReferenceStatement { if len(l) == 0 || l[0] == nil { - return nil, nil + return nil } m := l[0].(map[string]interface{}) - s := &wafv2.RegexPatternSetReferenceStatement{ + return &wafv2.RegexPatternSetReferenceStatement{ ARN: aws.String(m["arn"].(string)), + FieldToMatch: expandWafv2FieldToMatch(m["field_to_match"].([]interface{})), TextTransformations: expandWafv2TextTransformations(m["text_transformation"].(*schema.Set).List()), } - - fieldToMatch, err := expandWafv2FieldToMatch(m["field_to_match"].([]interface{})) - if err != nil { - return nil, err - } - - s.FieldToMatch = fieldToMatch - - return s, nil } -func expandWafv2SizeConstraintStatement(l []interface{}) (*wafv2.SizeConstraintStatement, error) { +func expandWafv2SizeConstraintStatement(l []interface{}) *wafv2.SizeConstraintStatement { if len(l) == 0 || l[0] == nil { - return nil, nil + return nil } m := l[0].(map[string]interface{}) - s := &wafv2.SizeConstraintStatement{ + return &wafv2.SizeConstraintStatement{ ComparisonOperator: aws.String(m["comparison_operator"].(string)), + FieldToMatch: expandWafv2FieldToMatch(m["field_to_match"].([]interface{})), Size: aws.Int64(int64(m["size"].(int))), TextTransformations: expandWafv2TextTransformations(m["text_transformation"].(*schema.Set).List()), } - - fieldToMatch, err := expandWafv2FieldToMatch(m["field_to_match"].([]interface{})) - if err != nil { - return nil, err - } - - s.FieldToMatch = fieldToMatch - - return s, nil } -func expandWafv2SqliMatchStatement(l []interface{}) (*wafv2.SqliMatchStatement, error) { +func expandWafv2SqliMatchStatement(l []interface{}) *wafv2.SqliMatchStatement { if len(l) == 0 || l[0] == nil { - return nil, nil + return nil } m := l[0].(map[string]interface{}) - s := &wafv2.SqliMatchStatement{ + return &wafv2.SqliMatchStatement{ + FieldToMatch: expandWafv2FieldToMatch(m["field_to_match"].([]interface{})), TextTransformations: expandWafv2TextTransformations(m["text_transformation"].(*schema.Set).List()), } - - fieldToMatch, err := expandWafv2FieldToMatch(m["field_to_match"].([]interface{})) - if err != nil { - return nil, err - } - - s.FieldToMatch = fieldToMatch - - return s, nil } -func expandWafv2XssMatchStatement(l []interface{}) (*wafv2.XssMatchStatement, error) { +func expandWafv2XssMatchStatement(l []interface{}) *wafv2.XssMatchStatement { if len(l) == 0 || l[0] == nil { - return nil, nil + return nil } m := l[0].(map[string]interface{}) - s := &wafv2.XssMatchStatement{ + return &wafv2.XssMatchStatement{ + FieldToMatch: expandWafv2FieldToMatch(m["field_to_match"].([]interface{})), TextTransformations: expandWafv2TextTransformations(m["text_transformation"].(*schema.Set).List()), } - - fieldToMatch, err := expandWafv2FieldToMatch(m["field_to_match"].([]interface{})) - if err != nil { - return nil, err - } - - s.FieldToMatch = fieldToMatch - - return s, nil } func flattenWafv2Rules(r []*wafv2.Rule) interface{} { From 68ef691e55fb1f9fe12c10bec01833dc29f49930 Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Thu, 18 Mar 2021 13:33:22 -0400 Subject: [PATCH 6/7] revert unneeded changes --- aws/resource_aws_wafv2_rule_group.go | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/aws/resource_aws_wafv2_rule_group.go b/aws/resource_aws_wafv2_rule_group.go index bea165b1b04..b523161891a 100644 --- a/aws/resource_aws_wafv2_rule_group.go +++ b/aws/resource_aws_wafv2_rule_group.go @@ -120,16 +120,10 @@ func resourceAwsWafv2RuleGroupCreate(d *schema.ResourceData, meta interface{}) e Name: aws.String(d.Get("name").(string)), Scope: aws.String(d.Get("scope").(string)), Capacity: aws.Int64(int64(d.Get("capacity").(int))), + Rules: expandWafv2Rules(d.Get("rule").(*schema.Set).List()), VisibilityConfig: expandWafv2VisibilityConfig(d.Get("visibility_config").([]interface{})), } - rules, err := expandWafv2Rules(d.Get("rule").(*schema.Set).List()) - if err != nil { - return err - } - - params.Rules = rules - if v, ok := d.GetOk("description"); ok { params.Description = aws.String(v.(string)) } @@ -138,7 +132,7 @@ func resourceAwsWafv2RuleGroupCreate(d *schema.ResourceData, meta interface{}) e params.Tags = keyvaluetags.New(v).IgnoreAws().Wafv2Tags() } - err = resource.Retry(5*time.Minute, func() *resource.RetryError { + err := resource.Retry(5*time.Minute, func() *resource.RetryError { var err error resp, err = conn.CreateRuleGroup(params) if err != nil { @@ -228,21 +222,15 @@ func resourceAwsWafv2RuleGroupUpdate(d *schema.ResourceData, meta interface{}) e Name: aws.String(d.Get("name").(string)), Scope: aws.String(d.Get("scope").(string)), LockToken: aws.String(d.Get("lock_token").(string)), + Rules: expandWafv2Rules(d.Get("rule").(*schema.Set).List()), VisibilityConfig: expandWafv2VisibilityConfig(d.Get("visibility_config").([]interface{})), } - rules, err := expandWafv2Rules(d.Get("rule").(*schema.Set).List()) - if err != nil { - return err - } - - u.Rules = rules - if v, ok := d.GetOk("description"); ok { u.Description = aws.String(v.(string)) } - err = resource.Retry(5*time.Minute, func() *resource.RetryError { + err := resource.Retry(5*time.Minute, func() *resource.RetryError { _, err := conn.UpdateRuleGroup(u) if err != nil { if isAWSErr(err, wafv2.ErrCodeWAFUnavailableEntityException, "") { From 917f3fe41b8318b9f55006e5d36764fba481ffef Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Thu, 18 Mar 2021 13:38:38 -0400 Subject: [PATCH 7/7] website linting --- website/docs/r/wafv2_rule_group.html.markdown | 2 +- website/docs/r/wafv2_web_acl.html.markdown | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/r/wafv2_rule_group.html.markdown b/website/docs/r/wafv2_rule_group.html.markdown index c6cfd3208a3..b10a5f7b002 100644 --- a/website/docs/r/wafv2_rule_group.html.markdown +++ b/website/docs/r/wafv2_rule_group.html.markdown @@ -429,7 +429,7 @@ The part of a web request that you want AWS WAF to inspect. Include the single ` The `field_to_match` block supports the following arguments: -~> **NOTE:** Only one of `all_query_arguments`, `body`, `method`, `query_string`, `single_header`, `single_query_argument`, or `uri_path` can be specified. +~> **NOTE:** Only one of `all_query_arguments`, `body`, `method`, `query_string`, `single_header`, `single_query_argument`, or `uri_path` can be specified. An empty configuration block `{}` should be used when specifying `all_query_arguments`, `body`, `method`, or `query_string` attributes. * `all_query_arguments` - (Optional) Inspect all query arguments. diff --git a/website/docs/r/wafv2_web_acl.html.markdown b/website/docs/r/wafv2_web_acl.html.markdown index 77a0d8c6a72..2ce8e49de23 100644 --- a/website/docs/r/wafv2_web_acl.html.markdown +++ b/website/docs/r/wafv2_web_acl.html.markdown @@ -466,7 +466,7 @@ The part of a web request that you want AWS WAF to inspect. Include the single ` The `field_to_match` block supports the following arguments: -~> **NOTE:** Only one of `all_query_arguments`, `body`, `method`, `query_string`, `single_header`, `single_query_argument`, or `uri_path` can be specified. +~> **NOTE:** Only one of `all_query_arguments`, `body`, `method`, `query_string`, `single_header`, `single_query_argument`, or `uri_path` can be specified. An empty configuration block `{}` should be used when specifying `all_query_arguments`, `body`, `method`, or `query_string` attributes. * `all_query_arguments` - (Optional) Inspect all query arguments.