From 4543df7e751d701c7f1002f7bda864225502beb9 Mon Sep 17 00:00:00 2001 From: Scott Westover Date: Sat, 27 Aug 2022 00:27:21 -0400 Subject: [PATCH 01/52] added oversize handling to wafv2 Signed-off-by: Scott Westover --- internal/service/wafv2/flex.go | 28 +++++++- internal/service/wafv2/rule_group_test.go | 70 +++++++++++++++++++ internal/service/wafv2/schemas.go | 19 ++++- website/docs/r/wafv2_rule_group.html.markdown | 14 +++- website/docs/r/wafv2_web_acl.html.markdown | 12 +++- 5 files changed, 136 insertions(+), 7 deletions(-) diff --git a/internal/service/wafv2/flex.go b/internal/service/wafv2/flex.go index 95b3ebf54b0..1b061fc90d6 100644 --- a/internal/service/wafv2/flex.go +++ b/internal/service/wafv2/flex.go @@ -392,7 +392,7 @@ func expandFieldToMatch(l []interface{}) *wafv2.FieldToMatch { } if v, ok := m["body"]; ok && len(v.([]interface{})) > 0 { - f.Body = &wafv2.Body{} + f.Body = expandBody(m["body"].([]interface{})) } if v, ok := m["method"]; ok && len(v.([]interface{})) > 0 { @@ -634,6 +634,18 @@ func expandXSSMatchStatement(l []interface{}) *wafv2.XssMatchStatement { } } +func expandBody(l []interface{}) *wafv2.Body { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + return &wafv2.Body{ + OversizeHandling: aws.String(m["oversize_handling"].(string)), + } +} + func flattenRules(r []*wafv2.Rule) interface{} { out := make([]map[string]interface{}, len(r)) for i, rule := range r { @@ -925,7 +937,7 @@ func flattenFieldToMatch(f *wafv2.FieldToMatch) interface{} { } if f.Body != nil { - m["body"] = make([]map[string]interface{}, 1) + m["body"] = flattenBody(f.Body) } if f.Method != nil { @@ -1144,3 +1156,15 @@ func flattenVisibilityConfig(config *wafv2.VisibilityConfig) interface{} { return []interface{}{m} } + +func flattenBody(s *wafv2.Body) interface{} { + if s == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "oversize_handling": aws.StringValue(s.OversizeHandling), + } + + return []interface{}{m} +} diff --git a/internal/service/wafv2/rule_group_test.go b/internal/service/wafv2/rule_group_test.go index ac18f1ce855..2818030a924 100644 --- a/internal/service/wafv2/rule_group_test.go +++ b/internal/service/wafv2/rule_group_test.go @@ -407,6 +407,27 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { }), ), }, + { + Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchBodyWithOversizeHandling(ruleGroupName, "CONTINUE"), + Check: resource.ComposeTestCheckFunc( + testAccCheckRuleGroupExists(resourceName, &v), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "statement.#": "1", + "statement.0.byte_match_statement.#": "1", + "statement.0.byte_match_statement.0.field_to_match.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.body.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.body.0.oversize_handling": "CONTINUE", + "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", + }), + ), + }, { Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchMethod(ruleGroupName), Check: resource.ComposeTestCheckFunc( @@ -2434,6 +2455,55 @@ resource "aws_wafv2_rule_group" "test" { `, name) } +func testAccRuleGroupConfig_byteMatchStatementFieldToMatchBodyWithOversizeHandling(name string, overSizeHandling string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 15 + name = "%[1]s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + byte_match_statement { + positional_constraint = "CONTAINS" + search_string = "word" + + field_to_match { + body { + oversize_handling = "%[2]s" + } + } + + text_transformation { + priority = 1 + type = "NONE" + } + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-rule-metric-name" + sampled_requests_enabled = false + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-metric-name" + sampled_requests_enabled = false + } +} +`, name, overSizeHandling) +} + func testAccRuleGroupConfig_byteMatchStatementFieldToMatchMethod(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { diff --git a/internal/service/wafv2/schemas.go b/internal/service/wafv2/schemas.go index 72ea6d290df..9e3fb9b1c8b 100644 --- a/internal/service/wafv2/schemas.go +++ b/internal/service/wafv2/schemas.go @@ -333,7 +333,7 @@ func fieldToMatchBaseSchema() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ "all_query_arguments": emptySchema(), - "body": emptySchema(), + "body": bodySchema(), "method": emptySchema(), "query_string": emptySchema(), "single_header": { @@ -617,3 +617,20 @@ func customResponseBodySchema() *schema.Schema { }, } } + +func bodySchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "oversize_handling": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(wafv2.OversizeHandling_Values(), false), + }, + }, + }, + } +} diff --git a/website/docs/r/wafv2_rule_group.html.markdown b/website/docs/r/wafv2_rule_group.html.markdown index 23b1c3d203f..c6e4892e549 100644 --- a/website/docs/r/wafv2_rule_group.html.markdown +++ b/website/docs/r/wafv2_rule_group.html.markdown @@ -145,7 +145,9 @@ resource "aws_wafv2_rule_group" "example" { sqli_match_statement { field_to_match { - body {} + body { + oversize_handling = "MATCH" + } } text_transformation { @@ -496,7 +498,7 @@ The `field_to_match` block supports the following arguments: 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. +* `body` - (Optional) Inspect the request body, which immediately follows the request headers. See [Body](#body) below for details. * `method` - (Optional) Inspect the HTTP method. The method indicates the type of operation that the request is asking the origin to perform. * `query_string` - (Optional) Inspect the query string. This is the part of a URL that appears after a `?` character, if any. * `single_header` - (Optional) Inspect a single header. See [Single Header](#single-header) below for details. @@ -524,6 +526,14 @@ The `ip_set_forwarded_ip_config` block supports the following arguments: * `header_name` - (Required) - The name of the HTTP header to use for the IP address. * `position` - (Required) - The position in the header to search for the IP address. Valid values include: `FIRST`, `LAST`, or `ANY`. If `ANY` is specified and the header contains more than 10 IP addresses, AWS WAFv2 inspects the last 10. +### Body + +Inspect the request body, which immediately follows the request headers. + +The `body` block supports the following arguments: + +* `oversize_handling` - (Required) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. + ### Single Header Inspect a single header. Provide the name of the header to inspect, for example, `User-Agent` or `Referer` (provided as lowercase strings). diff --git a/website/docs/r/wafv2_web_acl.html.markdown b/website/docs/r/wafv2_web_acl.html.markdown index 805dae3d8ba..9cebc11dc18 100644 --- a/website/docs/r/wafv2_web_acl.html.markdown +++ b/website/docs/r/wafv2_web_acl.html.markdown @@ -385,7 +385,7 @@ The `statement` block supports the following arguments: * `byte_match_statement` - (Optional) Rule statement that defines a string match search for AWS WAF to apply to web requests. See [Byte Match Statement](#byte-match-statement) below for details. * `geo_match_statement` - (Optional) Rule statement used to identify web requests based on country of origin. See [GEO Match Statement](#geo-match-statement) below for details. * `ip_set_reference_statement` - (Optional) Rule statement used to detect web requests coming from particular IP addresses or address ranges. See [IP Set Reference Statement](#ip-set-reference-statement) below for details. -* `label_match_statement` - (Optional) Rule statement that defines a string match search against labels that have been added to the web request by rules that have already run in the web ACL. See [Label Match Statement](#label-match-statement) below for details. +* `label_match_statement` - (Optional) Rule statement that defines a string match search against labels that have been added to the web request by rules that have already run in the web ACL. See [Label Match Statement](#label-match-statement) below for details. * `managed_rule_group_statement` - (Optional) Rule statement used to run the rules that are defined in a managed rule group. This statement can not be nested. See [Managed Rule Group Statement](#managed-rule-group-statement) below for details. * `not_statement` - (Optional) Logical rule statement used to negate the results of another rule statement. See [NOT Statement](#not-statement) below for details. * `or_statement` - (Optional) Logical rule statement used to combine other rule statements with OR logic. See [OR Statement](#or-statement) below for details. @@ -549,7 +549,7 @@ The `field_to_match` block supports the following arguments: 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. +* `body` - (Optional) Inspect the request body, which immediately follows the request headers. See [Body](#body) below for details. * `method` - (Optional) Inspect the HTTP method. The method indicates the type of operation that the request is asking the origin to perform. * `query_string` - (Optional) Inspect the query string. This is the part of a URL that appears after a `?` character, if any. * `single_header` - (Optional) Inspect a single header. See [Single Header](#single-header) below for details. @@ -577,6 +577,14 @@ The `ip_set_forwarded_ip_config` block supports the following arguments: * `header_name` - (Required) - Name of the HTTP header to use for the IP address. * `position` - (Required) - Position in the header to search for the IP address. Valid values include: `FIRST`, `LAST`, or `ANY`. If `ANY` is specified and the header contains more than 10 IP addresses, AWS WAFv2 inspects the last 10. +### Body + +Inspect the request body, which immediately follows the request headers. + +The `body` block supports the following arguments: + +* `oversize_handling` - (Required) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. + ### Single Header Inspect a single header. Provide the name of the header to inspect, for example, `User-Agent` or `Referer` (provided as lowercase strings). From 2e605f763d10f730382d8d398732779c18f4337f Mon Sep 17 00:00:00 2001 From: Scott Westover Date: Sat, 27 Aug 2022 00:37:07 -0400 Subject: [PATCH 02/52] [GH-25832] added: changelog for pull request Signed-off-by: Scott Westover --- .changelog/26506.txt | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .changelog/26506.txt diff --git a/.changelog/26506.txt b/.changelog/26506.txt new file mode 100644 index 00000000000..bd218f79f41 --- /dev/null +++ b/.changelog/26506.txt @@ -0,0 +1,4 @@ +```release-note:enhancement +resource/aws_wafv2_web_acl: Add `oversize_handling` attribute as part of `body` attribute. +resource/aws_wafv2_rule_group: Add `oversize_handling` attribute as part of `body` attribute. +``` From c901dc24b95a10e0acd46dd6abb1d36f3751b296 Mon Sep 17 00:00:00 2001 From: Scott Westover Date: Sun, 28 Aug 2022 14:56:39 -0400 Subject: [PATCH 03/52] added: support for wafv2 cookie match type Signed-off-by: Scott Westover --- internal/service/wafv2/flex.go | 117 ++++++++++++++ internal/service/wafv2/rule_group_test.go | 186 ++++++++++++++++++++++ internal/service/wafv2/schemas.go | 90 +++++++++++ 3 files changed, 393 insertions(+) diff --git a/internal/service/wafv2/flex.go b/internal/service/wafv2/flex.go index 1b061fc90d6..bafee555640 100644 --- a/internal/service/wafv2/flex.go +++ b/internal/service/wafv2/flex.go @@ -395,6 +395,10 @@ func expandFieldToMatch(l []interface{}) *wafv2.FieldToMatch { f.Body = expandBody(m["body"].([]interface{})) } + if v, ok := m["cookies"]; ok && len(v.([]interface{})) > 0 { + f.Cookies = expandCookies(m["cookies"].([]interface{})) + } + if v, ok := m["method"]; ok && len(v.([]interface{})) > 0 { f.Method = &wafv2.Method{} } @@ -646,6 +650,43 @@ func expandBody(l []interface{}) *wafv2.Body { } } +func expandCookies(l []interface{}) *wafv2.Cookies { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + return &wafv2.Cookies{ + MatchPattern: expandCookieMatchPattern(m["match_pattern"].([]interface{})), + MatchScope: aws.String(m["match_scope"].(string)), + OversizeHandling: aws.String(m["oversize_handling"].(string)), + } +} + +func expandCookieMatchPattern(l []interface{}) *wafv2.CookieMatchPattern { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + f := &wafv2.CookieMatchPattern{} + + if v, ok := m["all_match_pattern"]; ok && len(v.([]interface{})) > 0 { + f.All = &wafv2.All{} + } + + if v, ok := m["included_cookies_match_pattern"]; ok && len(v.([]interface{})) > 0 { + f.IncludedCookies = flex.ExpandStringList(m["included_cookies_match_pattern"].([]interface{})) + } + + if v, ok := m["excluded_cookies_match_pattern"]; ok && len(v.([]interface{})) > 0 { + f.ExcludedCookies = flex.ExpandStringList(m["excluded_cookies_match_pattern"].([]interface{})) + } + + return f +} + func flattenRules(r []*wafv2.Rule) interface{} { out := make([]map[string]interface{}, len(r)) for i, rule := range r { @@ -940,6 +981,10 @@ func flattenFieldToMatch(f *wafv2.FieldToMatch) interface{} { m["body"] = flattenBody(f.Body) } + if f.Cookies != nil { + m["cookies"] = flattenCookies(f.Cookies) + } + if f.Method != nil { m["method"] = make([]map[string]interface{}, 1) } @@ -1168,3 +1213,75 @@ func flattenBody(s *wafv2.Body) interface{} { return []interface{}{m} } + +func flattenCookies(s *wafv2.Cookies) interface{} { + if s == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "match_scope": aws.StringValue(s.MatchScope), + "match_pattern": flattenCookieMatchPattern(s.MatchPattern), + "oversize_handling": aws.StringValue(s.OversizeHandling), + } + + return []interface{}{m} +} + +func flattenCookieMatchPattern(s *wafv2.CookieMatchPattern) interface{} { + if s == nil { + return []interface{}{} + } + + m := map[string]interface{}{} + + if s.All != nil { + m["all_match_pattern"] = flattenAllMatchPattern(s.All) + } + + if s.ExcludedCookies != nil { + m["included_cookies_match_pattern"] = flattenExcludedCookiesMatchPattern(s.ExcludedCookies) + } + + if s.IncludedCookies != nil { + m["included_cookies_match_pattern"] = flattenIncludedCookiesMatchPattern(s.IncludedCookies) + } + + return []interface{}{m} +} + +func flattenAllMatchPattern(s *wafv2.All) interface{} { + if s == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "all": make([]map[string]interface{}, 1), + } + + return []interface{}{m} +} + +func flattenExcludedCookiesMatchPattern(s []*string) interface{} { + if s == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "excluded_cookies": flex.FlattenStringList(s), + } + + return []interface{}{m} +} + +func flattenIncludedCookiesMatchPattern(s []*string) interface{} { + if s == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "included_cookies": flex.FlattenStringList(s), + } + + return []interface{}{m} +} diff --git a/internal/service/wafv2/rule_group_test.go b/internal/service/wafv2/rule_group_test.go index 2818030a924..9ae92055480 100644 --- a/internal/service/wafv2/rule_group_test.go +++ b/internal/service/wafv2/rule_group_test.go @@ -356,6 +356,54 @@ func TestAccWAFV2RuleGroup_byteMatchStatement(t *testing.T) { }) } +func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch2(t *testing.T) { + var v wafv2.RuleGroup + ruleGroupName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_wafv2_rule_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); testAccPreCheckScopeRegional(t) }, + ErrorCheck: acctest.ErrorCheck(t, wafv2.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckRuleGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesMatchPatternIncludedCookies(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRuleGroupExists(resourceName, &v), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "statement.#": "1", + "statement.0.byte_match_statement.#": "1", + "statement.0.byte_match_statement.0.field_to_match.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.oversize_handling": "MATCH", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_scope": "ALL", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.included_cookies_match_pattern.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.included_cookies_match_pattern.0.included_cookies": "session", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.excluded_cookies_match_pattern.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccRuleGroupImportStateIdFunc(resourceName), + }, + }, + }) +} + func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { var v wafv2.RuleGroup ruleGroupName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -428,6 +476,34 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { }), ), }, + { + Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesMatchPatternAll(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRuleGroupExists(resourceName, &v), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "statement.#": "1", + "statement.0.byte_match_statement.#": "1", + "statement.0.byte_match_statement.0.field_to_match.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.oversize_handling": "MATCH", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_scope": "ALL", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.all_match_pattern.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.all_match_pattern.0.all.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.included_cookies_match_pattern.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.excluded_cookies_match_pattern.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", + }), + ), + }, { Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchMethod(ruleGroupName), Check: resource.ComposeTestCheckFunc( @@ -2504,6 +2580,116 @@ resource "aws_wafv2_rule_group" "test" { `, name, overSizeHandling) } +func testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesMatchPatternAll(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 15 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + byte_match_statement { + positional_constraint = "CONTAINS" + search_string = "word" + + field_to_match { + cookies { + match_scope = "ALL" + match_pattern { + all_match_pattern { + all {} + } + } + oversize_handling = "MATCH" + } + } + + text_transformation { + priority = 1 + type = "NONE" + } + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-rule-metric-name" + sampled_requests_enabled = false + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-metric-name" + sampled_requests_enabled = false + } +} +`, name) +} + +func testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesMatchPatternIncludedCookies(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 15 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + byte_match_statement { + positional_constraint = "CONTAINS" + search_string = "word" + + field_to_match { + cookies { + match_scope = "ALL" + match_pattern { + included_cookies_match_pattern { + included_cookies ["session"] + } + } + oversize_handling = "MATCH" + } + } + + text_transformation { + priority = 1 + type = "NONE" + } + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-rule-metric-name" + sampled_requests_enabled = false + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-metric-name" + sampled_requests_enabled = false + } +} +`, name) +} + func testAccRuleGroupConfig_byteMatchStatementFieldToMatchMethod(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { diff --git a/internal/service/wafv2/schemas.go b/internal/service/wafv2/schemas.go index 9e3fb9b1c8b..5909c725068 100644 --- a/internal/service/wafv2/schemas.go +++ b/internal/service/wafv2/schemas.go @@ -334,6 +334,7 @@ func fieldToMatchBaseSchema() *schema.Resource { Schema: map[string]*schema.Schema{ "all_query_arguments": emptySchema(), "body": bodySchema(), + "cookies": cookiesSchema(), "method": emptySchema(), "query_string": emptySchema(), "single_header": { @@ -634,3 +635,92 @@ func bodySchema() *schema.Schema { }, } } + +func cookiesSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "match_scope": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(wafv2.MapMatchScope_Values(), false), + }, + "match_pattern": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + MinItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "all_match_pattern": allMatchPatternSchema(), + "included_cookies_match_pattern": includedCookiesMatchPatternSchema(), + "excluded_cookies_match_pattern": excludedCookiesMatchPatternSchema(), + }, + }, + }, + "oversize_handling": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(wafv2.OversizeHandling_Values(), false), + }, + }, + }, + } +} + +func allMatchPatternSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "all": emptySchema(), + }, + }, + } +} + +func cookiesMatchPatternBaseSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 199, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 60), + validation.StringMatch(regexp.MustCompile(`.*\S.*`), ""), + ), + }, + } +} + +func includedCookiesMatchPatternSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "included_cookies": cookiesMatchPatternBaseSchema(), + }, + }, + } +} + +func excludedCookiesMatchPatternSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "excluded_cookies": cookiesMatchPatternBaseSchema(), + }, + }, + } +} From b38e2ea9119e1ca79df0d2544e7136f6f5838ee2 Mon Sep 17 00:00:00 2001 From: Scott Westover Date: Sun, 28 Aug 2022 20:04:59 -0400 Subject: [PATCH 04/52] added: support for cookies attribute to wafv2 web acl Added support for the `cookies` attribute in the `field_to_match` that is available under the wafv2 rule group and acl. Relates #25545 Relates #25832 Signed-off-by: Scott Westover --- .changelog/26506.txt | 2 + internal/service/wafv2/flex.go | 52 +------ internal/service/wafv2/rule_group_test.go | 175 ++++++++++------------ internal/service/wafv2/schemas.go | 50 +------ 4 files changed, 98 insertions(+), 181 deletions(-) diff --git a/.changelog/26506.txt b/.changelog/26506.txt index bd218f79f41..19f3695dca4 100644 --- a/.changelog/26506.txt +++ b/.changelog/26506.txt @@ -1,4 +1,6 @@ ```release-note:enhancement resource/aws_wafv2_web_acl: Add `oversize_handling` attribute as part of `body` attribute. resource/aws_wafv2_rule_group: Add `oversize_handling` attribute as part of `body` attribute. +resource/aws_wafv2_web_acl: Add support for `cookies` attribute as part of `field_to_match` attribute. +resource/aws_wafv2_rule_group: Add support for `cookies` attribute as part of `field_to_match` attribute. ``` diff --git a/internal/service/wafv2/flex.go b/internal/service/wafv2/flex.go index bafee555640..a776da51134 100644 --- a/internal/service/wafv2/flex.go +++ b/internal/service/wafv2/flex.go @@ -672,16 +672,16 @@ func expandCookieMatchPattern(l []interface{}) *wafv2.CookieMatchPattern { m := l[0].(map[string]interface{}) f := &wafv2.CookieMatchPattern{} - if v, ok := m["all_match_pattern"]; ok && len(v.([]interface{})) > 0 { + if v, ok := m["all"]; ok && len(v.([]interface{})) > 0 { f.All = &wafv2.All{} } - if v, ok := m["included_cookies_match_pattern"]; ok && len(v.([]interface{})) > 0 { - f.IncludedCookies = flex.ExpandStringList(m["included_cookies_match_pattern"].([]interface{})) + if v, ok := m["included_cookies"]; ok && len(v.([]interface{})) > 0 { + f.IncludedCookies = flex.ExpandStringList(m["included_cookies"].([]interface{})) } - if v, ok := m["excluded_cookies_match_pattern"]; ok && len(v.([]interface{})) > 0 { - f.ExcludedCookies = flex.ExpandStringList(m["excluded_cookies_match_pattern"].([]interface{})) + if v, ok := m["excluded_cookies"]; ok && len(v.([]interface{})) > 0 { + f.ExcludedCookies = flex.ExpandStringList(m["excluded_cookies"].([]interface{})) } return f @@ -1236,51 +1236,15 @@ func flattenCookieMatchPattern(s *wafv2.CookieMatchPattern) interface{} { m := map[string]interface{}{} if s.All != nil { - m["all_match_pattern"] = flattenAllMatchPattern(s.All) + m["all"] = make([]map[string]interface{}, 1) } if s.ExcludedCookies != nil { - m["included_cookies_match_pattern"] = flattenExcludedCookiesMatchPattern(s.ExcludedCookies) + m["excluded_cookies"] = flex.FlattenStringList(s.ExcludedCookies) } if s.IncludedCookies != nil { - m["included_cookies_match_pattern"] = flattenIncludedCookiesMatchPattern(s.IncludedCookies) - } - - return []interface{}{m} -} - -func flattenAllMatchPattern(s *wafv2.All) interface{} { - if s == nil { - return []interface{}{} - } - - m := map[string]interface{}{ - "all": make([]map[string]interface{}, 1), - } - - return []interface{}{m} -} - -func flattenExcludedCookiesMatchPattern(s []*string) interface{} { - if s == nil { - return []interface{}{} - } - - m := map[string]interface{}{ - "excluded_cookies": flex.FlattenStringList(s), - } - - return []interface{}{m} -} - -func flattenIncludedCookiesMatchPattern(s []*string) interface{} { - if s == nil { - return []interface{}{} - } - - m := map[string]interface{}{ - "included_cookies": flex.FlattenStringList(s), + m["included_cookies"] = flex.FlattenStringList(s.IncludedCookies) } return []interface{}{m} diff --git a/internal/service/wafv2/rule_group_test.go b/internal/service/wafv2/rule_group_test.go index 9ae92055480..4d50f61225c 100644 --- a/internal/service/wafv2/rule_group_test.go +++ b/internal/service/wafv2/rule_group_test.go @@ -356,7 +356,7 @@ func TestAccWAFV2RuleGroup_byteMatchStatement(t *testing.T) { }) } -func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch2(t *testing.T) { +func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { var v wafv2.RuleGroup ruleGroupName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_wafv2_rule_group.test" @@ -368,7 +368,7 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch2(t *testing.T) { CheckDestroy: testAccCheckRuleGroupDestroy, Steps: []resource.TestStep{ { - Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesMatchPatternIncludedCookies(ruleGroupName), + Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchAllQueryArguments(ruleGroupName), Check: resource.ComposeTestCheckFunc( testAccCheckRuleGroupExists(resourceName, &v), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), @@ -377,46 +377,18 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch2(t *testing.T) { "statement.#": "1", "statement.0.byte_match_statement.#": "1", "statement.0.byte_match_statement.0.field_to_match.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.oversize_handling": "MATCH", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_scope": "ALL", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.included_cookies_match_pattern.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.included_cookies_match_pattern.0.included_cookies": "session", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.excluded_cookies_match_pattern.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", }), ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateIdFunc: testAccRuleGroupImportStateIdFunc(resourceName), - }, - }, - }) -} - -func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { - var v wafv2.RuleGroup - ruleGroupName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_wafv2_rule_group.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(t); testAccPreCheckScopeRegional(t) }, - ErrorCheck: acctest.ErrorCheck(t, wafv2.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckRuleGroupDestroy, - Steps: []resource.TestStep{ - { - Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchAllQueryArguments(ruleGroupName), + Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchBody(ruleGroupName), Check: resource.ComposeTestCheckFunc( testAccCheckRuleGroupExists(resourceName, &v), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), @@ -425,18 +397,19 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.#": "1", "statement.0.byte_match_statement.#": "1", "statement.0.byte_match_statement.0.field_to_match.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.body.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.body.0.oversize_handling": "CONTINUE", + "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", }), ), }, { - Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchBody(ruleGroupName), + Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesMatchPatternAll(ruleGroupName), Check: resource.ComposeTestCheckFunc( testAccCheckRuleGroupExists(resourceName, &v), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), @@ -445,18 +418,25 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.#": "1", "statement.0.byte_match_statement.#": "1", "statement.0.byte_match_statement.0.field_to_match.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.body.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.oversize_handling": "MATCH", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_scope": "ALL", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.all.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.included_cookies.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.excluded_cookies.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", }), ), }, { - Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchBodyWithOversizeHandling(ruleGroupName, "CONTINUE"), + Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesMatchPatternIncludedCookies(ruleGroupName), Check: resource.ComposeTestCheckFunc( testAccCheckRuleGroupExists(resourceName, &v), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), @@ -465,19 +445,27 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.#": "1", "statement.0.byte_match_statement.#": "1", "statement.0.byte_match_statement.0.field_to_match.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.body.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.body.0.oversize_handling": "CONTINUE", - "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.oversize_handling": "MATCH", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_scope": "ALL", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.all.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.included_cookies.#": "2", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.included_cookies.0": "session", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.included_cookies.1": "session-id", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.excluded_cookies.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", }), ), }, { - Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesMatchPatternAll(ruleGroupName), + Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesMatchPatternExcludedCookies(ruleGroupName), Check: resource.ComposeTestCheckFunc( testAccCheckRuleGroupExists(resourceName, &v), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), @@ -486,21 +474,22 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.#": "1", "statement.0.byte_match_statement.#": "1", "statement.0.byte_match_statement.0.field_to_match.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.oversize_handling": "MATCH", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_scope": "ALL", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.all_match_pattern.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.all_match_pattern.0.all.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.included_cookies_match_pattern.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.excluded_cookies_match_pattern.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.oversize_handling": "MATCH", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_scope": "ALL", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.all.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.excluded_cookies.#": "2", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.excluded_cookies.0": "session", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.excluded_cookies.1": "session-id", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.included_cookies.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", }), ), }, @@ -2505,7 +2494,9 @@ resource "aws_wafv2_rule_group" "test" { search_string = "word" field_to_match { - body {} + body { + oversize_handling = "CONTINUE" + } } text_transformation { @@ -2531,11 +2522,11 @@ resource "aws_wafv2_rule_group" "test" { `, name) } -func testAccRuleGroupConfig_byteMatchStatementFieldToMatchBodyWithOversizeHandling(name string, overSizeHandling string) string { +func testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesMatchPatternAll(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { capacity = 15 - name = "%[1]s" + name = "%s" scope = "REGIONAL" rule { @@ -2552,8 +2543,12 @@ resource "aws_wafv2_rule_group" "test" { search_string = "word" field_to_match { - body { - oversize_handling = "%[2]s" + cookies { + match_scope = "ALL" + match_pattern { + all {} + } + oversize_handling = "MATCH" } } @@ -2577,10 +2572,10 @@ resource "aws_wafv2_rule_group" "test" { sampled_requests_enabled = false } } -`, name, overSizeHandling) +`, name) } -func testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesMatchPatternAll(name string) string { +func testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesMatchPatternIncludedCookies(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { capacity = 15 @@ -2604,9 +2599,7 @@ resource "aws_wafv2_rule_group" "test" { cookies { match_scope = "ALL" match_pattern { - all_match_pattern { - all {} - } + included_cookies = ["session", "session-id"] } oversize_handling = "MATCH" } @@ -2635,7 +2628,7 @@ resource "aws_wafv2_rule_group" "test" { `, name) } -func testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesMatchPatternIncludedCookies(name string) string { +func testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesMatchPatternExcludedCookies(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { capacity = 15 @@ -2659,9 +2652,7 @@ resource "aws_wafv2_rule_group" "test" { cookies { match_scope = "ALL" match_pattern { - included_cookies_match_pattern { - included_cookies ["session"] - } + excluded_cookies = ["session", "session-id"] } oversize_handling = "MATCH" } diff --git a/internal/service/wafv2/schemas.go b/internal/service/wafv2/schemas.go index 5909c725068..53968468401 100644 --- a/internal/service/wafv2/schemas.go +++ b/internal/service/wafv2/schemas.go @@ -623,12 +623,11 @@ func bodySchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeList, Optional: true, - MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "oversize_handling": { Type: schema.TypeString, - Optional: true, + Required: true, ValidateFunc: validation.StringInSlice(wafv2.OversizeHandling_Values(), false), }, }, @@ -654,9 +653,9 @@ func cookiesSchema() *schema.Schema { MinItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "all_match_pattern": allMatchPatternSchema(), - "included_cookies_match_pattern": includedCookiesMatchPatternSchema(), - "excluded_cookies_match_pattern": excludedCookiesMatchPatternSchema(), + "all": emptySchema(), + "included_cookies": cookiesMatchPatternBaseSchema(), + "excluded_cookies": cookiesMatchPatternBaseSchema(), }, }, }, @@ -670,23 +669,10 @@ func cookiesSchema() *schema.Schema { } } -func allMatchPatternSchema() *schema.Schema { - return &schema.Schema{ - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "all": emptySchema(), - }, - }, - } -} - func cookiesMatchPatternBaseSchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeList, - Required: true, + Optional: true, MinItems: 1, MaxItems: 199, Elem: &schema.Schema{ @@ -698,29 +684,3 @@ func cookiesMatchPatternBaseSchema() *schema.Schema { }, } } - -func includedCookiesMatchPatternSchema() *schema.Schema { - return &schema.Schema{ - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "included_cookies": cookiesMatchPatternBaseSchema(), - }, - }, - } -} - -func excludedCookiesMatchPatternSchema() *schema.Schema { - return &schema.Schema{ - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "excluded_cookies": cookiesMatchPatternBaseSchema(), - }, - }, - } -} From fd15aedc8ce7e2492611e18b4fbf9dbe238dc6f1 Mon Sep 17 00:00:00 2001 From: Scott Westover Date: Sun, 28 Aug 2022 22:13:00 -0400 Subject: [PATCH 05/52] added: support for headers attribute to wafv2 web acl Added support for the `headers` attribute in the `field_to_match` that is available under the wafv2 rule group and acl. Relates hashicorp#25545 Relates hashicorp#25832 Signed-off-by: Scott Westover --- .changelog/26506.txt | 2 + internal/service/wafv2/flex.go | 85 ++++++- internal/service/wafv2/rule_group_test.go | 264 ++++++++++++++++++++++ internal/service/wafv2/schemas.go | 77 +++++-- 4 files changed, 411 insertions(+), 17 deletions(-) diff --git a/.changelog/26506.txt b/.changelog/26506.txt index 19f3695dca4..ab183920820 100644 --- a/.changelog/26506.txt +++ b/.changelog/26506.txt @@ -3,4 +3,6 @@ resource/aws_wafv2_web_acl: Add `oversize_handling` attribute as part of `body` resource/aws_wafv2_rule_group: Add `oversize_handling` attribute as part of `body` attribute. resource/aws_wafv2_web_acl: Add support for `cookies` attribute as part of `field_to_match` attribute. resource/aws_wafv2_rule_group: Add support for `cookies` attribute as part of `field_to_match` attribute. +resource/aws_wafv2_web_acl: Add support for `headers` attribute as part of `field_to_match` attribute. +resource/aws_wafv2_rule_group: Add support for `headers` attribute as part of `field_to_match` attribute. ``` diff --git a/internal/service/wafv2/flex.go b/internal/service/wafv2/flex.go index a776da51134..e86d3502f54 100644 --- a/internal/service/wafv2/flex.go +++ b/internal/service/wafv2/flex.go @@ -399,6 +399,10 @@ func expandFieldToMatch(l []interface{}) *wafv2.FieldToMatch { f.Cookies = expandCookies(m["cookies"].([]interface{})) } + if v, ok := m["headers"]; ok && len(v.([]interface{})) > 0 { + f.Headers = expandHeaders(m["headers"].([]interface{})) + } + if v, ok := m["method"]; ok && len(v.([]interface{})) > 0 { f.Method = &wafv2.Method{} } @@ -658,13 +662,13 @@ func expandCookies(l []interface{}) *wafv2.Cookies { m := l[0].(map[string]interface{}) return &wafv2.Cookies{ - MatchPattern: expandCookieMatchPattern(m["match_pattern"].([]interface{})), + MatchPattern: expandCookiesMatchPattern(m["match_pattern"].([]interface{})), MatchScope: aws.String(m["match_scope"].(string)), OversizeHandling: aws.String(m["oversize_handling"].(string)), } } -func expandCookieMatchPattern(l []interface{}) *wafv2.CookieMatchPattern { +func expandCookiesMatchPattern(l []interface{}) *wafv2.CookieMatchPattern { if len(l) == 0 || l[0] == nil { return nil } @@ -687,6 +691,43 @@ func expandCookieMatchPattern(l []interface{}) *wafv2.CookieMatchPattern { return f } +func expandHeaders(l []interface{}) *wafv2.Headers { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + return &wafv2.Headers{ + MatchPattern: expandHeadersMatchPattern(m["match_pattern"].([]interface{})), + MatchScope: aws.String(m["match_scope"].(string)), + OversizeHandling: aws.String(m["oversize_handling"].(string)), + } +} + +func expandHeadersMatchPattern(l []interface{}) *wafv2.HeaderMatchPattern { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + f := &wafv2.HeaderMatchPattern{} + + if v, ok := m["all"]; ok && len(v.([]interface{})) > 0 { + f.All = &wafv2.All{} + } + + if v, ok := m["included_headers"]; ok && len(v.([]interface{})) > 0 { + f.IncludedHeaders = flex.ExpandStringList(m["included_headers"].([]interface{})) + } + + if v, ok := m["excluded_headers"]; ok && len(v.([]interface{})) > 0 { + f.ExcludedHeaders = flex.ExpandStringList(m["excluded_headers"].([]interface{})) + } + + return f +} + func flattenRules(r []*wafv2.Rule) interface{} { out := make([]map[string]interface{}, len(r)) for i, rule := range r { @@ -985,6 +1026,10 @@ func flattenFieldToMatch(f *wafv2.FieldToMatch) interface{} { m["cookies"] = flattenCookies(f.Cookies) } + if f.Headers != nil { + m["headers"] = flattenHeaders(f.Headers) + } + if f.Method != nil { m["method"] = make([]map[string]interface{}, 1) } @@ -1249,3 +1294,39 @@ func flattenCookieMatchPattern(s *wafv2.CookieMatchPattern) interface{} { return []interface{}{m} } + +func flattenHeaders(s *wafv2.Headers) interface{} { + if s == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "match_scope": aws.StringValue(s.MatchScope), + "match_pattern": flattenHeadersMatchPattern(s.MatchPattern), + "oversize_handling": aws.StringValue(s.OversizeHandling), + } + + return []interface{}{m} +} + +func flattenHeadersMatchPattern(s *wafv2.HeaderMatchPattern) interface{} { + if s == nil { + return []interface{}{} + } + + m := map[string]interface{}{} + + if s.All != nil { + m["all"] = make([]map[string]interface{}, 1) + } + + if s.ExcludedHeaders != nil { + m["excluded_headers"] = flex.FlattenStringList(s.ExcludedHeaders) + } + + if s.IncludedHeaders != nil { + m["included_headers"] = flex.FlattenStringList(s.IncludedHeaders) + } + + return []interface{}{m} +} diff --git a/internal/service/wafv2/rule_group_test.go b/internal/service/wafv2/rule_group_test.go index 4d50f61225c..ffbbe451185 100644 --- a/internal/service/wafv2/rule_group_test.go +++ b/internal/service/wafv2/rule_group_test.go @@ -379,6 +379,8 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.#": "1", "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "1", "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", @@ -400,6 +402,8 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.body.#": "1", "statement.0.byte_match_statement.0.field_to_match.0.body.0.oversize_handling": "CONTINUE", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", @@ -427,6 +431,7 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.all.#": "1", "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.included_cookies.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.excluded_cookies.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", @@ -456,6 +461,7 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.included_cookies.0": "session", "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.included_cookies.1": "session-id", "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.excluded_cookies.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", @@ -485,6 +491,95 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.excluded_cookies.0": "session", "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.excluded_cookies.1": "session-id", "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.included_cookies.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", + }), + ), + }, + { + Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchHeadersMatchPatternAll(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRuleGroupExists(resourceName, &v), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "statement.#": "1", + "statement.0.byte_match_statement.#": "1", + "statement.0.byte_match_statement.0.field_to_match.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.headers.0.oversize_handling": "MATCH", + "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_scope": "ALL", + "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_pattern.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_pattern.0.all.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_pattern.0.included_headers.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_pattern.0.excluded_headers.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", + }), + ), + }, + { + Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchHeadersMatchPatternIncludedHeaders(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRuleGroupExists(resourceName, &v), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "statement.#": "1", + "statement.0.byte_match_statement.#": "1", + "statement.0.byte_match_statement.0.field_to_match.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.headers.0.oversize_handling": "MATCH", + "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_scope": "ALL", + "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_pattern.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_pattern.0.all.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_pattern.0.included_headers.#": "2", + "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_pattern.0.included_headers.0": "session", + "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_pattern.0.included_headers.1": "session-id", + "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_pattern.0.excluded_headers.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", + }), + ), + }, + { + Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchHeadersMatchPatternExcludedHeaders(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRuleGroupExists(resourceName, &v), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "statement.#": "1", + "statement.0.byte_match_statement.#": "1", + "statement.0.byte_match_statement.0.field_to_match.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.headers.0.oversize_handling": "MATCH", + "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_scope": "ALL", + "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_pattern.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_pattern.0.all.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_pattern.0.excluded_headers.#": "2", + "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_pattern.0.excluded_headers.0": "session", + "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_pattern.0.excluded_headers.1": "session-id", + "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_pattern.0.included_headers.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", @@ -505,6 +600,8 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.#": "1", "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.method.#": "1", "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", @@ -525,6 +622,8 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.#": "1", "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "1", "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", @@ -545,6 +644,8 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.#": "1", "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "1", @@ -566,6 +667,8 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.#": "1", "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", @@ -587,6 +690,8 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.#": "1", "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", @@ -2681,6 +2786,165 @@ resource "aws_wafv2_rule_group" "test" { `, name) } +func testAccRuleGroupConfig_byteMatchStatementFieldToMatchHeadersMatchPatternAll(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 15 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + byte_match_statement { + positional_constraint = "CONTAINS" + search_string = "word" + + field_to_match { + headers { + match_scope = "ALL" + match_pattern { + all {} + } + oversize_handling = "MATCH" + } + } + + text_transformation { + priority = 1 + type = "NONE" + } + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-rule-metric-name" + sampled_requests_enabled = false + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-metric-name" + sampled_requests_enabled = false + } +} +`, name) +} + +func testAccRuleGroupConfig_byteMatchStatementFieldToMatchHeadersMatchPatternIncludedHeaders(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 15 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + byte_match_statement { + positional_constraint = "CONTAINS" + search_string = "word" + + field_to_match { + headers { + match_scope = "ALL" + match_pattern { + included_headers = ["session", "session-id"] + } + oversize_handling = "MATCH" + } + } + + text_transformation { + priority = 1 + type = "NONE" + } + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-rule-metric-name" + sampled_requests_enabled = false + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-metric-name" + sampled_requests_enabled = false + } +} +`, name) +} + +func testAccRuleGroupConfig_byteMatchStatementFieldToMatchHeadersMatchPatternExcludedHeaders(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 15 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + byte_match_statement { + positional_constraint = "CONTAINS" + search_string = "word" + + field_to_match { + headers { + match_scope = "ALL" + match_pattern { + excluded_headers = ["session", "session-id"] + } + oversize_handling = "MATCH" + } + } + + text_transformation { + priority = 1 + type = "NONE" + } + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-rule-metric-name" + sampled_requests_enabled = false + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-metric-name" + sampled_requests_enabled = false + } +} +`, name) +} + func testAccRuleGroupConfig_byteMatchStatementFieldToMatchMethod(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { diff --git a/internal/service/wafv2/schemas.go b/internal/service/wafv2/schemas.go index 53968468401..4c0b40d3792 100644 --- a/internal/service/wafv2/schemas.go +++ b/internal/service/wafv2/schemas.go @@ -335,6 +335,7 @@ func fieldToMatchBaseSchema() *schema.Resource { "all_query_arguments": emptySchema(), "body": bodySchema(), "cookies": cookiesSchema(), + "headers": headersSchema(), "method": emptySchema(), "query_string": emptySchema(), "single_header": { @@ -625,27 +626,27 @@ func bodySchema() *schema.Schema { Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "oversize_handling": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice(wafv2.OversizeHandling_Values(), false), - }, + "oversize_handling": oversizeHandlingSchema(), }, }, } } +func oversizeHandlingSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(wafv2.OversizeHandling_Values(), false), + } +} + func cookiesSchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeList, Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "match_scope": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice(wafv2.MapMatchScope_Values(), false), - }, + "match_scope": matchScopeSchema(), "match_pattern": { Type: schema.TypeList, Required: true, @@ -659,16 +660,20 @@ func cookiesSchema() *schema.Schema { }, }, }, - "oversize_handling": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice(wafv2.OversizeHandling_Values(), false), - }, + "oversize_handling": oversizeHandlingSchema(), }, }, } } +func matchScopeSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(wafv2.MapMatchScope_Values(), false), + } +} + func cookiesMatchPatternBaseSchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeList, @@ -684,3 +689,45 @@ func cookiesMatchPatternBaseSchema() *schema.Schema { }, } } + +func headersSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "match_scope": matchScopeSchema(), + "match_pattern": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + MinItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "all": emptySchema(), + "included_headers": headersMatchPatternBaseSchema(), + "excluded_headers": headersMatchPatternBaseSchema(), + }, + }, + }, + "oversize_handling": oversizeHandlingSchema(), + }, + }, + } +} + +func headersMatchPatternBaseSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 199, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 64), + validation.StringMatch(regexp.MustCompile(`.*\S.*`), ""), + ), + }, + } +} From a4734bc36d9624a269efd894fdafcdfe08cdf3ca Mon Sep 17 00:00:00 2001 From: Scott Westover Date: Mon, 29 Aug 2022 00:12:51 -0400 Subject: [PATCH 06/52] added: support for json body attribute to wafv2 web acl Added support for the `json_body` attribute in the `field_to_match` that is available under the wafv2 rule group and acl. Relates hashicorp#25545 Relates hashicorp#25832 Signed-off-by: Scott Westover --- .changelog/26506.txt | 2 + internal/service/wafv2/flex.go | 94 +++++++- internal/service/wafv2/rule_group_test.go | 269 +++++++++++++++++++++- internal/service/wafv2/schemas.go | 42 ++++ 4 files changed, 396 insertions(+), 11 deletions(-) diff --git a/.changelog/26506.txt b/.changelog/26506.txt index ab183920820..752d6dac64e 100644 --- a/.changelog/26506.txt +++ b/.changelog/26506.txt @@ -5,4 +5,6 @@ resource/aws_wafv2_web_acl: Add support for `cookies` attribute as part of `fiel resource/aws_wafv2_rule_group: Add support for `cookies` attribute as part of `field_to_match` attribute. resource/aws_wafv2_web_acl: Add support for `headers` attribute as part of `field_to_match` attribute. resource/aws_wafv2_rule_group: Add support for `headers` attribute as part of `field_to_match` attribute. +resource/aws_wafv2_web_acl: Add support for `json_body` attribute as part of `field_to_match` attribute. +resource/aws_wafv2_rule_group: Add support for `json_body` attribute as part of `field_to_match` attribute. ``` diff --git a/internal/service/wafv2/flex.go b/internal/service/wafv2/flex.go index e86d3502f54..0fb491d0118 100644 --- a/internal/service/wafv2/flex.go +++ b/internal/service/wafv2/flex.go @@ -403,6 +403,10 @@ func expandFieldToMatch(l []interface{}) *wafv2.FieldToMatch { f.Headers = expandHeaders(m["headers"].([]interface{})) } + if v, ok := m["json_body"]; ok && len(v.([]interface{})) > 0 { + f.JsonBody = expandJsonBody(m["json_body"].([]interface{})) + } + if v, ok := m["method"]; ok && len(v.([]interface{})) > 0 { f.Method = &wafv2.Method{} } @@ -662,13 +666,13 @@ func expandCookies(l []interface{}) *wafv2.Cookies { m := l[0].(map[string]interface{}) return &wafv2.Cookies{ - MatchPattern: expandCookiesMatchPattern(m["match_pattern"].([]interface{})), + MatchPattern: expandCookieMatchPattern(m["match_pattern"].([]interface{})), MatchScope: aws.String(m["match_scope"].(string)), OversizeHandling: aws.String(m["oversize_handling"].(string)), } } -func expandCookiesMatchPattern(l []interface{}) *wafv2.CookieMatchPattern { +func expandCookieMatchPattern(l []interface{}) *wafv2.CookieMatchPattern { if len(l) == 0 || l[0] == nil { return nil } @@ -699,13 +703,13 @@ func expandHeaders(l []interface{}) *wafv2.Headers { m := l[0].(map[string]interface{}) return &wafv2.Headers{ - MatchPattern: expandHeadersMatchPattern(m["match_pattern"].([]interface{})), + MatchPattern: expandHeaderMatchPattern(m["match_pattern"].([]interface{})), MatchScope: aws.String(m["match_scope"].(string)), OversizeHandling: aws.String(m["oversize_handling"].(string)), } } -func expandHeadersMatchPattern(l []interface{}) *wafv2.HeaderMatchPattern { +func expandHeaderMatchPattern(l []interface{}) *wafv2.HeaderMatchPattern { if len(l) == 0 || l[0] == nil { return nil } @@ -728,6 +732,44 @@ func expandHeadersMatchPattern(l []interface{}) *wafv2.HeaderMatchPattern { return f } +func expandJsonBody(l []interface{}) *wafv2.JsonBody { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + f := &wafv2.JsonBody{ + MatchPattern: expandJsonMatchPattern(m["match_pattern"].([]interface{})), + MatchScope: aws.String(m["match_scope"].(string)), + OversizeHandling: aws.String(m["oversize_handling"].(string)), + } + + if v, ok := m["invalid_fallback_behavior"]; ok && len(v.(string)) > 0 { + f.InvalidFallbackBehavior = aws.String(m["invalid_fallback_behavior"].(string)) + } + + return f +} + +func expandJsonMatchPattern(l []interface{}) *wafv2.JsonMatchPattern { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + f := &wafv2.JsonMatchPattern{} + + if v, ok := m["all"]; ok && len(v.([]interface{})) > 0 { + f.All = &wafv2.All{} + } + + if v, ok := m["included_paths"]; ok && len(v.([]interface{})) > 0 { + f.IncludedPaths = flex.ExpandStringList(m["included_paths"].([]interface{})) + } + + return f +} + func flattenRules(r []*wafv2.Rule) interface{} { out := make([]map[string]interface{}, len(r)) for i, rule := range r { @@ -1030,6 +1072,10 @@ func flattenFieldToMatch(f *wafv2.FieldToMatch) interface{} { m["headers"] = flattenHeaders(f.Headers) } + if f.JsonBody != nil { + m["json_body"] = flattenJsonBody(f.JsonBody) + } + if f.Method != nil { m["method"] = make([]map[string]interface{}, 1) } @@ -1302,14 +1348,14 @@ func flattenHeaders(s *wafv2.Headers) interface{} { m := map[string]interface{}{ "match_scope": aws.StringValue(s.MatchScope), - "match_pattern": flattenHeadersMatchPattern(s.MatchPattern), + "match_pattern": flattenHeaderMatchPattern(s.MatchPattern), "oversize_handling": aws.StringValue(s.OversizeHandling), } return []interface{}{m} } -func flattenHeadersMatchPattern(s *wafv2.HeaderMatchPattern) interface{} { +func flattenHeaderMatchPattern(s *wafv2.HeaderMatchPattern) interface{} { if s == nil { return []interface{}{} } @@ -1330,3 +1376,39 @@ func flattenHeadersMatchPattern(s *wafv2.HeaderMatchPattern) interface{} { return []interface{}{m} } + +func flattenJsonBody(s *wafv2.JsonBody) interface{} { + if s == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "match_scope": aws.StringValue(s.MatchScope), + "match_pattern": flattenJsonMatchPattern(s.MatchPattern), + "oversize_handling": aws.StringValue(s.OversizeHandling), + } + + if s.InvalidFallbackBehavior != nil { + m["invalid_fallback_behavior"] = aws.StringValue(s.InvalidFallbackBehavior) + } + + return []interface{}{m} +} + +func flattenJsonMatchPattern(s *wafv2.JsonMatchPattern) interface{} { + if s == nil { + return []interface{}{} + } + + m := map[string]interface{}{} + + if s.All != nil { + m["all"] = make([]map[string]interface{}, 1) + } + + if s.IncludedPaths != nil { + m["included_paths"] = flex.FlattenStringList(s.IncludedPaths) + } + + return []interface{}{m} +} diff --git a/internal/service/wafv2/rule_group_test.go b/internal/service/wafv2/rule_group_test.go index ffbbe451185..36b70772b5d 100644 --- a/internal/service/wafv2/rule_group_test.go +++ b/internal/service/wafv2/rule_group_test.go @@ -381,6 +381,7 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", @@ -404,6 +405,7 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.0.body.0.oversize_handling": "CONTINUE", "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", @@ -432,6 +434,7 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.included_cookies.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.excluded_cookies.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", @@ -462,6 +465,7 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.included_cookies.1": "session-id", "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.excluded_cookies.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", @@ -492,6 +496,7 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.excluded_cookies.1": "session-id", "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.included_cookies.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", @@ -521,6 +526,7 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_pattern.0.included_headers.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_pattern.0.excluded_headers.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", @@ -550,6 +556,7 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_pattern.0.included_headers.0": "session", "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_pattern.0.included_headers.1": "session-id", "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_pattern.0.excluded_headers.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", @@ -580,6 +587,93 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_pattern.0.excluded_headers.0": "session", "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_pattern.0.excluded_headers.1": "session-id", "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_pattern.0.included_headers.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", + }), + ), + }, + { + Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchJsonBodyMatchPatternAll(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRuleGroupExists(resourceName, &v), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "statement.#": "1", + "statement.0.byte_match_statement.#": "1", + "statement.0.byte_match_statement.0.field_to_match.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.oversize_handling": "MATCH", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_scope": "ALL", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.0.all.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.0.included_paths.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.invalid_fallback_behavior": "", + "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", + }), + ), + }, + { + Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchJsonBodyMatchPatternIncludedPaths(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRuleGroupExists(resourceName, &v), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "statement.#": "1", + "statement.0.byte_match_statement.#": "1", + "statement.0.byte_match_statement.0.field_to_match.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.oversize_handling": "MATCH", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_scope": "ALL", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.0.all.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.0.included_paths.#": "2", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.0.included_paths.0": "/dogs/0/name", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.0.included_paths.1": "/cats", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.invalid_fallback_behavior": "", + "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", + }), + ), + }, + { + Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchJsonBodyInvalidFallbackBehavior(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRuleGroupExists(resourceName, &v), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "statement.#": "1", + "statement.0.byte_match_statement.#": "1", + "statement.0.byte_match_statement.0.field_to_match.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.oversize_handling": "MATCH", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_scope": "ALL", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.0.all.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.0.included_paths.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.invalid_fallback_behavior": "NO_MATCH", "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", @@ -602,6 +696,7 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.method.#": "1", "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", @@ -624,6 +719,7 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "1", "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", @@ -646,6 +742,7 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "1", @@ -669,6 +766,7 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", @@ -693,6 +791,7 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", @@ -2683,7 +2782,7 @@ resource "aws_wafv2_rule_group" "test" { func testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesMatchPatternIncludedCookies(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { - capacity = 15 + capacity = 50 name = "%s" scope = "REGIONAL" @@ -2736,7 +2835,7 @@ resource "aws_wafv2_rule_group" "test" { func testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesMatchPatternExcludedCookies(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { - capacity = 15 + capacity = 50 name = "%s" scope = "REGIONAL" @@ -2789,7 +2888,7 @@ resource "aws_wafv2_rule_group" "test" { func testAccRuleGroupConfig_byteMatchStatementFieldToMatchHeadersMatchPatternAll(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { - capacity = 15 + capacity = 50 name = "%s" scope = "REGIONAL" @@ -2842,7 +2941,7 @@ resource "aws_wafv2_rule_group" "test" { func testAccRuleGroupConfig_byteMatchStatementFieldToMatchHeadersMatchPatternIncludedHeaders(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { - capacity = 15 + capacity = 50 name = "%s" scope = "REGIONAL" @@ -2895,7 +2994,7 @@ resource "aws_wafv2_rule_group" "test" { func testAccRuleGroupConfig_byteMatchStatementFieldToMatchHeadersMatchPatternExcludedHeaders(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { - capacity = 15 + capacity = 50 name = "%s" scope = "REGIONAL" @@ -2945,6 +3044,166 @@ resource "aws_wafv2_rule_group" "test" { `, name) } +func testAccRuleGroupConfig_byteMatchStatementFieldToMatchJsonBodyMatchPatternAll(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 50 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + byte_match_statement { + positional_constraint = "CONTAINS" + search_string = "word" + + field_to_match { + json_body { + match_scope = "ALL" + match_pattern { + all {} + } + oversize_handling = "MATCH" + } + } + + text_transformation { + priority = 1 + type = "NONE" + } + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-rule-metric-name" + sampled_requests_enabled = false + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-metric-name" + sampled_requests_enabled = false + } +} +`, name) +} + +func testAccRuleGroupConfig_byteMatchStatementFieldToMatchJsonBodyMatchPatternIncludedPaths(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 50 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + byte_match_statement { + positional_constraint = "CONTAINS" + search_string = "word" + + field_to_match { + json_body { + match_scope = "ALL" + match_pattern { + included_paths = ["/dogs/0/name", "/cats"] + } + oversize_handling = "MATCH" + } + } + + text_transformation { + priority = 1 + type = "NONE" + } + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-rule-metric-name" + sampled_requests_enabled = false + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-metric-name" + sampled_requests_enabled = false + } +} +`, name) +} + +func testAccRuleGroupConfig_byteMatchStatementFieldToMatchJsonBodyInvalidFallbackBehavior(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 50 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + byte_match_statement { + positional_constraint = "CONTAINS" + search_string = "word" + + field_to_match { + json_body { + invalid_fallback_behavior = "NO_MATCH" + match_scope = "ALL" + match_pattern { + all {} + } + oversize_handling = "MATCH" + } + } + + text_transformation { + priority = 1 + type = "NONE" + } + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-rule-metric-name" + sampled_requests_enabled = false + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-metric-name" + sampled_requests_enabled = false + } +} +`, name) +} + func testAccRuleGroupConfig_byteMatchStatementFieldToMatchMethod(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { diff --git a/internal/service/wafv2/schemas.go b/internal/service/wafv2/schemas.go index 4c0b40d3792..701b3f28ebc 100644 --- a/internal/service/wafv2/schemas.go +++ b/internal/service/wafv2/schemas.go @@ -336,6 +336,7 @@ func fieldToMatchBaseSchema() *schema.Resource { "body": bodySchema(), "cookies": cookiesSchema(), "headers": headersSchema(), + "json_body": jsonBodySchema(), "method": emptySchema(), "query_string": emptySchema(), "single_header": { @@ -731,3 +732,44 @@ func headersMatchPatternBaseSchema() *schema.Schema { }, } } + +func jsonBodySchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "invalid_fallback_behavior": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(wafv2.FallbackBehavior_Values(), false), + }, + "match_scope": matchScopeSchema(), + "match_pattern": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + MinItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "all": emptySchema(), + "included_paths": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 512), + validation.StringMatch(regexp.MustCompile(`([/])|([/](([^~])|(~[01]))+)`), ""), + ), + }, + }, + }, + }, + }, + "oversize_handling": oversizeHandlingSchema(), + }, + }, + } +} From 25b43f06e44f7d78ee75d5aa612714ad9dc03e89 Mon Sep 17 00:00:00 2001 From: Scott Westover Date: Mon, 29 Aug 2022 00:43:39 -0400 Subject: [PATCH 07/52] changed: updated wafv2 documentation to include new fields Signed-off-by: Scott Westover --- internal/service/wafv2/schemas.go | 10 ++-- website/docs/r/wafv2_rule_group.html.markdown | 46 ++++++++++++++++++- website/docs/r/wafv2_web_acl.html.markdown | 46 ++++++++++++++++++- 3 files changed, 93 insertions(+), 9 deletions(-) diff --git a/internal/service/wafv2/schemas.go b/internal/service/wafv2/schemas.go index 701b3f28ebc..63a1df34ae9 100644 --- a/internal/service/wafv2/schemas.go +++ b/internal/service/wafv2/schemas.go @@ -647,7 +647,6 @@ func cookiesSchema() *schema.Schema { Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "match_scope": matchScopeSchema(), "match_pattern": { Type: schema.TypeList, Required: true, @@ -656,11 +655,12 @@ func cookiesSchema() *schema.Schema { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "all": emptySchema(), - "included_cookies": cookiesMatchPatternBaseSchema(), "excluded_cookies": cookiesMatchPatternBaseSchema(), + "included_cookies": cookiesMatchPatternBaseSchema(), }, }, }, + "match_scope": matchScopeSchema(), "oversize_handling": oversizeHandlingSchema(), }, }, @@ -697,7 +697,6 @@ func headersSchema() *schema.Schema { Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "match_scope": matchScopeSchema(), "match_pattern": { Type: schema.TypeList, Required: true, @@ -706,11 +705,12 @@ func headersSchema() *schema.Schema { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "all": emptySchema(), - "included_headers": headersMatchPatternBaseSchema(), "excluded_headers": headersMatchPatternBaseSchema(), + "included_headers": headersMatchPatternBaseSchema(), }, }, }, + "match_scope": matchScopeSchema(), "oversize_handling": oversizeHandlingSchema(), }, }, @@ -744,7 +744,6 @@ func jsonBodySchema() *schema.Schema { Optional: true, ValidateFunc: validation.StringInSlice(wafv2.FallbackBehavior_Values(), false), }, - "match_scope": matchScopeSchema(), "match_pattern": { Type: schema.TypeList, Required: true, @@ -768,6 +767,7 @@ func jsonBodySchema() *schema.Schema { }, }, }, + "match_scope": matchScopeSchema(), "oversize_handling": oversizeHandlingSchema(), }, }, diff --git a/website/docs/r/wafv2_rule_group.html.markdown b/website/docs/r/wafv2_rule_group.html.markdown index c6e4892e549..dc6ac67e904 100644 --- a/website/docs/r/wafv2_rule_group.html.markdown +++ b/website/docs/r/wafv2_rule_group.html.markdown @@ -494,11 +494,14 @@ 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. -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`, `cookies`, `headers`, `json_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`, `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. See [Body](#body) below for details. +* `cookies` - (Optional) Inspect the cookies in the web request. See [Cookies](#cookies) below for details. +* `headers` - (Optional) Inspect the request headers. See [Headers](#headers) below for details. +* `json_body` - (Optional) Inspect the request body as JSON, which immediately follows the request headers. See [JsonBody](#json-body) below for details. * `method` - (Optional) Inspect the HTTP method. The method indicates the type of operation that the request is asking the origin to perform. * `query_string` - (Optional) Inspect the query string. This is the part of a URL that appears after a `?` character, if any. * `single_header` - (Optional) Inspect a single header. See [Single Header](#single-header) below for details. @@ -534,6 +537,45 @@ The `body` block supports the following arguments: * `oversize_handling` - (Required) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. +### Cookies + +Inspect the cookies in the web request. + +The `cookies` block supports the following arguments: + +* `match_pattern` - (Required) The filter to use to identify the subset of cookies to inspect in a web request. The `match_pattern` block supports only one of the following arguments: + * `all` - An empty configuration block that is used for inspecting all cookies. + * `included_cookies` - An array of strings that will be used for inspecting cookies that have a key that matches one of the provided values. + * `excluded_cookies` - An array of strings that will be used for inspecting cookies that do not have a key that matches one of the provided values. +* `match_scope` - (Required) The parts of the cookies to inspect with the rule inspection criteria. If you specify `All`, AWS WAF inspects both keys and values. Valid values include the following: `ALL`, `Key`, `Value`. +* `oversize_handling` - (Required) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. + +### Headers + +Inspect the request headers. + +The `headers` block supports the following arguments: + +* `match_pattern` - (Required) The filter to use to identify the subset of headers to inspect in a web request. The `match_pattern` block supports only one of the following arguments: + * `all` - An empty configuration block that is used for inspecting all headers. + * `included_headers` - An array of strings that will be used for inspecting headers that have a key that matches one of the provided values. + * `excluded_headers` - An array of strings that will be used for inspecting headers that do not have a key that matches one of the provided values. +* `match_scope` - (Required) The parts of the headers to inspect with the rule inspection criteria. If you specify `All`, AWS WAF inspects both keys and values. Valid values include the following: `ALL`, `Key`, `Value`. +* `oversize_handling` - (Required) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. + +### Json Body + +Inspect the request body as JSON, which immediately follows the request headers. + +The `json_body` block supports the following arguments: + +* `invalid_fallback_behavior` - (Optional) What AWS WAF should do if it fails to completely parse the JSON body. Valid values include the following: `EVALUATE_AS_STRING`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/APIReference/API_JsonBody.html) for more information. +* `match_pattern` - (Required) The patterns to look for in the JSON body. AWS WAF inspects the results of these pattern matches against the rule inspection criteria. The `match_pattern` block supports only one of the following arguments: + * `all` - An empty configuration block that is used for inspecting all of the elements. + * `included_paths` - An array of strings that will be used for inspecting the specified paths. Provide the include paths using JSON Pointer syntax. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/APIReference/API_JsonMatchPattern.html) for more information. +* `match_scope` - (Required) The parts of the headers to inspect with the rule inspection criteria. If you specify `All`, AWS WAF inspects both keys and values. Valid values include the following: `ALL`, `Key`, `Value`. +* `oversize_handling` - (Required) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. + ### Single Header Inspect a single header. Provide the name of the header to inspect, for example, `User-Agent` or `Referer` (provided as lowercase strings). diff --git a/website/docs/r/wafv2_web_acl.html.markdown b/website/docs/r/wafv2_web_acl.html.markdown index 9cebc11dc18..9983ebbf77f 100644 --- a/website/docs/r/wafv2_web_acl.html.markdown +++ b/website/docs/r/wafv2_web_acl.html.markdown @@ -545,11 +545,14 @@ 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. -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`, `cookies`, `headers`, `json_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`, `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. See [Body](#body) below for details. +* `cookies` - (Optional) Inspect the cookies in the web request. See [Cookies](#cookies) below for details. +* `headers` - (Optional) Inspect the request headers. See [Headers](#headers) below for details. +* `json_body` - (Optional) Inspect the request body as JSON, which immediately follows the request headers. See [JsonBody](#json-body) below for details. * `method` - (Optional) Inspect the HTTP method. The method indicates the type of operation that the request is asking the origin to perform. * `query_string` - (Optional) Inspect the query string. This is the part of a URL that appears after a `?` character, if any. * `single_header` - (Optional) Inspect a single header. See [Single Header](#single-header) below for details. @@ -585,6 +588,45 @@ The `body` block supports the following arguments: * `oversize_handling` - (Required) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. +### Cookies + +Inspect the cookies in the web request. + +The `cookies` block supports the following arguments: + +* `match_pattern` - (Required) The filter to use to identify the subset of cookies to inspect in a web request. The `match_pattern` block supports only one of the following arguments: + * `all` - An empty configuration block that is used for inspecting all cookies. + * `included_cookies` - An array of strings that will be used for inspecting cookies that have a key that matches one of the provided values. + * `excluded_cookies` - An array of strings that will be used for inspecting cookies that do not have a key that matches one of the provided values. +* `match_scope` - (Required) The parts of the cookies to inspect with the rule inspection criteria. If you specify `All`, AWS WAF inspects both keys and values. Valid values include the following: `ALL`, `Key`, `Value`. +* `oversize_handling` - (Required) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. + +### Headers + +Inspect the request headers. + +The `headers` block supports the following arguments: + +* `match_pattern` - (Required) The filter to use to identify the subset of headers to inspect in a web request. The `match_pattern` block supports only one of the following arguments: + * `all` - An empty configuration block that is used for inspecting all headers. + * `included_headers` - An array of strings that will be used for inspecting headers that have a key that matches one of the provided values. + * `excluded_headers` - An array of strings that will be used for inspecting headers that do not have a key that matches one of the provided values. +* `match_scope` - (Required) The parts of the headers to inspect with the rule inspection criteria. If you specify `All`, AWS WAF inspects both keys and values. Valid values include the following: `ALL`, `Key`, `Value`. +* `oversize_handling` - (Required) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. + +### Json Body + +Inspect the request body as JSON, which immediately follows the request headers. + +The `json_body` block supports the following arguments: + +* `invalid_fallback_behavior` - (Optional) What AWS WAF should do if it fails to completely parse the JSON body. Valid values include the following: `EVALUATE_AS_STRING`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/APIReference/API_JsonBody.html) for more information. +* `match_pattern` - (Required) The patterns to look for in the JSON body. AWS WAF inspects the results of these pattern matches against the rule inspection criteria. The `match_pattern` block supports only one of the following arguments: + * `all` - An empty configuration block that is used for inspecting all of the elements. + * `included_paths` - An array of strings that will be used for inspecting the specified paths. Provide the include paths using JSON Pointer syntax. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/APIReference/API_JsonMatchPattern.html) for more information. +* `match_scope` - (Required) The parts of the headers to inspect with the rule inspection criteria. If you specify `All`, AWS WAF inspects both keys and values. Valid values include the following: `ALL`, `Key`, `Value`. +* `oversize_handling` - (Required) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. + ### Single Header Inspect a single header. Provide the name of the header to inspect, for example, `User-Agent` or `Referer` (provided as lowercase strings). From 9e778804b446d4027359f8516d23e96a46449a24 Mon Sep 17 00:00:00 2001 From: Scott Westover Date: Mon, 29 Aug 2022 01:39:46 -0400 Subject: [PATCH 08/52] added: additional acceptance tests for wafv2 Signed-off-by: Scott Westover --- internal/service/wafv2/rule_group_test.go | 219 ++++++++++++++++++++++ 1 file changed, 219 insertions(+) diff --git a/internal/service/wafv2/rule_group_test.go b/internal/service/wafv2/rule_group_test.go index 36b70772b5d..d9163cc6abe 100644 --- a/internal/service/wafv2/rule_group_test.go +++ b/internal/service/wafv2/rule_group_test.go @@ -414,6 +414,10 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { }), ), }, + { + Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchBodyInvalidConfiguration(ruleGroupName), + ExpectError: regexp.MustCompile(`argument "oversize_handling" is required`), + }, { Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesMatchPatternAll(ruleGroupName), Check: resource.ComposeTestCheckFunc( @@ -505,6 +509,10 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { }), ), }, + { + Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesInvalidConfiguration(ruleGroupName), + ExpectError: regexp.MustCompile(`argument "oversize_handling" is required`), + }, { Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchHeadersMatchPatternAll(ruleGroupName), Check: resource.ComposeTestCheckFunc( @@ -596,6 +604,10 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { }), ), }, + { + Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchHeadersInvalidConfiguration(ruleGroupName), + ExpectError: regexp.MustCompile(`argument "oversize_handling" is required`), + }, { Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchJsonBodyMatchPatternAll(ruleGroupName), Check: resource.ComposeTestCheckFunc( @@ -682,6 +694,10 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { }), ), }, + { + Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchJsonBodyMatchInvalidConfiguration(ruleGroupName), + ExpectError: regexp.MustCompile(`argument "match_scope" is required`), + }, { Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchMethod(ruleGroupName), Check: resource.ComposeTestCheckFunc( @@ -2726,6 +2742,105 @@ resource "aws_wafv2_rule_group" "test" { `, name) } +func testAccRuleGroupConfig_byteMatchStatementFieldToMatchBodyInvalidConfiguration(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 15 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + byte_match_statement { + positional_constraint = "CONTAINS" + search_string = "word" + + field_to_match { + body {} + } + + text_transformation { + priority = 1 + type = "NONE" + } + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-rule-metric-name" + sampled_requests_enabled = false + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-metric-name" + sampled_requests_enabled = false + } +} +`, name) +} + +func testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesInvalidConfiguration(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 15 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + byte_match_statement { + positional_constraint = "CONTAINS" + search_string = "word" + + field_to_match { + cookies { + match_scope = "ALL" + match_pattern { + all {} + } + } + } + + text_transformation { + priority = 1 + type = "NONE" + } + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-rule-metric-name" + sampled_requests_enabled = false + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-metric-name" + sampled_requests_enabled = false + } +} +`, name) +} + func testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesMatchPatternAll(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { @@ -2885,6 +3000,58 @@ resource "aws_wafv2_rule_group" "test" { `, name) } +func testAccRuleGroupConfig_byteMatchStatementFieldToMatchHeadersInvalidConfiguration(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 50 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + byte_match_statement { + positional_constraint = "CONTAINS" + search_string = "word" + + field_to_match { + headers { + match_scope = "ALL" + match_pattern { + all {} + } + } + } + + text_transformation { + priority = 1 + type = "NONE" + } + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-rule-metric-name" + sampled_requests_enabled = false + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-metric-name" + sampled_requests_enabled = false + } +} +`, name) +} + func testAccRuleGroupConfig_byteMatchStatementFieldToMatchHeadersMatchPatternAll(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { @@ -3044,6 +3211,58 @@ resource "aws_wafv2_rule_group" "test" { `, name) } +func testAccRuleGroupConfig_byteMatchStatementFieldToMatchJsonBodyMatchInvalidConfiguration(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 50 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + byte_match_statement { + positional_constraint = "CONTAINS" + search_string = "word" + + field_to_match { + json_body { + match_pattern { + all {} + } + oversize_handling = "MATCH" + } + } + + text_transformation { + priority = 1 + type = "NONE" + } + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-rule-metric-name" + sampled_requests_enabled = false + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-metric-name" + sampled_requests_enabled = false + } +} +`, name) +} + func testAccRuleGroupConfig_byteMatchStatementFieldToMatchJsonBodyMatchPatternAll(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { From ef63bd814d4b7f17b3efe6e96cde213f7fa729f7 Mon Sep 17 00:00:00 2001 From: Scott Westover Date: Mon, 29 Aug 2022 02:31:39 -0400 Subject: [PATCH 09/52] f/wafv2_rule_group - updated changelog Signed-off-by: Scott Westover --- .changelog/26506.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.changelog/26506.txt b/.changelog/26506.txt index 752d6dac64e..7549010b835 100644 --- a/.changelog/26506.txt +++ b/.changelog/26506.txt @@ -8,3 +8,8 @@ resource/aws_wafv2_rule_group: Add support for `headers` attribute as part of `f resource/aws_wafv2_web_acl: Add support for `json_body` attribute as part of `field_to_match` attribute. resource/aws_wafv2_rule_group: Add support for `json_body` attribute as part of `field_to_match` attribute. ``` + +```release-note:breaking-change +resource/aws_wafv2_rule_group: The `body` attribute on the `field_to_match` will no longer accept an empty configuration block, and now requires `oversize_handling` attribute to be provided. +resource/aws_wafv2_web_acl: The `body` attribute on the `field_to_match` will no longer accept an empty configuration block, and now requires `oversize_handling` attribute to be provided. +``` From ba06059abbb7babd7deb2499d65af52ce2b539f4 Mon Sep 17 00:00:00 2001 From: Scott Westover Date: Tue, 30 Aug 2022 10:41:18 -0400 Subject: [PATCH 10/52] fixed: changelog to match the changes that were added --- .changelog/26506.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.changelog/26506.txt b/.changelog/26506.txt index 7549010b835..007359d9bee 100644 --- a/.changelog/26506.txt +++ b/.changelog/26506.txt @@ -1,8 +1,6 @@ ```release-note:enhancement resource/aws_wafv2_web_acl: Add `oversize_handling` attribute as part of `body` attribute. -resource/aws_wafv2_rule_group: Add `oversize_handling` attribute as part of `body` attribute. -resource/aws_wafv2_web_acl: Add support for `cookies` attribute as part of `field_to_match` attribute. -resource/aws_wafv2_rule_group: Add support for `cookies` attribute as part of `field_to_match` attribute. +resource/aws_wafv2_rule_group: Add `oversize_handling` attribute as part of `body` attribute resource/aws_wafv2_web_acl: Add support for `headers` attribute as part of `field_to_match` attribute. resource/aws_wafv2_rule_group: Add support for `headers` attribute as part of `field_to_match` attribute. resource/aws_wafv2_web_acl: Add support for `json_body` attribute as part of `field_to_match` attribute. From 2c9005babcab0d2a7622d3a295beb16ae854e403 Mon Sep 17 00:00:00 2001 From: Scott Westover Date: Tue, 30 Aug 2022 10:41:35 -0400 Subject: [PATCH 11/52] fixed: conflicts in test file for waf after merge --- internal/service/wafv2/rule_group_test.go | 313 +--------------------- 1 file changed, 5 insertions(+), 308 deletions(-) diff --git a/internal/service/wafv2/rule_group_test.go b/internal/service/wafv2/rule_group_test.go index da27b644bc0..420826b6b26 100644 --- a/internal/service/wafv2/rule_group_test.go +++ b/internal/service/wafv2/rule_group_test.go @@ -446,101 +446,6 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { }), ), }, - { - Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesMatchPatternAll(ruleGroupName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName, &v), - acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), - resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), - resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ - "statement.#": "1", - "statement.0.byte_match_statement.#": "1", - "statement.0.byte_match_statement.0.field_to_match.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.oversize_handling": "MATCH", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_scope": "ALL", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.all.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.included_cookies.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.excluded_cookies.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", - }), - ), - }, - { - Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesMatchPatternIncludedCookies(ruleGroupName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName, &v), - acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), - resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), - resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ - "statement.#": "1", - "statement.0.byte_match_statement.#": "1", - "statement.0.byte_match_statement.0.field_to_match.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.oversize_handling": "MATCH", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_scope": "ALL", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.all.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.included_cookies.#": "2", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.included_cookies.0": "session", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.included_cookies.1": "session-id", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.excluded_cookies.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", - }), - ), - }, - { - Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesMatchPatternExcludedCookies(ruleGroupName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName, &v), - acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), - resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), - resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ - "statement.#": "1", - "statement.0.byte_match_statement.#": "1", - "statement.0.byte_match_statement.0.field_to_match.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.oversize_handling": "MATCH", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_scope": "ALL", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.all.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.excluded_cookies.#": "2", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.excluded_cookies.0": "session", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.excluded_cookies.1": "session-id", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.0.match_pattern.0.included_cookies.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", - }), - ), - }, - { - Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesInvalidConfiguration(ruleGroupName), - ExpectError: regexp.MustCompile(`argument "oversize_handling" is required`), - }, { Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchHeadersMatchPatternAll(ruleGroupName), Check: resource.ComposeTestCheckFunc( @@ -561,8 +466,8 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_pattern.0.all.#": "1", "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_pattern.0.included_headers.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.headers.0.match_pattern.0.excluded_headers.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", @@ -649,6 +554,7 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "1", "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.oversize_handling": "MATCH", "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_scope": "ALL", @@ -677,6 +583,7 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "1", "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.oversize_handling": "MATCH", "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_scope": "ALL", @@ -707,6 +614,7 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "1", "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.oversize_handling": "MATCH", "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_scope": "ALL", @@ -834,8 +742,8 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", @@ -2817,217 +2725,6 @@ resource "aws_wafv2_rule_group" "test" { `, name) } -func testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesInvalidConfiguration(name string) string { - return fmt.Sprintf(` -resource "aws_wafv2_rule_group" "test" { - capacity = 15 - name = "%s" - scope = "REGIONAL" - - rule { - name = "rule-1" - priority = 1 - - action { - allow {} - } - - statement { - byte_match_statement { - positional_constraint = "CONTAINS" - search_string = "word" - - field_to_match { - cookies { - match_scope = "ALL" - match_pattern { - all {} - } - } - } - - text_transformation { - priority = 1 - type = "NONE" - } - } - } - - visibility_config { - cloudwatch_metrics_enabled = false - metric_name = "friendly-rule-metric-name" - sampled_requests_enabled = false - } - } - - visibility_config { - cloudwatch_metrics_enabled = false - metric_name = "friendly-metric-name" - sampled_requests_enabled = false - } -} -`, name) -} - -func testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesMatchPatternAll(name string) string { - return fmt.Sprintf(` -resource "aws_wafv2_rule_group" "test" { - capacity = 15 - name = "%s" - scope = "REGIONAL" - - rule { - name = "rule-1" - priority = 1 - - action { - allow {} - } - - statement { - byte_match_statement { - positional_constraint = "CONTAINS" - search_string = "word" - - field_to_match { - cookies { - match_scope = "ALL" - match_pattern { - all {} - } - oversize_handling = "MATCH" - } - } - - text_transformation { - priority = 1 - type = "NONE" - } - } - } - - visibility_config { - cloudwatch_metrics_enabled = false - metric_name = "friendly-rule-metric-name" - sampled_requests_enabled = false - } - } - - visibility_config { - cloudwatch_metrics_enabled = false - metric_name = "friendly-metric-name" - sampled_requests_enabled = false - } -} -`, name) -} - -func testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesMatchPatternIncludedCookies(name string) string { - return fmt.Sprintf(` -resource "aws_wafv2_rule_group" "test" { - capacity = 50 - name = "%s" - scope = "REGIONAL" - - rule { - name = "rule-1" - priority = 1 - - action { - allow {} - } - - statement { - byte_match_statement { - positional_constraint = "CONTAINS" - search_string = "word" - - field_to_match { - cookies { - match_scope = "ALL" - match_pattern { - included_cookies = ["session", "session-id"] - } - oversize_handling = "MATCH" - } - } - - text_transformation { - priority = 1 - type = "NONE" - } - } - } - - visibility_config { - cloudwatch_metrics_enabled = false - metric_name = "friendly-rule-metric-name" - sampled_requests_enabled = false - } - } - - visibility_config { - cloudwatch_metrics_enabled = false - metric_name = "friendly-metric-name" - sampled_requests_enabled = false - } -} -`, name) -} - -func testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookiesMatchPatternExcludedCookies(name string) string { - return fmt.Sprintf(` -resource "aws_wafv2_rule_group" "test" { - capacity = 50 - name = "%s" - scope = "REGIONAL" - - rule { - name = "rule-1" - priority = 1 - - action { - allow {} - } - - statement { - byte_match_statement { - positional_constraint = "CONTAINS" - search_string = "word" - - field_to_match { - cookies { - match_scope = "ALL" - match_pattern { - excluded_cookies = ["session", "session-id"] - } - oversize_handling = "MATCH" - } - } - - text_transformation { - priority = 1 - type = "NONE" - } - } - } - - visibility_config { - cloudwatch_metrics_enabled = false - metric_name = "friendly-rule-metric-name" - sampled_requests_enabled = false - } - } - - visibility_config { - cloudwatch_metrics_enabled = false - metric_name = "friendly-metric-name" - sampled_requests_enabled = false - } -} -`, name) -} - func testAccRuleGroupConfig_byteMatchStatementFieldToMatchHeadersInvalidConfiguration(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { From 71a44ca03e2845149fd89f76664d88ba799b5de6 Mon Sep 17 00:00:00 2001 From: Scott Westover Date: Tue, 30 Aug 2022 11:41:42 -0400 Subject: [PATCH 12/52] updated linting for terraform resource blocks in waf test file Signed-off-by: Scott Westover --- internal/service/wafv2/rule_group_test.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/internal/service/wafv2/rule_group_test.go b/internal/service/wafv2/rule_group_test.go index 420826b6b26..71b54637d9d 100644 --- a/internal/service/wafv2/rule_group_test.go +++ b/internal/service/wafv2/rule_group_test.go @@ -2651,8 +2651,8 @@ resource "aws_wafv2_rule_group" "test" { field_to_match { body { - oversize_handling = "CONTINUE" - } + oversize_handling = "CONTINUE" + } } text_transformation { @@ -2749,7 +2749,7 @@ resource "aws_wafv2_rule_group" "test" { headers { match_scope = "ALL" match_pattern { - all {} + all {} } } } @@ -2801,7 +2801,7 @@ resource "aws_wafv2_rule_group" "test" { headers { match_scope = "ALL" match_pattern { - all {} + all {} } oversize_handling = "MATCH" } @@ -2854,7 +2854,7 @@ resource "aws_wafv2_rule_group" "test" { headers { match_scope = "ALL" match_pattern { - included_headers = ["session", "session-id"] + included_headers = ["session", "session-id"] } oversize_handling = "MATCH" } @@ -2907,7 +2907,7 @@ resource "aws_wafv2_rule_group" "test" { headers { match_scope = "ALL" match_pattern { - excluded_headers = ["session", "session-id"] + excluded_headers = ["session", "session-id"] } oversize_handling = "MATCH" } @@ -2959,7 +2959,7 @@ resource "aws_wafv2_rule_group" "test" { field_to_match { json_body { match_pattern { - all {} + all {} } oversize_handling = "MATCH" } @@ -3012,7 +3012,7 @@ resource "aws_wafv2_rule_group" "test" { json_body { match_scope = "ALL" match_pattern { - all {} + all {} } oversize_handling = "MATCH" } @@ -3065,7 +3065,7 @@ resource "aws_wafv2_rule_group" "test" { json_body { match_scope = "ALL" match_pattern { - included_paths = ["/dogs/0/name", "/cats"] + included_paths = ["/dogs/0/name", "/cats"] } oversize_handling = "MATCH" } @@ -3119,7 +3119,7 @@ resource "aws_wafv2_rule_group" "test" { invalid_fallback_behavior = "NO_MATCH" match_scope = "ALL" match_pattern { - all {} + all {} } oversize_handling = "MATCH" } From 447371a07762c2df3a89158884ea703c5ca3c789 Mon Sep 17 00:00:00 2001 From: Scott Westover Date: Tue, 30 Aug 2022 11:52:46 -0400 Subject: [PATCH 13/52] updated linting for terraform resource blocks in waf test file Signed-off-by: Scott Westover --- internal/service/wafv2/rule_group_test.go | 64 +++++++++++------------ 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/internal/service/wafv2/rule_group_test.go b/internal/service/wafv2/rule_group_test.go index 71b54637d9d..5bbf568b300 100644 --- a/internal/service/wafv2/rule_group_test.go +++ b/internal/service/wafv2/rule_group_test.go @@ -2747,11 +2747,11 @@ resource "aws_wafv2_rule_group" "test" { field_to_match { headers { - match_scope = "ALL" + match_scope = "ALL" match_pattern { - all {} - } - } + all {} + } + } } text_transformation { @@ -2799,12 +2799,12 @@ resource "aws_wafv2_rule_group" "test" { field_to_match { headers { - match_scope = "ALL" + match_scope = "ALL" match_pattern { - all {} - } + all {} + } oversize_handling = "MATCH" - } + } } text_transformation { @@ -2852,12 +2852,12 @@ resource "aws_wafv2_rule_group" "test" { field_to_match { headers { - match_scope = "ALL" + match_scope = "ALL" match_pattern { - included_headers = ["session", "session-id"] - } + included_headers = ["session", "session-id"] + } oversize_handling = "MATCH" - } + } } text_transformation { @@ -2905,12 +2905,12 @@ resource "aws_wafv2_rule_group" "test" { field_to_match { headers { - match_scope = "ALL" + match_scope = "ALL" match_pattern { - excluded_headers = ["session", "session-id"] - } + excluded_headers = ["session", "session-id"] + } oversize_handling = "MATCH" - } + } } text_transformation { @@ -2959,10 +2959,10 @@ resource "aws_wafv2_rule_group" "test" { field_to_match { json_body { match_pattern { - all {} - } + all {} + } oversize_handling = "MATCH" - } + } } text_transformation { @@ -3010,12 +3010,12 @@ resource "aws_wafv2_rule_group" "test" { field_to_match { json_body { - match_scope = "ALL" + match_scope = "ALL" match_pattern { - all {} - } + all {} + } oversize_handling = "MATCH" - } + } } text_transformation { @@ -3063,12 +3063,12 @@ resource "aws_wafv2_rule_group" "test" { field_to_match { json_body { - match_scope = "ALL" + match_scope = "ALL" match_pattern { - included_paths = ["/dogs/0/name", "/cats"] - } + included_paths = ["/dogs/0/name", "/cats"] + } oversize_handling = "MATCH" - } + } } text_transformation { @@ -3116,13 +3116,13 @@ resource "aws_wafv2_rule_group" "test" { field_to_match { json_body { - invalid_fallback_behavior = "NO_MATCH" - match_scope = "ALL" + invalid_fallback_behavior = "NO_MATCH" + match_scope = "ALL" match_pattern { - all {} - } + all {} + } oversize_handling = "MATCH" - } + } } text_transformation { From 1ca4d3899b1256ce9885d42c2a53b8495de389dd Mon Sep 17 00:00:00 2001 From: Scott Westover Date: Tue, 30 Aug 2022 11:55:29 -0400 Subject: [PATCH 14/52] updated linting for terraform resource blocks in waf test file Signed-off-by: Scott Westover --- internal/service/wafv2/rule_group_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/wafv2/rule_group_test.go b/internal/service/wafv2/rule_group_test.go index 5bbf568b300..c01139c791e 100644 --- a/internal/service/wafv2/rule_group_test.go +++ b/internal/service/wafv2/rule_group_test.go @@ -3117,7 +3117,7 @@ resource "aws_wafv2_rule_group" "test" { field_to_match { json_body { invalid_fallback_behavior = "NO_MATCH" - match_scope = "ALL" + match_scope = "ALL" match_pattern { all {} } From 5aad6e6fa94fc3212c56ab5e11b9bdb810d46607 Mon Sep 17 00:00:00 2001 From: Scott Westover Date: Tue, 30 Aug 2022 12:00:14 -0400 Subject: [PATCH 15/52] updated linting for terraform resource blocks in waf test file Signed-off-by: Scott Westover --- internal/service/wafv2/rule_group_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/wafv2/rule_group_test.go b/internal/service/wafv2/rule_group_test.go index c01139c791e..ef1ac3a0697 100644 --- a/internal/service/wafv2/rule_group_test.go +++ b/internal/service/wafv2/rule_group_test.go @@ -3117,7 +3117,7 @@ resource "aws_wafv2_rule_group" "test" { field_to_match { json_body { invalid_fallback_behavior = "NO_MATCH" - match_scope = "ALL" + match_scope = "ALL" match_pattern { all {} } From 20e2fdfc55d209c1e92cfd8eebf462a2cd0badab Mon Sep 17 00:00:00 2001 From: Scott Westover Date: Tue, 30 Aug 2022 12:15:03 -0400 Subject: [PATCH 16/52] fixed: function names for wafv2 flex files Signed-off-by: Scott Westover --- internal/service/wafv2/flex.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/service/wafv2/flex.go b/internal/service/wafv2/flex.go index 01c142a633b..ec9de473fc7 100644 --- a/internal/service/wafv2/flex.go +++ b/internal/service/wafv2/flex.go @@ -404,7 +404,7 @@ func expandFieldToMatch(l []interface{}) *wafv2.FieldToMatch { } if v, ok := m["json_body"]; ok && len(v.([]interface{})) > 0 { - f.JsonBody = expandJsonBody(m["json_body"].([]interface{})) + f.JsonBody = expandJSONBody(m["json_body"].([]interface{})) } if v, ok := m["method"]; ok && len(v.([]interface{})) > 0 { @@ -737,14 +737,14 @@ func expandHeaderMatchPattern(l []interface{}) *wafv2.HeaderMatchPattern { return f } -func expandJsonBody(l []interface{}) *wafv2.JsonBody { +func expandJSONBody(l []interface{}) *wafv2.JsonBody { if len(l) == 0 || l[0] == nil { return nil } m := l[0].(map[string]interface{}) f := &wafv2.JsonBody{ - MatchPattern: expandJsonMatchPattern(m["match_pattern"].([]interface{})), + MatchPattern: expandJSONMatchPattern(m["match_pattern"].([]interface{})), MatchScope: aws.String(m["match_scope"].(string)), OversizeHandling: aws.String(m["oversize_handling"].(string)), } @@ -756,7 +756,7 @@ func expandJsonBody(l []interface{}) *wafv2.JsonBody { return f } -func expandJsonMatchPattern(l []interface{}) *wafv2.JsonMatchPattern { +func expandJSONMatchPattern(l []interface{}) *wafv2.JsonMatchPattern { if len(l) == 0 || l[0] == nil { return nil } @@ -1078,7 +1078,7 @@ func flattenFieldToMatch(f *wafv2.FieldToMatch) interface{} { } if f.JsonBody != nil { - m["json_body"] = flattenJsonBody(f.JsonBody) + m["json_body"] = flattenJSONBody(f.JsonBody) } if f.Method != nil { @@ -1374,14 +1374,14 @@ func flattenHeaderMatchPattern(s *wafv2.HeaderMatchPattern) interface{} { return []interface{}{m} } -func flattenJsonBody(s *wafv2.JsonBody) interface{} { +func flattenJSONBody(s *wafv2.JsonBody) interface{} { if s == nil { return []interface{}{} } m := map[string]interface{}{ "match_scope": aws.StringValue(s.MatchScope), - "match_pattern": flattenJsonMatchPattern(s.MatchPattern), + "match_pattern": flattenJSONMatchPattern(s.MatchPattern), "oversize_handling": aws.StringValue(s.OversizeHandling), } @@ -1392,7 +1392,7 @@ func flattenJsonBody(s *wafv2.JsonBody) interface{} { return []interface{}{m} } -func flattenJsonMatchPattern(s *wafv2.JsonMatchPattern) interface{} { +func flattenJSONMatchPattern(s *wafv2.JsonMatchPattern) interface{} { if s == nil { return []interface{}{} } From a25241d2acbaa196fd3d19a1ca61c486f4123467 Mon Sep 17 00:00:00 2001 From: Scott Westover Date: Tue, 30 Aug 2022 12:28:32 -0400 Subject: [PATCH 17/52] f/fixed json naming convention in test files for wafv2 Signed-off-by: Scott Westover --- internal/service/wafv2/rule_group_test.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/service/wafv2/rule_group_test.go b/internal/service/wafv2/rule_group_test.go index ef1ac3a0697..7e1bb4f0d85 100644 --- a/internal/service/wafv2/rule_group_test.go +++ b/internal/service/wafv2/rule_group_test.go @@ -381,7 +381,7 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0._body.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", @@ -542,7 +542,7 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { ExpectError: regexp.MustCompile(`argument "oversize_handling" is required`), }, { - Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchJsonBodyMatchPatternAll(ruleGroupName), + Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchJSONBodyMatchPatternAll(ruleGroupName), Check: resource.ComposeTestCheckFunc( testAccCheckRuleGroupExists(resourceName, &v), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), @@ -571,7 +571,7 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { ), }, { - Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchJsonBodyMatchPatternIncludedPaths(ruleGroupName), + Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchJSONBodyMatchPatternIncludedPaths(ruleGroupName), Check: resource.ComposeTestCheckFunc( testAccCheckRuleGroupExists(resourceName, &v), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), @@ -602,7 +602,7 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { ), }, { - Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchJsonBodyInvalidFallbackBehavior(ruleGroupName), + Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchJSONBodyInvalidFallbackBehavior(ruleGroupName), Check: resource.ComposeTestCheckFunc( testAccCheckRuleGroupExists(resourceName, &v), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), @@ -631,7 +631,7 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { ), }, { - Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchJsonBodyMatchInvalidConfiguration(ruleGroupName), + Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchJSONBodyMatchInvalidConfiguration(ruleGroupName), ExpectError: regexp.MustCompile(`argument "match_scope" is required`), }, { @@ -2936,7 +2936,7 @@ resource "aws_wafv2_rule_group" "test" { `, name) } -func testAccRuleGroupConfig_byteMatchStatementFieldToMatchJsonBodyMatchInvalidConfiguration(name string) string { +func testAccRuleGroupConfig_byteMatchStatementFieldToMatchJSONBodyMatchInvalidConfiguration(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { capacity = 50 @@ -2988,7 +2988,7 @@ resource "aws_wafv2_rule_group" "test" { `, name) } -func testAccRuleGroupConfig_byteMatchStatementFieldToMatchJsonBodyMatchPatternAll(name string) string { +func testAccRuleGroupConfig_byteMatchStatementFieldToMatchJSONBodyMatchPatternAll(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { capacity = 50 @@ -3041,7 +3041,7 @@ resource "aws_wafv2_rule_group" "test" { `, name) } -func testAccRuleGroupConfig_byteMatchStatementFieldToMatchJsonBodyMatchPatternIncludedPaths(name string) string { +func testAccRuleGroupConfig_byteMatchStatementFieldToMatchJSONBodyMatchPatternIncludedPaths(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { capacity = 50 @@ -3094,7 +3094,7 @@ resource "aws_wafv2_rule_group" "test" { `, name) } -func testAccRuleGroupConfig_byteMatchStatementFieldToMatchJsonBodyInvalidFallbackBehavior(name string) string { +func testAccRuleGroupConfig_byteMatchStatementFieldToMatchJSONBodyInvalidFallbackBehavior(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { capacity = 50 From fcb4587914440b29fa9cf389529d5bcb6116c298 Mon Sep 17 00:00:00 2001 From: Scott Westover Date: Tue, 30 Aug 2022 21:16:01 -0400 Subject: [PATCH 18/52] f/fixed conflicts Signed-off-by: Scott Westover --- .changelog/24772.txt | 7 + internal/service/wafv2/flex.go | 151 +++++---- internal/service/wafv2/rule_group_test.go | 312 +++--------------- internal/service/wafv2/schemas.go | 95 +++--- internal/service/wafv2/web_acl_test.go | 225 +++++++++++++ .../docs/d/ecs_task_definition.html.markdown | 2 +- .../docs/r/autoscaling_group.html.markdown | 2 +- website/docs/r/eks_node_group.html.markdown | 4 +- website/docs/r/wafv2_rule_group.html.markdown | 29 +- website/docs/r/wafv2_web_acl.html.markdown | 29 +- 10 files changed, 418 insertions(+), 438 deletions(-) create mode 100644 .changelog/24772.txt diff --git a/.changelog/24772.txt b/.changelog/24772.txt new file mode 100644 index 00000000000..18d0701076d --- /dev/null +++ b/.changelog/24772.txt @@ -0,0 +1,7 @@ +```release-note:enhancement +resource/aws_wafv2_rule_group: Add `json_body` attribute to the `field_to_match` block +``` + +```release-note:enhancement +resource/aws_wafv2_web_acl: Add `json_body` attribute to the `field_to_match` block +``` \ No newline at end of file diff --git a/internal/service/wafv2/flex.go b/internal/service/wafv2/flex.go index ec9de473fc7..669fb947ef0 100644 --- a/internal/service/wafv2/flex.go +++ b/internal/service/wafv2/flex.go @@ -404,7 +404,7 @@ func expandFieldToMatch(l []interface{}) *wafv2.FieldToMatch { } if v, ok := m["json_body"]; ok && len(v.([]interface{})) > 0 { - f.JsonBody = expandJSONBody(m["json_body"].([]interface{})) + f.JsonBody = expandJSONBody(v.([]interface{})) } if v, ok := m["method"]; ok && len(v.([]interface{})) > 0 { @@ -499,6 +499,45 @@ func expandCookieMatchPattern(l []interface{}) *wafv2.CookieMatchPattern { return CookieMatchPattern } +func expandJSONBody(l []interface{}) *wafv2.JsonBody { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + jsonBody := &wafv2.JsonBody{ + MatchScope: aws.String(m["match_scope"].(string)), + OversizeHandling: aws.String(m["oversize_handling"].(string)), + MatchPattern: expandJSONMatchPattern(m["match_pattern"].([]interface{})), + } + + if v, ok := m["invalid_fallback_behavior"].(string); ok && v != "" { + jsonBody.InvalidFallbackBehavior = aws.String(v) + } + + return jsonBody +} + +func expandJSONMatchPattern(l []interface{}) *wafv2.JsonMatchPattern { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + jsonMatchPattern := &wafv2.JsonMatchPattern{} + + if v, ok := m["all"].([]interface{}); ok && len(v) > 0 { + jsonMatchPattern.All = &wafv2.All{} + } + + if v, ok := m["included_paths"]; ok && len(v.([]interface{})) > 0 { + jsonMatchPattern.IncludedPaths = flex.ExpandStringList(v.([]interface{})) + } + + return jsonMatchPattern +} + func expandSingleHeader(l []interface{}) *wafv2.SingleHeader { if len(l) == 0 || l[0] == nil { return nil @@ -737,44 +776,6 @@ func expandHeaderMatchPattern(l []interface{}) *wafv2.HeaderMatchPattern { return f } -func expandJSONBody(l []interface{}) *wafv2.JsonBody { - if len(l) == 0 || l[0] == nil { - return nil - } - - m := l[0].(map[string]interface{}) - f := &wafv2.JsonBody{ - MatchPattern: expandJSONMatchPattern(m["match_pattern"].([]interface{})), - MatchScope: aws.String(m["match_scope"].(string)), - OversizeHandling: aws.String(m["oversize_handling"].(string)), - } - - if v, ok := m["invalid_fallback_behavior"]; ok && len(v.(string)) > 0 { - f.InvalidFallbackBehavior = aws.String(m["invalid_fallback_behavior"].(string)) - } - - return f -} - -func expandJSONMatchPattern(l []interface{}) *wafv2.JsonMatchPattern { - if len(l) == 0 || l[0] == nil { - return nil - } - - m := l[0].(map[string]interface{}) - f := &wafv2.JsonMatchPattern{} - - if v, ok := m["all"]; ok && len(v.([]interface{})) > 0 { - f.All = &wafv2.All{} - } - - if v, ok := m["included_paths"]; ok && len(v.([]interface{})) > 0 { - f.IncludedPaths = flex.ExpandStringList(m["included_paths"].([]interface{})) - } - - return f -} - func flattenRules(r []*wafv2.Rule) interface{} { out := make([]map[string]interface{}, len(r)) for i, rule := range r { @@ -1151,11 +1152,45 @@ func flattenCookiesMatchPattern(c *wafv2.CookieMatchPattern) interface{} { } m := map[string]interface{}{ - "all": c.All, "included_cookies": aws.StringValueSlice(c.IncludedCookies), "excluded_cookies": aws.StringValueSlice(c.ExcludedCookies), } + if c.All != nil { + m["all"] = make([]map[string]interface{}, 1) + } + + return []interface{}{m} +} + +func flattenJSONBody(b *wafv2.JsonBody) interface{} { + if b == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "invalid_fallback_behavior": aws.StringValue(b.InvalidFallbackBehavior), + "match_pattern": flattenJSONMatchPattern(b.MatchPattern), + "match_scope": aws.StringValue(b.MatchScope), + "oversize_handling": aws.StringValue(b.OversizeHandling), + } + + return []interface{}{m} +} + +func flattenJSONMatchPattern(p *wafv2.JsonMatchPattern) []interface{} { + if p == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "included_paths": flex.FlattenStringList(p.IncludedPaths), + } + + if p.All != nil { + m["all"] = make([]map[string]interface{}, 1) + } + return []interface{}{m} } @@ -1373,39 +1408,3 @@ func flattenHeaderMatchPattern(s *wafv2.HeaderMatchPattern) interface{} { return []interface{}{m} } - -func flattenJSONBody(s *wafv2.JsonBody) interface{} { - if s == nil { - return []interface{}{} - } - - m := map[string]interface{}{ - "match_scope": aws.StringValue(s.MatchScope), - "match_pattern": flattenJSONMatchPattern(s.MatchPattern), - "oversize_handling": aws.StringValue(s.OversizeHandling), - } - - if s.InvalidFallbackBehavior != nil { - m["invalid_fallback_behavior"] = aws.StringValue(s.InvalidFallbackBehavior) - } - - return []interface{}{m} -} - -func flattenJSONMatchPattern(s *wafv2.JsonMatchPattern) interface{} { - if s == nil { - return []interface{}{} - } - - m := map[string]interface{}{} - - if s.All != nil { - m["all"] = make([]map[string]interface{}, 1) - } - - if s.IncludedPaths != nil { - m["included_paths"] = flex.FlattenStringList(s.IncludedPaths) - } - - return []interface{}{m} -} diff --git a/internal/service/wafv2/rule_group_test.go b/internal/service/wafv2/rule_group_test.go index 7e1bb4f0d85..df84da51953 100644 --- a/internal/service/wafv2/rule_group_test.go +++ b/internal/service/wafv2/rule_group_test.go @@ -381,7 +381,7 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0._body.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", @@ -446,6 +446,27 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { }), ), }, + { + Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchJSONBody(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRuleGroupExists(resourceName, &v), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "statement.#": "1", + "statement.0.byte_match_statement.#": "1", + "statement.0.byte_match_statement.0.field_to_match.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_scope": "VALUE", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.invalid_fallback_behavior": "MATCH", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.oversize_handling": "CONTINUE", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.0.included_paths.#": "2", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.0.included_paths.0": "/dogs/0/name", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.0.included_paths.1": "/dogs/1/name", + }), + ), + }, { Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchHeadersMatchPatternAll(ruleGroupName), Check: resource.ComposeTestCheckFunc( @@ -541,99 +562,6 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchHeadersInvalidConfiguration(ruleGroupName), ExpectError: regexp.MustCompile(`argument "oversize_handling" is required`), }, - { - Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchJSONBodyMatchPatternAll(ruleGroupName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName, &v), - acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), - resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), - resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ - "statement.#": "1", - "statement.0.byte_match_statement.#": "1", - "statement.0.byte_match_statement.0.field_to_match.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.oversize_handling": "MATCH", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_scope": "ALL", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.0.all.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.0.included_paths.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.invalid_fallback_behavior": "", - "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", - }), - ), - }, - { - Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchJSONBodyMatchPatternIncludedPaths(ruleGroupName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName, &v), - acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), - resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), - resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ - "statement.#": "1", - "statement.0.byte_match_statement.#": "1", - "statement.0.byte_match_statement.0.field_to_match.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.oversize_handling": "MATCH", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_scope": "ALL", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.0.all.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.0.included_paths.#": "2", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.0.included_paths.0": "/dogs/0/name", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.0.included_paths.1": "/cats", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.invalid_fallback_behavior": "", - "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", - }), - ), - }, - { - Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchJSONBodyInvalidFallbackBehavior(ruleGroupName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName, &v), - acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), - resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), - resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ - "statement.#": "1", - "statement.0.byte_match_statement.#": "1", - "statement.0.byte_match_statement.0.field_to_match.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.body.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.oversize_handling": "MATCH", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_scope": "ALL", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.0.all.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.0.included_paths.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.invalid_fallback_behavior": "NO_MATCH", - "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", - }), - ), - }, - { - Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchJSONBodyMatchInvalidConfiguration(ruleGroupName), - ExpectError: regexp.MustCompile(`argument "match_scope" is required`), - }, { Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchMethod(ruleGroupName), Check: resource.ComposeTestCheckFunc( @@ -2678,7 +2606,7 @@ resource "aws_wafv2_rule_group" "test" { `, name) } -func testAccRuleGroupConfig_byteMatchStatementFieldToMatchBodyInvalidConfiguration(name string) string { +func testAccRuleGroupConfig_byteMatchStatementFieldToMatchJSONBody(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { capacity = 15 @@ -2696,60 +2624,15 @@ resource "aws_wafv2_rule_group" "test" { statement { byte_match_statement { positional_constraint = "CONTAINS" - search_string = "word" - - field_to_match { - body {} - } - - text_transformation { - priority = 1 - type = "NONE" - } - } - } - - visibility_config { - cloudwatch_metrics_enabled = false - metric_name = "friendly-rule-metric-name" - sampled_requests_enabled = false - } - } - - visibility_config { - cloudwatch_metrics_enabled = false - metric_name = "friendly-metric-name" - sampled_requests_enabled = false - } -} -`, name) -} - -func testAccRuleGroupConfig_byteMatchStatementFieldToMatchHeadersInvalidConfiguration(name string) string { - return fmt.Sprintf(` -resource "aws_wafv2_rule_group" "test" { - capacity = 50 - name = "%s" - scope = "REGIONAL" - - rule { - name = "rule-1" - priority = 1 - - action { - allow {} - } - - statement { - byte_match_statement { - positional_constraint = "CONTAINS" - search_string = "word" + search_string = "Clifford" field_to_match { - headers { - match_scope = "ALL" + json_body { + match_scope = "VALUE" + invalid_fallback_behavior = "MATCH" + oversize_handling = "CONTINUE" match_pattern { - all {} + included_paths = ["/dogs/0/name", "/dogs/1/name"] } } } @@ -2777,10 +2660,10 @@ resource "aws_wafv2_rule_group" "test" { `, name) } -func testAccRuleGroupConfig_byteMatchStatementFieldToMatchHeadersMatchPatternAll(name string) string { +func testAccRuleGroupConfig_byteMatchStatementFieldToMatchBodyInvalidConfiguration(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { - capacity = 50 + capacity = 15 name = "%s" scope = "REGIONAL" @@ -2798,13 +2681,7 @@ resource "aws_wafv2_rule_group" "test" { search_string = "word" field_to_match { - headers { - match_scope = "ALL" - match_pattern { - all {} - } - oversize_handling = "MATCH" - } + body {} } text_transformation { @@ -2830,7 +2707,7 @@ resource "aws_wafv2_rule_group" "test" { `, name) } -func testAccRuleGroupConfig_byteMatchStatementFieldToMatchHeadersMatchPatternIncludedHeaders(name string) string { +func testAccRuleGroupConfig_byteMatchStatementFieldToMatchHeadersInvalidConfiguration(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { capacity = 50 @@ -2854,9 +2731,8 @@ resource "aws_wafv2_rule_group" "test" { headers { match_scope = "ALL" match_pattern { - included_headers = ["session", "session-id"] + all {} } - oversize_handling = "MATCH" } } @@ -2883,7 +2759,7 @@ resource "aws_wafv2_rule_group" "test" { `, name) } -func testAccRuleGroupConfig_byteMatchStatementFieldToMatchHeadersMatchPatternExcludedHeaders(name string) string { +func testAccRuleGroupConfig_byteMatchStatementFieldToMatchHeadersMatchPatternAll(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { capacity = 50 @@ -2906,58 +2782,6 @@ resource "aws_wafv2_rule_group" "test" { field_to_match { headers { match_scope = "ALL" - match_pattern { - excluded_headers = ["session", "session-id"] - } - oversize_handling = "MATCH" - } - } - - text_transformation { - priority = 1 - type = "NONE" - } - } - } - - visibility_config { - cloudwatch_metrics_enabled = false - metric_name = "friendly-rule-metric-name" - sampled_requests_enabled = false - } - } - - visibility_config { - cloudwatch_metrics_enabled = false - metric_name = "friendly-metric-name" - sampled_requests_enabled = false - } -} -`, name) -} - -func testAccRuleGroupConfig_byteMatchStatementFieldToMatchJSONBodyMatchInvalidConfiguration(name string) string { - return fmt.Sprintf(` -resource "aws_wafv2_rule_group" "test" { - capacity = 50 - name = "%s" - scope = "REGIONAL" - - rule { - name = "rule-1" - priority = 1 - - action { - allow {} - } - - statement { - byte_match_statement { - positional_constraint = "CONTAINS" - search_string = "word" - - field_to_match { - json_body { match_pattern { all {} } @@ -2988,7 +2812,7 @@ resource "aws_wafv2_rule_group" "test" { `, name) } -func testAccRuleGroupConfig_byteMatchStatementFieldToMatchJSONBodyMatchPatternAll(name string) string { +func testAccRuleGroupConfig_byteMatchStatementFieldToMatchHeadersMatchPatternIncludedHeaders(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { capacity = 50 @@ -3009,10 +2833,10 @@ resource "aws_wafv2_rule_group" "test" { search_string = "word" field_to_match { - json_body { + headers { match_scope = "ALL" match_pattern { - all {} + included_headers = ["session", "session-id"] } oversize_handling = "MATCH" } @@ -3041,7 +2865,7 @@ resource "aws_wafv2_rule_group" "test" { `, name) } -func testAccRuleGroupConfig_byteMatchStatementFieldToMatchJSONBodyMatchPatternIncludedPaths(name string) string { +func testAccRuleGroupConfig_byteMatchStatementFieldToMatchHeadersMatchPatternExcludedHeaders(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { capacity = 50 @@ -3062,64 +2886,10 @@ resource "aws_wafv2_rule_group" "test" { search_string = "word" field_to_match { - json_body { + headers { match_scope = "ALL" match_pattern { - included_paths = ["/dogs/0/name", "/cats"] - } - oversize_handling = "MATCH" - } - } - - text_transformation { - priority = 1 - type = "NONE" - } - } - } - - visibility_config { - cloudwatch_metrics_enabled = false - metric_name = "friendly-rule-metric-name" - sampled_requests_enabled = false - } - } - - visibility_config { - cloudwatch_metrics_enabled = false - metric_name = "friendly-metric-name" - sampled_requests_enabled = false - } -} -`, name) -} - -func testAccRuleGroupConfig_byteMatchStatementFieldToMatchJSONBodyInvalidFallbackBehavior(name string) string { - return fmt.Sprintf(` -resource "aws_wafv2_rule_group" "test" { - capacity = 50 - name = "%s" - scope = "REGIONAL" - - rule { - name = "rule-1" - priority = 1 - - action { - allow {} - } - - statement { - byte_match_statement { - positional_constraint = "CONTAINS" - search_string = "word" - - field_to_match { - json_body { - invalid_fallback_behavior = "NO_MATCH" - match_scope = "ALL" - match_pattern { - all {} + excluded_headers = ["session", "session-id"] } oversize_handling = "MATCH" } diff --git a/internal/service/wafv2/schemas.go b/internal/service/wafv2/schemas.go index 0c817f8bcf4..1dd52da4e47 100644 --- a/internal/service/wafv2/schemas.go +++ b/internal/service/wafv2/schemas.go @@ -329,6 +329,7 @@ func xssMatchStatementSchema() *schema.Schema { }, } } + func fieldToMatchBaseSchema() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -420,6 +421,59 @@ func fieldToMatchSchema() *schema.Schema { } } +func jsonBodySchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "invalid_fallback_behavior": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(wafv2.BodyParsingFallbackBehavior_Values(), false), + }, + "match_pattern": jsonMatchPattern(), + "match_scope": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(wafv2.JsonMatchScope_Values(), false), + }, + "oversize_handling": { + Type: schema.TypeString, + Optional: true, + Default: wafv2.OversizeHandlingContinue, + ValidateFunc: validation.StringInSlice(wafv2.OversizeHandling_Values(), false), + }, + }, + }, + } +} + +func jsonMatchPattern() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "all": emptySchema(), + "included_paths": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 512), + validation.StringMatch(regexp.MustCompile(`(/)|(/(([^~])|(~[01]))+)`), "must be a valid JSON pointer")), + }, + }, + }, + }, + } +} + func forwardedIPConfig() *schema.Schema { return &schema.Schema{ Type: schema.TypeList, @@ -719,44 +773,3 @@ func headersMatchPatternBaseSchema() *schema.Schema { }, } } - -func jsonBodySchema() *schema.Schema { - return &schema.Schema{ - Type: schema.TypeList, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "invalid_fallback_behavior": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice(wafv2.FallbackBehavior_Values(), false), - }, - "match_pattern": { - Type: schema.TypeList, - Required: true, - MaxItems: 1, - MinItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "all": emptySchema(), - "included_paths": { - Type: schema.TypeList, - Optional: true, - MinItems: 1, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validation.All( - validation.StringLenBetween(1, 512), - validation.StringMatch(regexp.MustCompile(`([/])|([/](([^~])|(~[01]))+)`), ""), - ), - }, - }, - }, - }, - }, - "match_scope": matchScopeSchema(), - "oversize_handling": oversizeHandlingSchema(), - }, - }, - } -} diff --git a/internal/service/wafv2/web_acl_test.go b/internal/service/wafv2/web_acl_test.go index 2c91a0f9979..300b7f3f6a7 100644 --- a/internal/service/wafv2/web_acl_test.go +++ b/internal/service/wafv2/web_acl_test.go @@ -737,6 +737,126 @@ func TestAccWAFV2WebACL_RateBased_basic(t *testing.T) { }) } +func TestAccWAFV2WebACL_ByteMatchStatement_basic(t *testing.T) { + var v wafv2.WebACL + webACLName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_wafv2_web_acl.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); testAccPreCheckScopeRegional(t) }, + ErrorCheck: acctest.ErrorCheck(t, wafv2.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckWebACLDestroy, + Steps: []resource.TestStep{ + { + Config: testAccWebACLConfig_byteMatchStatement(webACLName, wafv2.PositionalConstraintContainsWord, "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckWebACLExists(resourceName, &v), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), + resource.TestCheckResourceAttr(resourceName, "name", webACLName), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "statement.#": "1", + "statement.0.byte_match_statement.#": "1", + "statement.0.byte_match_statement.0.field_to_match.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "1", + "statement.0.byte_match_statement.0.positional_constraint": "CONTAINS_WORD", + "statement.0.byte_match_statement.0.search_string": "value1", + "statement.0.byte_match_statement.0.text_transformation.#": "1", + "statement.0.byte_match_statement.0.text_transformation.0.priority": "0", + "statement.0.byte_match_statement.0.text_transformation.0.type": "NONE", + }), + ), + }, + { + Config: testAccWebACLConfig_byteMatchStatement(webACLName, wafv2.PositionalConstraintExactly, "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckWebACLExists(resourceName, &v), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), + resource.TestCheckResourceAttr(resourceName, "name", webACLName), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "statement.#": "1", + "statement.0.byte_match_statement.#": "1", + "statement.0.byte_match_statement.0.field_to_match.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "1", + "statement.0.byte_match_statement.0.positional_constraint": "EXACTLY", + "statement.0.byte_match_statement.0.search_string": "value2", + "statement.0.byte_match_statement.0.text_transformation.#": "1", + "statement.0.byte_match_statement.0.text_transformation.0.priority": "0", + "statement.0.byte_match_statement.0.text_transformation.0.type": "NONE", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccWebACLImportStateIdFunc(resourceName), + }, + }, + }) +} + +func TestAccWAFV2WebACL_ByteMatchStatement_jsonBody(t *testing.T) { + var v wafv2.WebACL + webACLName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_wafv2_web_acl.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); testAccPreCheckScopeRegional(t) }, + ErrorCheck: acctest.ErrorCheck(t, wafv2.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckWebACLDestroy, + Steps: []resource.TestStep{ + { + Config: testAccWebACLConfig_byteMatchStatementJSONBody(webACLName, wafv2.JsonMatchScopeValue, wafv2.FallbackBehaviorMatch, wafv2.OversizeHandlingNoMatch, `included_paths = ["/dogs/0/name", "/dogs/1/name"]`), + Check: resource.ComposeTestCheckFunc( + testAccCheckWebACLExists(resourceName, &v), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), + resource.TestCheckResourceAttr(resourceName, "name", webACLName), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_scope": "VALUE", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.invalid_fallback_behavior": "MATCH", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.oversize_handling": "NO_MATCH", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.0.all.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.0.included_paths.#": "2", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.0.included_paths.0": "/dogs/0/name", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.0.included_paths.1": "/dogs/1/name", + }), + ), + }, + { + Config: testAccWebACLConfig_byteMatchStatementJSONBody(webACLName, wafv2.JsonMatchScopeAll, wafv2.FallbackBehaviorNoMatch, wafv2.OversizeHandlingContinue, "all {}"), + Check: resource.ComposeTestCheckFunc( + testAccCheckWebACLExists(resourceName, &v), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), + resource.TestCheckResourceAttr(resourceName, "name", webACLName), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_scope": "ALL", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.invalid_fallback_behavior": "NO_MATCH", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.oversize_handling": "CONTINUE", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.0.all.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.0.match_pattern.0.included_paths.#": "0", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccWebACLImportStateIdFunc(resourceName), + }, + }, + }) +} + func TestAccWAFV2WebACL_GeoMatch_basic(t *testing.T) { var v wafv2.WebACL webACLName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -1870,6 +1990,111 @@ resource "aws_wafv2_web_acl" "test" { `, name, ruleName1, priority1, ruleName2, priority2) } +func testAccWebACLConfig_byteMatchStatement(name, positionalConstraint, searchString string) string { + return fmt.Sprintf(` +resource "aws_wafv2_web_acl" "test" { + name = "%[1]s" + description = "%[1]s" + scope = "REGIONAL" + + default_action { + allow {} + } + + rule { + name = "rule-1" + priority = 1 + + action { + count {} + } + + statement { + byte_match_statement { + field_to_match { + all_query_arguments {} + } + positional_constraint = "%[2]s" + search_string = "%[3]s" + text_transformation { + priority = 0 + type = "NONE" + } + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-rule-metric-name" + sampled_requests_enabled = false + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-metric-name" + sampled_requests_enabled = false + } +} +`, name, positionalConstraint, searchString) +} + +func testAccWebACLConfig_byteMatchStatementJSONBody(name, matchScope, invalidFallbackBehavior, oversizeHandling, matchPattern string) string { + return fmt.Sprintf(` +resource "aws_wafv2_web_acl" "test" { + name = "%[1]s" + description = "%[1]s" + scope = "REGIONAL" + + default_action { + allow {} + } + + rule { + name = "rule-1" + priority = 1 + + action { + count {} + } + + statement { + byte_match_statement { + field_to_match { + json_body { + match_scope = "%[2]s" + invalid_fallback_behavior = "%[3]s" + oversize_handling = "%[4]s" + match_pattern { + %[5]s + } + } + } + positional_constraint = "CONTAINS_WORD" + search_string = "Buddy" + text_transformation { + priority = 0 + type = "NONE" + } + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-rule-metric-name" + sampled_requests_enabled = false + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-metric-name" + sampled_requests_enabled = false + } +} +`, name, matchScope, invalidFallbackBehavior, oversizeHandling, matchPattern) +} + func testAccWebACLConfig_geoMatchStatement(name, countryCodes string) string { return fmt.Sprintf(` resource "aws_wafv2_web_acl" "test" { diff --git a/website/docs/d/ecs_task_definition.html.markdown b/website/docs/d/ecs_task_definition.html.markdown index 868c2a170f8..8ebc57f36ef 100644 --- a/website/docs/d/ecs_task_definition.html.markdown +++ b/website/docs/d/ecs_task_definition.html.markdown @@ -51,7 +51,7 @@ resource "aws_ecs_service" "mongo" { desired_count = 2 # Track the latest ACTIVE revision - task_definition = aws_ecs_task_definition.mongo.arn + task_definition = data.aws_ecs_task_definition.mongo.arn } ``` diff --git a/website/docs/r/autoscaling_group.html.markdown b/website/docs/r/autoscaling_group.html.markdown index 3183afffbeb..ffba6cc6514 100644 --- a/website/docs/r/autoscaling_group.html.markdown +++ b/website/docs/r/autoscaling_group.html.markdown @@ -414,7 +414,7 @@ The following arguments are supported: group names. Only valid for classic load balancers. For ALBs, use `target_group_arns` instead. * `vpc_zone_identifier` (Optional) A list of subnet IDs to launch resources in. Subnets automatically determine which availability zones the group will reside. Conflicts with `availability_zones`. * `target_group_arns` (Optional) A set of `aws_alb_target_group` ARNs, for use with Application or Network Load Balancing. -* `termination_policies` (Optional) A list of policies to decide how the instances in the Auto Scaling Group should be terminated. The allowed values are `OldestInstance`, `NewestInstance`, `OldestLaunchConfiguration`, `ClosestToNextInstanceHour`, `OldestLaunchTemplate`, `AllocationStrategy`, `Default`. +* `termination_policies` (Optional) A list of policies to decide how the instances in the Auto Scaling Group should be terminated. The allowed values are `OldestInstance`, `NewestInstance`, `OldestLaunchConfiguration`, `ClosestToNextInstanceHour`, `OldestLaunchTemplate`, `AllocationStrategy`, `Default`. Additionally, the ARN of a Lambda function can be specified for custom termination policies. * `suspended_processes` - (Optional) A list of processes to suspend for the Auto Scaling Group. The allowed values are `Launch`, `Terminate`, `HealthCheck`, `ReplaceUnhealthy`, `AZRebalance`, `AlarmNotification`, `ScheduledActions`, `AddToLoadBalancer`, `InstanceRefresh`. Note that if you suspend either the `Launch` or `Terminate` process types, it can prevent your Auto Scaling Group from functioning properly. * `tag` (Optional) Configuration block(s) containing resource tags. Conflicts with `tags`. See [Tag](#tag-and-tags) below for more details. diff --git a/website/docs/r/eks_node_group.html.markdown b/website/docs/r/eks_node_group.html.markdown index 7069782f41f..0d53a9cc465 100644 --- a/website/docs/r/eks_node_group.html.markdown +++ b/website/docs/r/eks_node_group.html.markdown @@ -21,12 +21,12 @@ resource "aws_eks_node_group" "example" { scaling_config { desired_size = 1 - max_size = 1 + max_size = 2 min_size = 1 } update_config { - max_unavailable = 2 + max_unavailable = 1 } # Ensure that IAM Role permissions are created before and deleted after EKS Node Group handling. diff --git a/website/docs/r/wafv2_rule_group.html.markdown b/website/docs/r/wafv2_rule_group.html.markdown index 1fda5fb2541..2d47437f1e5 100644 --- a/website/docs/r/wafv2_rule_group.html.markdown +++ b/website/docs/r/wafv2_rule_group.html.markdown @@ -501,7 +501,7 @@ An empty configuration block `{}` should be used when specifying `all_query_argu * `body` - (Optional) Inspect the request body, which immediately follows the request headers. See [Body](#body) below for details. * `cookies` - (Optional) Inspect the cookies in the web request. See [Cookies](#cookies) below for details. * `headers` - (Optional) Inspect the request headers. See [Headers](#headers) below for details. -* `json_body` - (Optional) Inspect the request body as JSON, which immediately follows the request headers. See [JsonBody](#json-body) below for details. +* `json_body` - (Optional) Inspect the request body as JSON. See [JSON Body](#json-body) for details. * `method` - (Optional) Inspect the HTTP method. The method indicates the type of operation that the request is asking the origin to perform. * `query_string` - (Optional) Inspect the query string. This is the part of a URL that appears after a `?` character, if any. * `single_header` - (Optional) Inspect a single header. See [Single Header](#single-header) below for details. @@ -537,19 +537,6 @@ The `body` block supports the following arguments: * `oversize_handling` - (Required) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. -### Cookies - -Inspect the cookies in the web request. - -The `cookies` block supports the following arguments: - -* `match_pattern` - (Required) The filter to use to identify the subset of cookies to inspect in a web request. The `match_pattern` block supports only one of the following arguments: - * `all` - An empty configuration block that is used for inspecting all cookies. - * `included_cookies` - An array of strings that will be used for inspecting cookies that have a key that matches one of the provided values. - * `excluded_cookies` - An array of strings that will be used for inspecting cookies that do not have a key that matches one of the provided values. -* `match_scope` - (Required) The parts of the cookies to inspect with the rule inspection criteria. If you specify `All`, AWS WAF inspects both keys and values. Valid values include the following: `ALL`, `Key`, `Value`. -* `oversize_handling` - (Required) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. - ### Headers Inspect the request headers. @@ -563,18 +550,14 @@ The `headers` block supports the following arguments: * `match_scope` - (Required) The parts of the headers to inspect with the rule inspection criteria. If you specify `All`, AWS WAF inspects both keys and values. Valid values include the following: `ALL`, `Key`, `Value`. * `oversize_handling` - (Required) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. -### Json Body - -Inspect the request body as JSON, which immediately follows the request headers. +### JSON Body The `json_body` block supports the following arguments: -* `invalid_fallback_behavior` - (Optional) What AWS WAF should do if it fails to completely parse the JSON body. Valid values include the following: `EVALUATE_AS_STRING`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/APIReference/API_JsonBody.html) for more information. -* `match_pattern` - (Required) The patterns to look for in the JSON body. AWS WAF inspects the results of these pattern matches against the rule inspection criteria. The `match_pattern` block supports only one of the following arguments: - * `all` - An empty configuration block that is used for inspecting all of the elements. - * `included_paths` - An array of strings that will be used for inspecting the specified paths. Provide the include paths using JSON Pointer syntax. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/APIReference/API_JsonMatchPattern.html) for more information. -* `match_scope` - (Required) The parts of the headers to inspect with the rule inspection criteria. If you specify `All`, AWS WAF inspects both keys and values. Valid values include the following: `ALL`, `Key`, `Value`. -* `oversize_handling` - (Required) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. +* `invalid_fallback_behavior` - (Optional) What to do when JSON parsing fails. Defaults to evaluating up to the first parsing failure. Valid values are `EVALUATE_AS_STRING`, `MATCH` and `NO_MATCH`. +* `match_pattern` - (Required) The patterns to look for in the JSON body. You must specify exactly one setting: either `all` or `included_paths`. See [JsonMatchPattern](https://docs.aws.amazon.com/waf/latest/APIReference/API_JsonMatchPattern.html) for details. +* `match_scope` - (Required) The parts of the JSON to match against using the `match_pattern`. Valid values are `ALL`, `KEY` and `VALUE`. +* `oversize_handling` - (Optional) What to do if the body is larger than can be inspected. Valid values are `CONTINUE` (default), `MATCH` and `NO_MATCH`. ### Single Header diff --git a/website/docs/r/wafv2_web_acl.html.markdown b/website/docs/r/wafv2_web_acl.html.markdown index 3dfbeea37e9..5910edad84a 100644 --- a/website/docs/r/wafv2_web_acl.html.markdown +++ b/website/docs/r/wafv2_web_acl.html.markdown @@ -552,7 +552,7 @@ An empty configuration block `{}` should be used when specifying `all_query_argu * `body` - (Optional) Inspect the request body, which immediately follows the request headers. See [Body](#body) below for details. * `cookies` - (Optional) Inspect the cookies in the web request. See [Cookies](#cookies) below for details. * `headers` - (Optional) Inspect the request headers. See [Headers](#headers) below for details. -* `json_body` - (Optional) Inspect the request body as JSON, which immediately follows the request headers. See [JsonBody](#json-body) below for details. +* `json_body` - (Optional) Inspect the request body as JSON. See [JSON Body](#json-body) for details. * `method` - (Optional) Inspect the HTTP method. The method indicates the type of operation that the request is asking the origin to perform. * `query_string` - (Optional) Inspect the query string. This is the part of a URL that appears after a `?` character, if any. * `single_header` - (Optional) Inspect a single header. See [Single Header](#single-header) below for details. @@ -588,19 +588,6 @@ The `body` block supports the following arguments: * `oversize_handling` - (Required) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. -### Cookies - -Inspect the cookies in the web request. - -The `cookies` block supports the following arguments: - -* `match_pattern` - (Required) The filter to use to identify the subset of cookies to inspect in a web request. The `match_pattern` block supports only one of the following arguments: - * `all` - An empty configuration block that is used for inspecting all cookies. - * `included_cookies` - An array of strings that will be used for inspecting cookies that have a key that matches one of the provided values. - * `excluded_cookies` - An array of strings that will be used for inspecting cookies that do not have a key that matches one of the provided values. -* `match_scope` - (Required) The parts of the cookies to inspect with the rule inspection criteria. If you specify `All`, AWS WAF inspects both keys and values. Valid values include the following: `ALL`, `Key`, `Value`. -* `oversize_handling` - (Required) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. - ### Headers Inspect the request headers. @@ -614,18 +601,14 @@ The `headers` block supports the following arguments: * `match_scope` - (Required) The parts of the headers to inspect with the rule inspection criteria. If you specify `All`, AWS WAF inspects both keys and values. Valid values include the following: `ALL`, `Key`, `Value`. * `oversize_handling` - (Required) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. -### Json Body - -Inspect the request body as JSON, which immediately follows the request headers. +### JSON Body The `json_body` block supports the following arguments: -* `invalid_fallback_behavior` - (Optional) What AWS WAF should do if it fails to completely parse the JSON body. Valid values include the following: `EVALUATE_AS_STRING`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/APIReference/API_JsonBody.html) for more information. -* `match_pattern` - (Required) The patterns to look for in the JSON body. AWS WAF inspects the results of these pattern matches against the rule inspection criteria. The `match_pattern` block supports only one of the following arguments: - * `all` - An empty configuration block that is used for inspecting all of the elements. - * `included_paths` - An array of strings that will be used for inspecting the specified paths. Provide the include paths using JSON Pointer syntax. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/APIReference/API_JsonMatchPattern.html) for more information. -* `match_scope` - (Required) The parts of the headers to inspect with the rule inspection criteria. If you specify `All`, AWS WAF inspects both keys and values. Valid values include the following: `ALL`, `Key`, `Value`. -* `oversize_handling` - (Required) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. +* `invalid_fallback_behavior` - (Optional) What to do when JSON parsing fails. Defaults to evaluating up to the first parsing failure. Valid values are `EVALUATE_AS_STRING`, `MATCH` and `NO_MATCH`. +* `match_pattern` - (Required) The patterns to look for in the JSON body. You must specify exactly one setting: either `all` or `included_paths`. See [JsonMatchPattern](https://docs.aws.amazon.com/waf/latest/APIReference/API_JsonMatchPattern.html) for details. +* `match_scope` - (Required) The parts of the JSON to match against using the `match_pattern`. Valid values are `ALL`, `KEY` and `VALUE`. +* `oversize_handling` - (Optional) What to do if the body is larger than can be inspected. Valid values are `CONTINUE` (default), `MATCH` and `NO_MATCH`. ### Single Header From d7091ccc6418015d589c74e6c3ed8521d72a0cc5 Mon Sep 17 00:00:00 2001 From: Scott Westover Date: Tue, 30 Aug 2022 21:29:17 -0400 Subject: [PATCH 19/52] f/fixed conflicts Signed-off-by: Scott Westover --- internal/service/wafv2/rule_group_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/wafv2/rule_group_test.go b/internal/service/wafv2/rule_group_test.go index df84da51953..980764b4acb 100644 --- a/internal/service/wafv2/rule_group_test.go +++ b/internal/service/wafv2/rule_group_test.go @@ -2609,7 +2609,7 @@ resource "aws_wafv2_rule_group" "test" { func testAccRuleGroupConfig_byteMatchStatementFieldToMatchJSONBody(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { - capacity = 15 + capacity = 20 name = "%s" scope = "REGIONAL" From d18b9772657c405ce1a183d1df4dc467f4118015 Mon Sep 17 00:00:00 2001 From: Scott Westover Date: Tue, 30 Aug 2022 21:31:35 -0400 Subject: [PATCH 20/52] f/fixed changelog after resolving conflicts Signed-off-by: Scott Westover --- .changelog/26506.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/.changelog/26506.txt b/.changelog/26506.txt index 007359d9bee..590dd202348 100644 --- a/.changelog/26506.txt +++ b/.changelog/26506.txt @@ -3,8 +3,6 @@ resource/aws_wafv2_web_acl: Add `oversize_handling` attribute as part of `body` resource/aws_wafv2_rule_group: Add `oversize_handling` attribute as part of `body` attribute resource/aws_wafv2_web_acl: Add support for `headers` attribute as part of `field_to_match` attribute. resource/aws_wafv2_rule_group: Add support for `headers` attribute as part of `field_to_match` attribute. -resource/aws_wafv2_web_acl: Add support for `json_body` attribute as part of `field_to_match` attribute. -resource/aws_wafv2_rule_group: Add support for `json_body` attribute as part of `field_to_match` attribute. ``` ```release-note:breaking-change From 3d806e7035c01576ac3bf7e94e963df17bbb42d2 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 31 Aug 2022 14:22:03 -0400 Subject: [PATCH 21/52] Separate CHANGELOG entries. --- .changelog/26506.txt | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/.changelog/26506.txt b/.changelog/26506.txt index 590dd202348..d65e760a9e4 100644 --- a/.changelog/26506.txt +++ b/.changelog/26506.txt @@ -1,8 +1,17 @@ ```release-note:enhancement -resource/aws_wafv2_web_acl: Add `oversize_handling` attribute as part of `body` attribute. -resource/aws_wafv2_rule_group: Add `oversize_handling` attribute as part of `body` attribute -resource/aws_wafv2_web_acl: Add support for `headers` attribute as part of `field_to_match` attribute. -resource/aws_wafv2_rule_group: Add support for `headers` attribute as part of `field_to_match` attribute. +resource/aws_wafv2_web_acl: Add `oversize_handling` attribute to the `field_to_match.body` block +``` + +```release-note:enhancement +resource/aws_wafv2_rule_group: Add `oversize_handling` attribute to the `field_to_match.body` block +``` + +```release-note:enhancement +resource/aws_wafv2_web_acl: Add `headers` attribute to the `field_to_match` block +``` + +```release-note:enhancement +resource/aws_wafv2_rule_group: Add `headers` attribute to the `field_to_match` block ``` ```release-note:breaking-change From 1cc708e9d5f9eeb2b3cad26d574c9185566e1341 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 31 Aug 2022 14:27:18 -0400 Subject: [PATCH 22/52] WAFv2: Use '_Values()' functions (#14601). --- internal/service/wafv2/schemas.go | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/internal/service/wafv2/schemas.go b/internal/service/wafv2/schemas.go index 1dd52da4e47..de03f7564cf 100644 --- a/internal/service/wafv2/schemas.go +++ b/internal/service/wafv2/schemas.go @@ -143,15 +143,9 @@ func byteMatchStatementSchema() *schema.Schema { Schema: map[string]*schema.Schema{ "field_to_match": fieldToMatchSchema(), "positional_constraint": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - wafv2.PositionalConstraintContains, - wafv2.PositionalConstraintContainsWord, - wafv2.PositionalConstraintEndsWith, - wafv2.PositionalConstraintExactly, - wafv2.PositionalConstraintStartsWith, - }, false), + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(wafv2.PositionalConstraint_Values(), false), }, "search_string": { Type: schema.TypeString, @@ -279,16 +273,9 @@ func sizeConstraintSchema() *schema.Schema { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "comparison_operator": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - wafv2.ComparisonOperatorEq, - wafv2.ComparisonOperatorGe, - wafv2.ComparisonOperatorGt, - wafv2.ComparisonOperatorLe, - wafv2.ComparisonOperatorLt, - wafv2.ComparisonOperatorNe, - }, false), + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(wafv2.ComparisonOperator_Values(), false), }, "field_to_match": fieldToMatchSchema(), "size": { From 38964c484e571b2d6cda962cc92f3534f29d3053 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 31 Aug 2022 14:30:02 -0400 Subject: [PATCH 23/52] WAFv2: Ensure schema generator names end with 'Schema'. --- internal/service/wafv2/schemas.go | 8 ++++---- internal/service/wafv2/web_acl.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/service/wafv2/schemas.go b/internal/service/wafv2/schemas.go index de03f7564cf..9d415576ebd 100644 --- a/internal/service/wafv2/schemas.go +++ b/internal/service/wafv2/schemas.go @@ -171,7 +171,7 @@ func geoMatchStatementSchema() *schema.Schema { MinItems: 1, Elem: &schema.Schema{Type: schema.TypeString}, }, - "forwarded_ip_config": forwardedIPConfig(), + "forwarded_ip_config": forwardedIPConfigSchema(), }, }, } @@ -420,7 +420,7 @@ func jsonBodySchema() *schema.Schema { Optional: true, ValidateFunc: validation.StringInSlice(wafv2.BodyParsingFallbackBehavior_Values(), false), }, - "match_pattern": jsonMatchPattern(), + "match_pattern": jsonMatchPatternSchema(), "match_scope": { Type: schema.TypeString, Required: true, @@ -437,7 +437,7 @@ func jsonBodySchema() *schema.Schema { } } -func jsonMatchPattern() *schema.Schema { +func jsonMatchPatternSchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeList, Required: true, @@ -461,7 +461,7 @@ func jsonMatchPattern() *schema.Schema { } } -func forwardedIPConfig() *schema.Schema { +func forwardedIPConfigSchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeList, Optional: true, diff --git a/internal/service/wafv2/web_acl.go b/internal/service/wafv2/web_acl.go index 9049ce6a52f..1cf46ae6cde 100644 --- a/internal/service/wafv2/web_acl.go +++ b/internal/service/wafv2/web_acl.go @@ -451,7 +451,7 @@ func rateBasedStatementSchema(level int) *schema.Schema { Default: wafv2.RateBasedStatementAggregateKeyTypeIp, ValidateFunc: validation.StringInSlice(wafv2.RateBasedStatementAggregateKeyType_Values(), false), }, - "forwarded_ip_config": forwardedIPConfig(), + "forwarded_ip_config": forwardedIPConfigSchema(), "limit": { Type: schema.TypeInt, Required: true, From 05c025ba0897ced0261cef9fa08bc32eb1bbfe57 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 31 Aug 2022 14:35:26 -0400 Subject: [PATCH 24/52] WAFv2: Use 'Scope_Values()' (#14601). --- internal/service/wafv2/ip_set.go | 11 ++++------- internal/service/wafv2/ip_set_data_source.go | 9 +++------ internal/service/wafv2/regex_pattern_set.go | 11 ++++------- .../wafv2/regex_pattern_set_data_source.go | 9 +++------ internal/service/wafv2/rule_group.go | 15 ++++++--------- internal/service/wafv2/rule_group_data_source.go | 9 +++------ internal/service/wafv2/web_acl.go | 15 ++++++--------- internal/service/wafv2/web_acl_data_source.go | 9 +++------ 8 files changed, 32 insertions(+), 56 deletions(-) diff --git a/internal/service/wafv2/ip_set.go b/internal/service/wafv2/ip_set.go index d11aea960c5..84e101223f7 100644 --- a/internal/service/wafv2/ip_set.go +++ b/internal/service/wafv2/ip_set.go @@ -98,13 +98,10 @@ func ResourceIPSet() *schema.Resource { ValidateFunc: validation.StringLenBetween(1, 128), }, "scope": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - wafv2.ScopeCloudfront, - wafv2.ScopeRegional, - }, false), + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(wafv2.Scope_Values(), false), }, "tags": tftags.TagsSchema(), "tags_all": tftags.TagsSchemaComputed(), diff --git a/internal/service/wafv2/ip_set_data_source.go b/internal/service/wafv2/ip_set_data_source.go index 1bcab407226..69af5ee76da 100644 --- a/internal/service/wafv2/ip_set_data_source.go +++ b/internal/service/wafv2/ip_set_data_source.go @@ -38,12 +38,9 @@ func DataSourceIPSet() *schema.Resource { Required: true, }, "scope": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - wafv2.ScopeCloudfront, - wafv2.ScopeRegional, - }, false), + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(wafv2.Scope_Values(), false), }, }, } diff --git a/internal/service/wafv2/regex_pattern_set.go b/internal/service/wafv2/regex_pattern_set.go index ec209c30881..d88b1989d20 100644 --- a/internal/service/wafv2/regex_pattern_set.go +++ b/internal/service/wafv2/regex_pattern_set.go @@ -75,13 +75,10 @@ func ResourceRegexPatternSet() *schema.Resource { }, }, "scope": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - wafv2.ScopeCloudfront, - wafv2.ScopeRegional, - }, false), + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(wafv2.Scope_Values(), false), }, "tags": tftags.TagsSchema(), "tags_all": tftags.TagsSchemaComputed(), diff --git a/internal/service/wafv2/regex_pattern_set_data_source.go b/internal/service/wafv2/regex_pattern_set_data_source.go index a99019afd32..4e51629a0f4 100644 --- a/internal/service/wafv2/regex_pattern_set_data_source.go +++ b/internal/service/wafv2/regex_pattern_set_data_source.go @@ -40,12 +40,9 @@ func DataSourceRegexPatternSet() *schema.Resource { }, }, "scope": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - wafv2.ScopeCloudfront, - wafv2.ScopeRegional, - }, false), + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(wafv2.Scope_Values(), false), }, }, } diff --git a/internal/service/wafv2/rule_group.go b/internal/service/wafv2/rule_group.go index 1dadb1e4e65..6cc712f5408 100644 --- a/internal/service/wafv2/rule_group.go +++ b/internal/service/wafv2/rule_group.go @@ -71,15 +71,6 @@ func ResourceRuleGroup() *schema.Resource { validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9-_]+$`), "must contain only alphanumeric hyphen and underscore characters"), ), }, - "scope": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - wafv2.ScopeCloudfront, - wafv2.ScopeRegional, - }, false), - }, "rule": { Type: schema.TypeSet, Optional: true, @@ -112,6 +103,12 @@ func ResourceRuleGroup() *schema.Resource { }, }, }, + "scope": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(wafv2.Scope_Values(), false), + }, "tags": tftags.TagsSchema(), "tags_all": tftags.TagsSchemaComputed(), "visibility_config": visibilityConfigSchema(), diff --git a/internal/service/wafv2/rule_group_data_source.go b/internal/service/wafv2/rule_group_data_source.go index 6dddc561cd8..4e66b5b50c8 100644 --- a/internal/service/wafv2/rule_group_data_source.go +++ b/internal/service/wafv2/rule_group_data_source.go @@ -28,12 +28,9 @@ func DataSourceRuleGroup() *schema.Resource { Required: true, }, "scope": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - wafv2.ScopeCloudfront, - wafv2.ScopeRegional, - }, false), + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(wafv2.Scope_Values(), false), }, }, } diff --git a/internal/service/wafv2/web_acl.go b/internal/service/wafv2/web_acl.go index 1cf46ae6cde..3f2e799df1c 100644 --- a/internal/service/wafv2/web_acl.go +++ b/internal/service/wafv2/web_acl.go @@ -86,15 +86,6 @@ func ResourceWebACL() *schema.Resource { validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9-_]+$`), "must contain only alphanumeric hyphen and underscore characters"), ), }, - "scope": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - wafv2.ScopeCloudfront, - wafv2.ScopeRegional, - }, false), - }, "rule": { Type: schema.TypeSet, Optional: true, @@ -139,6 +130,12 @@ func ResourceWebACL() *schema.Resource { }, }, }, + "scope": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(wafv2.Scope_Values(), false), + }, "tags": tftags.TagsSchema(), "tags_all": tftags.TagsSchemaComputed(), "visibility_config": visibilityConfigSchema(), diff --git a/internal/service/wafv2/web_acl_data_source.go b/internal/service/wafv2/web_acl_data_source.go index 1e20941d077..bfa99e957e0 100644 --- a/internal/service/wafv2/web_acl_data_source.go +++ b/internal/service/wafv2/web_acl_data_source.go @@ -28,12 +28,9 @@ func DataSourceWebACL() *schema.Resource { Required: true, }, "scope": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - wafv2.ScopeCloudfront, - wafv2.ScopeRegional, - }, false), + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(wafv2.Scope_Values(), false), }, }, } From 712deda5e878e3d39ead46ddf7b0ef5c780dcaac Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 31 Aug 2022 14:39:29 -0400 Subject: [PATCH 25/52] WAFv2: Use 'IPAddressVersion_Values()' (#14601). --- internal/service/wafv2/ip_set.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/internal/service/wafv2/ip_set.go b/internal/service/wafv2/ip_set.go index 84e101223f7..aa39db7b2cb 100644 --- a/internal/service/wafv2/ip_set.go +++ b/internal/service/wafv2/ip_set.go @@ -79,13 +79,10 @@ func ResourceIPSet() *schema.Resource { ValidateFunc: validation.StringLenBetween(1, 256), }, "ip_address_version": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - wafv2.IPAddressVersionIpv4, - wafv2.IPAddressVersionIpv6, - }, false), + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(wafv2.IPAddressVersion_Values(), false), }, "lock_token": { Type: schema.TypeString, From 1b0bec9ee2f5ed4f75bdd2ea087aa3d3fb610fcf Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 31 Aug 2022 14:46:23 -0400 Subject: [PATCH 26/52] 'emptySchemaDeprecated' -> 'emptyDeprecatedSchema'. --- internal/service/wafv2/schemas.go | 2 +- internal/service/wafv2/web_acl_logging_configuration.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/service/wafv2/schemas.go b/internal/service/wafv2/schemas.go index 9d415576ebd..68eb5a5b1cf 100644 --- a/internal/service/wafv2/schemas.go +++ b/internal/service/wafv2/schemas.go @@ -21,7 +21,7 @@ func emptySchema() *schema.Schema { } } -func emptySchemaDeprecated() *schema.Schema { +func emptyDeprecatedSchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeList, Optional: true, diff --git a/internal/service/wafv2/web_acl_logging_configuration.go b/internal/service/wafv2/web_acl_logging_configuration.go index 44bb1d2b73a..2231f803c66 100644 --- a/internal/service/wafv2/web_acl_logging_configuration.go +++ b/internal/service/wafv2/web_acl_logging_configuration.go @@ -126,8 +126,8 @@ func ResourceWebACLLoggingConfiguration() *schema.Resource { // 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": emptySchemaDeprecated(), - "body": emptySchemaDeprecated(), + "all_query_arguments": emptyDeprecatedSchema(), + "body": emptyDeprecatedSchema(), "method": emptySchema(), "query_string": emptySchema(), "single_header": { From edab50090d826072c261f0a7a9475d7dc375e985 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 31 Aug 2022 14:53:01 -0400 Subject: [PATCH 27/52] 'oversizeHandlingSchema' -> 'oversizeHandlingOptionalComputedSchema' and 'oversizeHandlingRequiredSchema'. --- internal/service/wafv2/schemas.go | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/internal/service/wafv2/schemas.go b/internal/service/wafv2/schemas.go index 68eb5a5b1cf..967126192fb 100644 --- a/internal/service/wafv2/schemas.go +++ b/internal/service/wafv2/schemas.go @@ -329,7 +329,7 @@ func fieldToMatchBaseSchema() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "match_scope": matchScopeSchema(), - "oversize_handling": oversizeHandlingSchema(), + "oversize_handling": oversizeHandlingRequiredSchema(), "match_pattern": { Type: schema.TypeList, Required: true, @@ -426,12 +426,7 @@ func jsonBodySchema() *schema.Schema { Required: true, ValidateFunc: validation.StringInSlice(wafv2.JsonMatchScope_Values(), false), }, - "oversize_handling": { - Type: schema.TypeString, - Optional: true, - Default: wafv2.OversizeHandlingContinue, - ValidateFunc: validation.StringInSlice(wafv2.OversizeHandling_Values(), false), - }, + "oversize_handling": oversizeHandlingRequiredSchema(), }, }, } @@ -697,13 +692,22 @@ func bodySchema() *schema.Schema { Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "oversize_handling": oversizeHandlingSchema(), + "oversize_handling": oversizeHandlingOptionalComputedSchema(), }, }, } } -func oversizeHandlingSchema() *schema.Schema { +func oversizeHandlingOptionalComputedSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice(wafv2.OversizeHandling_Values(), false), + } +} + +func oversizeHandlingRequiredSchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeString, Required: true, @@ -739,7 +743,7 @@ func headersSchema() *schema.Schema { }, }, "match_scope": matchScopeSchema(), - "oversize_handling": oversizeHandlingSchema(), + "oversize_handling": oversizeHandlingRequiredSchema(), }, }, } From 6efdbca5980e5c4aaca77f4083cf3865d50217df Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 31 Aug 2022 14:56:07 -0400 Subject: [PATCH 28/52] Add 'cookiesSchema'. --- internal/service/wafv2/schemas.go | 72 ++++++++++++++++--------------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/internal/service/wafv2/schemas.go b/internal/service/wafv2/schemas.go index 967126192fb..2670df2e1d7 100644 --- a/internal/service/wafv2/schemas.go +++ b/internal/service/wafv2/schemas.go @@ -322,40 +322,11 @@ func fieldToMatchBaseSchema() *schema.Resource { Schema: map[string]*schema.Schema{ "all_query_arguments": emptySchema(), "body": bodySchema(), - "cookies": { - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "match_scope": matchScopeSchema(), - "oversize_handling": oversizeHandlingRequiredSchema(), - "match_pattern": { - Type: schema.TypeList, - Required: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "all": emptySchema(), - "included_cookies": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - "excluded_cookies": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - }, - }, - }, - }, - }, - }, - "headers": headersSchema(), - "json_body": jsonBodySchema(), - "method": emptySchema(), - "query_string": emptySchema(), + "cookies": cookiesSchema(), + "headers": headersSchema(), + "json_body": jsonBodySchema(), + "method": emptySchema(), + "query_string": emptySchema(), "single_header": { Type: schema.TypeList, Optional: true, @@ -698,6 +669,39 @@ func bodySchema() *schema.Schema { } } +func cookiesSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "match_scope": matchScopeSchema(), + "oversize_handling": oversizeHandlingRequiredSchema(), + "match_pattern": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "all": emptySchema(), + "included_cookies": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "excluded_cookies": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, + }, + }, + } +} + func oversizeHandlingOptionalComputedSchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeString, From 479bf378e62a4d8e438d10602ffce228b011805d Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 31 Aug 2022 14:56:44 -0400 Subject: [PATCH 29/52] 'jsonMatchPatternSchema' -> 'jsonBodyMatchPatternSchema'. --- internal/service/wafv2/schemas.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/service/wafv2/schemas.go b/internal/service/wafv2/schemas.go index 2670df2e1d7..b3ff6aa31d5 100644 --- a/internal/service/wafv2/schemas.go +++ b/internal/service/wafv2/schemas.go @@ -391,7 +391,7 @@ func jsonBodySchema() *schema.Schema { Optional: true, ValidateFunc: validation.StringInSlice(wafv2.BodyParsingFallbackBehavior_Values(), false), }, - "match_pattern": jsonMatchPatternSchema(), + "match_pattern": jsonBodyMatchPatternSchema(), "match_scope": { Type: schema.TypeString, Required: true, @@ -403,7 +403,7 @@ func jsonBodySchema() *schema.Schema { } } -func jsonMatchPatternSchema() *schema.Schema { +func jsonBodyMatchPatternSchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeList, Required: true, From 14bf018de1ff2b606c8be1ef3d34de7fc99c9141 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 31 Aug 2022 14:58:35 -0400 Subject: [PATCH 30/52] Add 'cookiesMatchPatternSchema'. --- internal/service/wafv2/schemas.go | 38 +++++++++++++++++-------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/internal/service/wafv2/schemas.go b/internal/service/wafv2/schemas.go index b3ff6aa31d5..712a70dab6a 100644 --- a/internal/service/wafv2/schemas.go +++ b/internal/service/wafv2/schemas.go @@ -678,24 +678,28 @@ func cookiesSchema() *schema.Schema { Schema: map[string]*schema.Schema{ "match_scope": matchScopeSchema(), "oversize_handling": oversizeHandlingRequiredSchema(), - "match_pattern": { + "match_pattern": cookiesMatchPatternSchema(), + }, + }, + } +} + +func cookiesMatchPatternSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "all": emptySchema(), + "excluded_cookies": { Type: schema.TypeList, - Required: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "all": emptySchema(), - "included_cookies": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - "excluded_cookies": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - }, - }, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "included_cookies": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, }, }, }, From 2b4da927e8ecd9eafac2e09aefe747028e1ccee7 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 31 Aug 2022 16:20:38 -0400 Subject: [PATCH 31/52] WAFv2: 'oversize_handling' is Optional for 'body'. --- .changelog/26506.txt | 5 - internal/service/wafv2/flex.go | 14 +- internal/service/wafv2/rule_group_test.go | 141 ++++++++++++++++-- website/docs/r/wafv2_rule_group.html.markdown | 4 +- website/docs/r/wafv2_web_acl.html.markdown | 4 +- 5 files changed, 144 insertions(+), 24 deletions(-) diff --git a/.changelog/26506.txt b/.changelog/26506.txt index d65e760a9e4..57420ebfe0c 100644 --- a/.changelog/26506.txt +++ b/.changelog/26506.txt @@ -13,8 +13,3 @@ resource/aws_wafv2_web_acl: Add `headers` attribute to the `field_to_match` bloc ```release-note:enhancement resource/aws_wafv2_rule_group: Add `headers` attribute to the `field_to_match` block ``` - -```release-note:breaking-change -resource/aws_wafv2_rule_group: The `body` attribute on the `field_to_match` will no longer accept an empty configuration block, and now requires `oversize_handling` attribute to be provided. -resource/aws_wafv2_web_acl: The `body` attribute on the `field_to_match` will no longer accept an empty configuration block, and now requires `oversize_handling` attribute to be provided. -``` diff --git a/internal/service/wafv2/flex.go b/internal/service/wafv2/flex.go index 669fb947ef0..7d322766d8d 100644 --- a/internal/service/wafv2/flex.go +++ b/internal/service/wafv2/flex.go @@ -734,9 +734,13 @@ func expandBody(l []interface{}) *wafv2.Body { m := l[0].(map[string]interface{}) - return &wafv2.Body{ - OversizeHandling: aws.String(m["oversize_handling"].(string)), + apiObject := &wafv2.Body{} + + if v, ok := m["oversize_handling"].(string); ok && v != "" { + apiObject.OversizeHandling = aws.String(v) } + + return apiObject } func expandHeaders(l []interface{}) *wafv2.Headers { @@ -1366,8 +1370,10 @@ func flattenBody(s *wafv2.Body) interface{} { return []interface{}{} } - m := map[string]interface{}{ - "oversize_handling": aws.StringValue(s.OversizeHandling), + m := map[string]interface{}{} + + if v := s.OversizeHandling; v != nil { + m["oversize_handling"] = aws.StringValue(v) } return []interface{}{m} diff --git a/internal/service/wafv2/rule_group_test.go b/internal/service/wafv2/rule_group_test.go index 980764b4acb..83ce83745a6 100644 --- a/internal/service/wafv2/rule_group_test.go +++ b/internal/service/wafv2/rule_group_test.go @@ -390,8 +390,129 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { }), ), }, + /* + This test case fails even though an apply and subsequent plan on the same configuration reports no difference. + + === CONT TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch + rule_group_test.go:364: Step 2/15 error: After applying this test step, the plan was not empty. + stdout: + + + Terraform used the selected providers to generate the following execution + plan. Resource actions are indicated with the following symbols: + ~ update in-place + + Terraform will perform the following actions: + + # aws_wafv2_rule_group.test will be updated in-place + ~ resource "aws_wafv2_rule_group" "test" { + id = "f9702b0d-b73f-4895-a63f-aa0489396669" + name = "tf-acc-test-4192374723646850271" + # (5 unchanged attributes hidden) + + + rule { + + name = "rule-1" + + priority = 1 + + + action { + + allow { + } + } + + + statement { + + + byte_match_statement { + + positional_constraint = "CONTAINS" + + search_string = "word" + + + field_to_match { + + + body { + + oversize_handling = (known after apply) + } + } + + + text_transformation { + + priority = 1 + + type = "NONE" + } + } + } + + + visibility_config { + + cloudwatch_metrics_enabled = false + + metric_name = "friendly-rule-metric-name" + + sampled_requests_enabled = false + } + } + - rule { + - name = "rule-1" -> null + - priority = 1 -> null + + - action { + - allow { + } + } + + - statement { + + - byte_match_statement { + - positional_constraint = "CONTAINS" -> null + - search_string = "word" -> null + + - field_to_match { + + - body {} + } + + - text_transformation { + - priority = 1 -> null + - type = "NONE" -> null + } + } + } + + - visibility_config { + - cloudwatch_metrics_enabled = false -> null + - metric_name = "friendly-rule-metric-name" -> null + - sampled_requests_enabled = false -> null + } + } + + rule { + } + + # (1 unchanged block hidden) + } + + Plan: 0 to add, 1 to change, 0 to destroy. + --- FAIL: TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch (28.52s) + + { + Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchBody(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRuleGroupExists(resourceName, &v), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "statement.#": "1", + "statement.0.byte_match_statement.#": "1", + "statement.0.byte_match_statement.0.field_to_match.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.body.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", + }), + ), + }, + */ { - Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchBody(ruleGroupName), + Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchBodyOversizeHandling(ruleGroupName), Check: resource.ComposeTestCheckFunc( testAccCheckRuleGroupExists(resourceName, &v), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), @@ -402,7 +523,7 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { "statement.0.byte_match_statement.0.field_to_match.#": "1", "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.body.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.body.0.oversize_handling": "CONTINUE", + "statement.0.byte_match_statement.0.field_to_match.0.body.0.oversize_handling": "MATCH", "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "0", @@ -414,10 +535,6 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { }), ), }, - { - Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchBodyInvalidConfiguration(ruleGroupName), - ExpectError: regexp.MustCompile(`argument "oversize_handling" is required`), - }, { Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookies(ruleGroupName), Check: resource.ComposeTestCheckFunc( @@ -2557,6 +2674,7 @@ resource "aws_wafv2_rule_group" "test" { `, name) } +/* func testAccRuleGroupConfig_byteMatchStatementFieldToMatchBody(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { @@ -2578,9 +2696,7 @@ resource "aws_wafv2_rule_group" "test" { search_string = "word" field_to_match { - body { - oversize_handling = "CONTINUE" - } + body {} } text_transformation { @@ -2605,6 +2721,7 @@ resource "aws_wafv2_rule_group" "test" { } `, name) } +*/ func testAccRuleGroupConfig_byteMatchStatementFieldToMatchJSONBody(name string) string { return fmt.Sprintf(` @@ -2660,7 +2777,7 @@ resource "aws_wafv2_rule_group" "test" { `, name) } -func testAccRuleGroupConfig_byteMatchStatementFieldToMatchBodyInvalidConfiguration(name string) string { +func testAccRuleGroupConfig_byteMatchStatementFieldToMatchBodyOversizeHandling(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { capacity = 15 @@ -2681,7 +2798,9 @@ resource "aws_wafv2_rule_group" "test" { search_string = "word" field_to_match { - body {} + body { + oversize_handling = "MATCH" + } } text_transformation { diff --git a/website/docs/r/wafv2_rule_group.html.markdown b/website/docs/r/wafv2_rule_group.html.markdown index 2d47437f1e5..fbee3118b12 100644 --- a/website/docs/r/wafv2_rule_group.html.markdown +++ b/website/docs/r/wafv2_rule_group.html.markdown @@ -535,7 +535,7 @@ Inspect the request body, which immediately follows the request headers. The `body` block supports the following arguments: -* `oversize_handling` - (Required) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. +* `oversize_handling` - (Optional) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. ### Headers @@ -557,7 +557,7 @@ The `json_body` block supports the following arguments: * `invalid_fallback_behavior` - (Optional) What to do when JSON parsing fails. Defaults to evaluating up to the first parsing failure. Valid values are `EVALUATE_AS_STRING`, `MATCH` and `NO_MATCH`. * `match_pattern` - (Required) The patterns to look for in the JSON body. You must specify exactly one setting: either `all` or `included_paths`. See [JsonMatchPattern](https://docs.aws.amazon.com/waf/latest/APIReference/API_JsonMatchPattern.html) for details. * `match_scope` - (Required) The parts of the JSON to match against using the `match_pattern`. Valid values are `ALL`, `KEY` and `VALUE`. -* `oversize_handling` - (Optional) What to do if the body is larger than can be inspected. Valid values are `CONTINUE` (default), `MATCH` and `NO_MATCH`. +* `oversize_handling` - (Required) What to do if the body is larger than can be inspected. Valid values are `CONTINUE`, `MATCH` and `NO_MATCH`. ### Single Header diff --git a/website/docs/r/wafv2_web_acl.html.markdown b/website/docs/r/wafv2_web_acl.html.markdown index 5910edad84a..39bd256dd09 100644 --- a/website/docs/r/wafv2_web_acl.html.markdown +++ b/website/docs/r/wafv2_web_acl.html.markdown @@ -586,7 +586,7 @@ Inspect the request body, which immediately follows the request headers. The `body` block supports the following arguments: -* `oversize_handling` - (Required) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. +* `oversize_handling` - (Optional) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. ### Headers @@ -608,7 +608,7 @@ The `json_body` block supports the following arguments: * `invalid_fallback_behavior` - (Optional) What to do when JSON parsing fails. Defaults to evaluating up to the first parsing failure. Valid values are `EVALUATE_AS_STRING`, `MATCH` and `NO_MATCH`. * `match_pattern` - (Required) The patterns to look for in the JSON body. You must specify exactly one setting: either `all` or `included_paths`. See [JsonMatchPattern](https://docs.aws.amazon.com/waf/latest/APIReference/API_JsonMatchPattern.html) for details. * `match_scope` - (Required) The parts of the JSON to match against using the `match_pattern`. Valid values are `ALL`, `KEY` and `VALUE`. -* `oversize_handling` - (Optional) What to do if the body is larger than can be inspected. Valid values are `CONTINUE` (default), `MATCH` and `NO_MATCH`. +* `oversize_handling` - (Required) What to do if the body is larger than can be inspected. Valid values are `CONTINUE`, `MATCH` and `NO_MATCH`. ### Single Header From f4246d280876678a1c10f75d40ed803eba1beaa9 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 31 Aug 2022 16:28:47 -0400 Subject: [PATCH 32/52] 'oversizeHandlingOptionalComputedSchema' -> 'oversizeHandlingOptionalSchema'. --- internal/service/wafv2/rule_group_test.go | 146 ++++------------------ internal/service/wafv2/schemas.go | 5 +- 2 files changed, 25 insertions(+), 126 deletions(-) diff --git a/internal/service/wafv2/rule_group_test.go b/internal/service/wafv2/rule_group_test.go index 83ce83745a6..407853cd721 100644 --- a/internal/service/wafv2/rule_group_test.go +++ b/internal/service/wafv2/rule_group_test.go @@ -390,127 +390,29 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { }), ), }, - /* - This test case fails even though an apply and subsequent plan on the same configuration reports no difference. - - === CONT TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch - rule_group_test.go:364: Step 2/15 error: After applying this test step, the plan was not empty. - stdout: - - - Terraform used the selected providers to generate the following execution - plan. Resource actions are indicated with the following symbols: - ~ update in-place - - Terraform will perform the following actions: - - # aws_wafv2_rule_group.test will be updated in-place - ~ resource "aws_wafv2_rule_group" "test" { - id = "f9702b0d-b73f-4895-a63f-aa0489396669" - name = "tf-acc-test-4192374723646850271" - # (5 unchanged attributes hidden) - - + rule { - + name = "rule-1" - + priority = 1 - - + action { - + allow { - } - } - - + statement { - - + byte_match_statement { - + positional_constraint = "CONTAINS" - + search_string = "word" - - + field_to_match { - - + body { - + oversize_handling = (known after apply) - } - } - - + text_transformation { - + priority = 1 - + type = "NONE" - } - } - } - - + visibility_config { - + cloudwatch_metrics_enabled = false - + metric_name = "friendly-rule-metric-name" - + sampled_requests_enabled = false - } - } - - rule { - - name = "rule-1" -> null - - priority = 1 -> null - - - action { - - allow { - } - } - - - statement { - - - byte_match_statement { - - positional_constraint = "CONTAINS" -> null - - search_string = "word" -> null - - - field_to_match { - - - body {} - } - - - text_transformation { - - priority = 1 -> null - - type = "NONE" -> null - } - } - } - - - visibility_config { - - cloudwatch_metrics_enabled = false -> null - - metric_name = "friendly-rule-metric-name" -> null - - sampled_requests_enabled = false -> null - } - } - + rule { - } - - # (1 unchanged block hidden) - } - - Plan: 0 to add, 1 to change, 0 to destroy. - --- FAIL: TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch (28.52s) - - { - Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchBody(ruleGroupName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName, &v), - acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), - resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), - resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ - "statement.#": "1", - "statement.0.byte_match_statement.#": "1", - "statement.0.byte_match_statement.0.field_to_match.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.body.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", - }), - ), - }, - */ + { + Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchBody(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRuleGroupExists(resourceName, &v), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "statement.#": "1", + "statement.0.byte_match_statement.#": "1", + "statement.0.byte_match_statement.0.field_to_match.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.body.#": "1", + "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", + "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", + }), + ), + }, { Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchBodyOversizeHandling(ruleGroupName), Check: resource.ComposeTestCheckFunc( @@ -2674,7 +2576,6 @@ resource "aws_wafv2_rule_group" "test" { `, name) } -/* func testAccRuleGroupConfig_byteMatchStatementFieldToMatchBody(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { @@ -2721,7 +2622,6 @@ resource "aws_wafv2_rule_group" "test" { } `, name) } -*/ func testAccRuleGroupConfig_byteMatchStatementFieldToMatchJSONBody(name string) string { return fmt.Sprintf(` diff --git a/internal/service/wafv2/schemas.go b/internal/service/wafv2/schemas.go index 712a70dab6a..c2153293de2 100644 --- a/internal/service/wafv2/schemas.go +++ b/internal/service/wafv2/schemas.go @@ -663,7 +663,7 @@ func bodySchema() *schema.Schema { Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "oversize_handling": oversizeHandlingOptionalComputedSchema(), + "oversize_handling": oversizeHandlingOptionalSchema(), }, }, } @@ -706,11 +706,10 @@ func cookiesMatchPatternSchema() *schema.Schema { } } -func oversizeHandlingOptionalComputedSchema() *schema.Schema { +func oversizeHandlingOptionalSchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeString, Optional: true, - Computed: true, ValidateFunc: validation.StringInSlice(wafv2.OversizeHandling_Values(), false), } } From 95e73b39b05bd0f10b3472dd8d2bb7c4aab20c5b Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 31 Aug 2022 17:00:49 -0400 Subject: [PATCH 33/52] MaxItems for 'body' is 1. --- internal/service/wafv2/schemas.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/service/wafv2/schemas.go b/internal/service/wafv2/schemas.go index c2153293de2..8de2eb8b8af 100644 --- a/internal/service/wafv2/schemas.go +++ b/internal/service/wafv2/schemas.go @@ -661,6 +661,7 @@ func bodySchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeList, Optional: true, + MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "oversize_handling": oversizeHandlingOptionalSchema(), From 3295b86e014a4f48181834f9e1693fffdd37addd Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 27 Sep 2022 15:17:03 -0400 Subject: [PATCH 34/52] r/aws_wafv2_rule_group: Switch to 'WithoutTimeout' CRUD handlers (#15090). Acceptance test output: % make testacc TESTARGS='-run=TestAccWAFV2RuleGroup_basic\|TestAccWAFV2RuleGroup_tags\|TestAccWAFV2RuleGroup_updateRule' PKG=wafv2 ACCTEST_PARALLELISM=2 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/wafv2/... -v -count 1 -parallel 2 -run=TestAccWAFV2RuleGroup_basic\|TestAccWAFV2RuleGroup_tags\|TestAccWAFV2RuleGroup_updateRule -timeout 180m === RUN TestAccWAFV2RuleGroup_basic === PAUSE TestAccWAFV2RuleGroup_basic === RUN TestAccWAFV2RuleGroup_updateRule === PAUSE TestAccWAFV2RuleGroup_updateRule === RUN TestAccWAFV2RuleGroup_updateRuleProperties === PAUSE TestAccWAFV2RuleGroup_updateRuleProperties === RUN TestAccWAFV2RuleGroup_tags === PAUSE TestAccWAFV2RuleGroup_tags === CONT TestAccWAFV2RuleGroup_basic === CONT TestAccWAFV2RuleGroup_updateRuleProperties --- PASS: TestAccWAFV2RuleGroup_basic (21.45s) === CONT TestAccWAFV2RuleGroup_tags --- PASS: TestAccWAFV2RuleGroup_updateRuleProperties (53.11s) === CONT TestAccWAFV2RuleGroup_updateRule --- PASS: TestAccWAFV2RuleGroup_tags (49.61s) --- PASS: TestAccWAFV2RuleGroup_updateRule (35.30s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/wafv2 92.482s --- internal/service/wafv2/rule_group.go | 239 +++++++++++----------- internal/service/wafv2/rule_group_test.go | 50 ++--- 2 files changed, 131 insertions(+), 158 deletions(-) diff --git a/internal/service/wafv2/rule_group.go b/internal/service/wafv2/rule_group.go index 6cc712f5408..e1a30d38a18 100644 --- a/internal/service/wafv2/rule_group.go +++ b/internal/service/wafv2/rule_group.go @@ -1,6 +1,7 @@ package wafv2 import ( + "context" "fmt" "log" "regexp" @@ -10,6 +11,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/wafv2" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -19,12 +21,19 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/verify" ) +const ( + ruleGroupCreateTimeout = 5 * time.Minute + ruleGroupUpdateTimeout = 5 * time.Minute + ruleGroupDeleteTimeout = 5 * time.Minute +) + func ResourceRuleGroup() *schema.Resource { return &schema.Resource{ - Create: resourceRuleGroupCreate, - Read: resourceRuleGroupRead, - Update: resourceRuleGroupUpdate, - Delete: resourceRuleGroupDelete, + CreateWithoutTimeout: resourceRuleGroupCreate, + ReadWithoutTimeout: resourceRuleGroupRead, + UpdateWithoutTimeout: resourceRuleGroupUpdate, + DeleteWithoutTimeout: resourceRuleGroupDelete, + Importer: &schema.ResourceImporter{ State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { idParts := strings.Split(d.Id(), "/") @@ -118,208 +127,194 @@ func ResourceRuleGroup() *schema.Resource { } } -func resourceRuleGroupCreate(d *schema.ResourceData, meta interface{}) error { +func resourceRuleGroupCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).WAFV2Conn defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{}))) - var resp *wafv2.CreateRuleGroupOutput - params := &wafv2.CreateRuleGroupInput{ - Name: aws.String(d.Get("name").(string)), - Scope: aws.String(d.Get("scope").(string)), + name := d.Get("name").(string) + input := &wafv2.CreateRuleGroupInput{ Capacity: aws.Int64(int64(d.Get("capacity").(int))), + Name: aws.String(name), Rules: expandRules(d.Get("rule").(*schema.Set).List()), + Scope: aws.String(d.Get("scope").(string)), VisibilityConfig: expandVisibilityConfig(d.Get("visibility_config").([]interface{})), } if v, ok := d.GetOk("custom_response_body"); ok && v.(*schema.Set).Len() > 0 { - params.CustomResponseBodies = expandCustomResponseBodies(v.(*schema.Set).List()) + input.CustomResponseBodies = expandCustomResponseBodies(v.(*schema.Set).List()) } if v, ok := d.GetOk("description"); ok { - params.Description = aws.String(v.(string)) + input.Description = aws.String(v.(string)) } if len(tags) > 0 { - params.Tags = Tags(tags.IgnoreAWS()) + input.Tags = Tags(tags.IgnoreAWS()) } - err := resource.Retry(5*time.Minute, func() *resource.RetryError { - var err error - resp, err = conn.CreateRuleGroup(params) - if err != nil { - if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFUnavailableEntityException) { - return resource.RetryableError(err) - } - return resource.NonRetryableError(err) - } - return nil - }) - - if tfresource.TimedOut(err) { - resp, err = conn.CreateRuleGroup(params) - } + log.Printf("[INFO] Creating WAFv2 RuleGroup: %s", input) + outputRaw, err := tfresource.RetryWhenAWSErrCodeEqualsContext(ctx, ruleGroupCreateTimeout, func() (interface{}, error) { + return conn.CreateRuleGroupWithContext(ctx, input) + }, wafv2.ErrCodeWAFUnavailableEntityException) if err != nil { - return fmt.Errorf("Error creating WAFv2 RuleGroup: %s", err) + return diag.Errorf("creating WAFv2 RuleGroup (%s): %s", name, err) } - if resp == nil || resp.Summary == nil { - return fmt.Errorf("Error creating WAFv2 RuleGroup") - } + output := outputRaw.(*wafv2.CreateRuleGroupOutput) - d.SetId(aws.StringValue(resp.Summary.Id)) + d.SetId(aws.StringValue(output.Summary.Id)) - return resourceRuleGroupRead(d, meta) + return resourceRuleGroupRead(ctx, d, meta) } -func resourceRuleGroupRead(d *schema.ResourceData, meta interface{}) error { +func resourceRuleGroupRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).WAFV2Conn defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - params := &wafv2.GetRuleGroupInput{ - Id: aws.String(d.Id()), - Name: aws.String(d.Get("name").(string)), - Scope: aws.String(d.Get("scope").(string)), + output, err := FindRuleGroupByThreePartKey(ctx, conn, d.Id(), d.Get("name").(string), d.Get("scope").(string)) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] WAFv2 RuleGroup (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil } - resp, err := conn.GetRuleGroup(params) if err != nil { - if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFNonexistentItemException) { - log.Printf("[WARN] WAFv2 RuleGroup (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } - return err + return diag.Errorf("reading WAFv2 RuleGroup (%s): %s", d.Id(), err) } - if resp == nil || resp.RuleGroup == nil { - return fmt.Errorf("Error getting WAFv2 RuleGroup") + ruleGroup := output.RuleGroup + arn := aws.StringValue(ruleGroup.ARN) + d.Set("arn", ruleGroup.ARN) + d.Set("capacity", ruleGroup.Capacity) + if err := d.Set("custom_response_body", flattenCustomResponseBodies(ruleGroup.CustomResponseBodies)); err != nil { + return diag.Errorf("setting custom_response_body: %s", err) } - - d.Set("name", resp.RuleGroup.Name) - d.Set("capacity", resp.RuleGroup.Capacity) - d.Set("description", resp.RuleGroup.Description) - d.Set("arn", resp.RuleGroup.ARN) - d.Set("lock_token", resp.LockToken) - - if err := d.Set("custom_response_body", flattenCustomResponseBodies(resp.RuleGroup.CustomResponseBodies)); err != nil { - return fmt.Errorf("Error setting custom_response_body: %w", err) + d.Set("description", ruleGroup.Description) + d.Set("lock_token", output.LockToken) + d.Set("name", ruleGroup.Name) + if err := d.Set("rule", flattenRules(ruleGroup.Rules)); err != nil { + return diag.Errorf("setting rule: %s", err) } - - if err := d.Set("rule", flattenRules(resp.RuleGroup.Rules)); err != nil { - return fmt.Errorf("Error setting rule: %s", err) + if err := d.Set("visibility_config", flattenVisibilityConfig(ruleGroup.VisibilityConfig)); err != nil { + return diag.Errorf("setting visibility_config: %s", err) } - if err := d.Set("visibility_config", flattenVisibilityConfig(resp.RuleGroup.VisibilityConfig)); err != nil { - return fmt.Errorf("Error setting visibility_config: %s", err) - } + tags, err := ListTagsWithContext(ctx, conn, arn) - arn := aws.StringValue(resp.RuleGroup.ARN) - tags, err := ListTags(conn, arn) if err != nil { - return fmt.Errorf("Error listing tags for WAFv2 RuleGroup (%s): %s", arn, err) + return diag.Errorf("listing tags for WAFv2 RuleGroup (%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) + return diag.Errorf("setting tags: %s", err) } if err := d.Set("tags_all", tags.Map()); err != nil { - return fmt.Errorf("error setting tags_all: %w", err) + return diag.Errorf("setting tags_all: %s", err) } return nil } -func resourceRuleGroupUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceRuleGroupUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).WAFV2Conn - log.Printf("[INFO] Updating WAFv2 RuleGroup %s", d.Id()) + if d.HasChangesExcept("tags", "tags_all") { + input := &wafv2.UpdateRuleGroupInput{ + Id: aws.String(d.Id()), + LockToken: aws.String(d.Get("lock_token").(string)), + Name: aws.String(d.Get("name").(string)), + Rules: expandRules(d.Get("rule").(*schema.Set).List()), + Scope: aws.String(d.Get("scope").(string)), + VisibilityConfig: expandVisibilityConfig(d.Get("visibility_config").([]interface{})), + } - u := &wafv2.UpdateRuleGroupInput{ - Id: aws.String(d.Id()), - Name: aws.String(d.Get("name").(string)), - Scope: aws.String(d.Get("scope").(string)), - LockToken: aws.String(d.Get("lock_token").(string)), - Rules: expandRules(d.Get("rule").(*schema.Set).List()), - VisibilityConfig: expandVisibilityConfig(d.Get("visibility_config").([]interface{})), - } + if v, ok := d.GetOk("custom_response_body"); ok && v.(*schema.Set).Len() > 0 { + input.CustomResponseBodies = expandCustomResponseBodies(v.(*schema.Set).List()) + } - if v, ok := d.GetOk("custom_response_body"); ok && v.(*schema.Set).Len() > 0 { - u.CustomResponseBodies = expandCustomResponseBodies(v.(*schema.Set).List()) - } + if v, ok := d.GetOk("description"); ok { + input.Description = aws.String(v.(string)) + } - if v, ok := d.GetOk("description"); ok { - u.Description = aws.String(v.(string)) - } + log.Printf("[INFO] Updating WAFv2 RuleGroup: %s", input) + _, err := tfresource.RetryWhenAWSErrCodeEqualsContext(ctx, ruleGroupUpdateTimeout, func() (interface{}, error) { + return conn.UpdateRuleGroupWithContext(ctx, input) + }, wafv2.ErrCodeWAFUnavailableEntityException) - err := resource.Retry(5*time.Minute, func() *resource.RetryError { - _, err := conn.UpdateRuleGroup(u) if err != nil { - if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFUnavailableEntityException) { - return resource.RetryableError(err) - } - return resource.NonRetryableError(err) + return diag.Errorf("updating WAFv2 RuleGroup (%s): %s", d.Id(), err) } - return nil - }) - - if tfresource.TimedOut(err) { - _, err = conn.UpdateRuleGroup(u) - } - - if err != nil { - return fmt.Errorf("Error updating WAFv2 RuleGroup: %s", err) } if d.HasChange("tags_all") { o, n := d.GetChange("tags_all") - if err := UpdateTags(conn, d.Get("arn").(string), o, n); err != nil { - return fmt.Errorf("Error updating tags: %s", err) + arn := d.Get("arn").(string) + + if err := UpdateTagsWithContext(ctx, conn, arn, o, n); err != nil { + return diag.Errorf("updating tags for WAFv2 RuleGroup (%s): %s", arn, err) } } - return resourceRuleGroupRead(d, meta) + return resourceRuleGroupRead(ctx, d, meta) } -func resourceRuleGroupDelete(d *schema.ResourceData, meta interface{}) error { +func resourceRuleGroupDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).WAFV2Conn - log.Printf("[INFO] Deleting WAFv2 RuleGroup %s", d.Id()) - - r := &wafv2.DeleteRuleGroupInput{ + input := &wafv2.DeleteRuleGroupInput{ Id: aws.String(d.Id()), + LockToken: aws.String(d.Get("lock_token").(string)), Name: aws.String(d.Get("name").(string)), Scope: aws.String(d.Get("scope").(string)), - LockToken: aws.String(d.Get("lock_token").(string)), } - err := resource.Retry(5*time.Minute, func() *resource.RetryError { - _, err := conn.DeleteRuleGroup(r) - if err != nil { - if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFAssociatedItemException) { - return resource.RetryableError(err) - } - if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFUnavailableEntityException) { - return resource.RetryableError(err) - } - return resource.NonRetryableError(err) - } - return nil - }) + log.Printf("[INFO] Deleting WAFv2 RuleGroup: %s", d.Id()) + _, err := tfresource.RetryWhenAWSErrCodeEqualsContext(ctx, ruleGroupDeleteTimeout, func() (interface{}, error) { + return conn.DeleteRuleGroupWithContext(ctx, input) + }, wafv2.ErrCodeWAFAssociatedItemException, wafv2.ErrCodeWAFUnavailableEntityException) - if tfresource.TimedOut(err) { - _, err = conn.DeleteRuleGroup(r) + if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFNonexistentItemException) { + return nil } if err != nil { - return fmt.Errorf("Error deleting WAFv2 RuleGroup: %s", err) + return diag.Errorf("deleting WAFv2 RuleGroup (%s): %s", d.Id(), err) } return nil } + +func FindRuleGroupByThreePartKey(ctx context.Context, conn *wafv2.WAFV2, id, name, scope string) (*wafv2.GetRuleGroupOutput, error) { + input := &wafv2.GetRuleGroupInput{ + Id: aws.String(id), + Name: aws.String(name), + Scope: aws.String(scope), + } + + output, err := conn.GetRuleGroupWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFNonexistentItemException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.RuleGroup == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, nil +} diff --git a/internal/service/wafv2/rule_group_test.go b/internal/service/wafv2/rule_group_test.go index 407853cd721..2907e05586f 100644 --- a/internal/service/wafv2/rule_group_test.go +++ b/internal/service/wafv2/rule_group_test.go @@ -1,19 +1,20 @@ package wafv2_test import ( + "context" "fmt" "regexp" "testing" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/wafv2" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfwafv2 "github.com/hashicorp/terraform-provider-aws/internal/service/wafv2" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccWAFV2RuleGroup_basic(t *testing.T) { @@ -1880,31 +1881,18 @@ func testAccCheckRuleGroupDestroy(s *terraform.State) error { } conn := acctest.Provider.Meta().(*conns.AWSClient).WAFV2Conn - resp, err := conn.GetRuleGroup( - &wafv2.GetRuleGroupInput{ - Id: aws.String(rs.Primary.ID), - Name: aws.String(rs.Primary.Attributes["name"]), - Scope: aws.String(rs.Primary.Attributes["scope"]), - }) - - if err == nil { - if resp == nil || resp.RuleGroup == nil { - return fmt.Errorf("Error getting WAFv2 RuleGroup") - } - - if aws.StringValue(resp.RuleGroup.Id) == rs.Primary.ID { - return fmt.Errorf("WAFv2 RuleGroup %s still exists", rs.Primary.ID) - } - - return nil + + _, err := tfwafv2.FindRuleGroupByThreePartKey(context.Background(), conn, rs.Primary.ID, rs.Primary.Attributes["name"], rs.Primary.Attributes["scope"]) + + if tfresource.NotFound(err) { + continue } - // Return nil if the RuleGroup is already destroyed - if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFNonexistentItemException) { - return nil + if err != nil { + return err } - return err + return fmt.Errorf("WAFv2 RuleGroup %s still exists", rs.Primary.ID) } return nil @@ -1922,26 +1910,16 @@ func testAccCheckRuleGroupExists(n string, v *wafv2.RuleGroup) resource.TestChec } conn := acctest.Provider.Meta().(*conns.AWSClient).WAFV2Conn - resp, err := conn.GetRuleGroup(&wafv2.GetRuleGroupInput{ - Id: aws.String(rs.Primary.ID), - Name: aws.String(rs.Primary.Attributes["name"]), - Scope: aws.String(rs.Primary.Attributes["scope"]), - }) + + output, err := tfwafv2.FindRuleGroupByThreePartKey(context.Background(), conn, rs.Primary.ID, rs.Primary.Attributes["name"], rs.Primary.Attributes["scope"]) if err != nil { return err } - if resp == nil || resp.RuleGroup == nil { - return fmt.Errorf("Error getting WAFv2 RuleGroup") - } - - if aws.StringValue(resp.RuleGroup.Id) == rs.Primary.ID { - *v = *resp.RuleGroup - return nil - } + *v = *output.RuleGroup - return fmt.Errorf("WAFv2 RuleGroup (%s) not found", rs.Primary.ID) + return nil } } From fc32dd90f2a6e03168fa15f13910bc13b7bdf272 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 27 Sep 2022 15:59:17 -0400 Subject: [PATCH 35/52] r/aws_wafv2_web_acl: Switch to 'WithoutTimeout' CRUD handlers (#15090). Acceptance test output: % make testacc TESTARGS='-run=TestAccWAFV2WebACL_basic\|TestAccWAFV2WebACL_tags\|TestAccWAFV2WebACL_Update' PKG=wafv2 ACCTEST_PARALLELISM=2 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/wafv2/... -v -count 1 -parallel 2 -run=TestAccWAFV2WebACL_basic\|TestAccWAFV2WebACL_tags\|TestAccWAFV2WebACL_Update -timeout 180m === RUN TestAccWAFV2WebACL_basic === PAUSE TestAccWAFV2WebACL_basic === RUN TestAccWAFV2WebACL_Update_rule === PAUSE TestAccWAFV2WebACL_Update_rule === RUN TestAccWAFV2WebACL_Update_ruleProperties === PAUSE TestAccWAFV2WebACL_Update_ruleProperties === RUN TestAccWAFV2WebACL_Update_nameForceNew === PAUSE TestAccWAFV2WebACL_Update_nameForceNew === RUN TestAccWAFV2WebACL_tags === PAUSE TestAccWAFV2WebACL_tags === CONT TestAccWAFV2WebACL_basic === CONT TestAccWAFV2WebACL_Update_nameForceNew --- PASS: TestAccWAFV2WebACL_basic (27.48s) === CONT TestAccWAFV2WebACL_tags --- PASS: TestAccWAFV2WebACL_Update_nameForceNew (44.56s) === CONT TestAccWAFV2WebACL_Update_ruleProperties --- PASS: TestAccWAFV2WebACL_tags (63.42s) === CONT TestAccWAFV2WebACL_Update_rule --- PASS: TestAccWAFV2WebACL_Update_ruleProperties (76.10s) --- PASS: TestAccWAFV2WebACL_Update_rule (51.36s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/wafv2 146.077s --- internal/service/wafv2/flex.go | 445 ++++++++++++++ internal/service/wafv2/schemas.go | 137 +++++ internal/service/wafv2/web_acl.go | 784 +++---------------------- internal/service/wafv2/web_acl_test.go | 48 +- 4 files changed, 689 insertions(+), 725 deletions(-) diff --git a/internal/service/wafv2/flex.go b/internal/service/wafv2/flex.go index 7d322766d8d..63a72afeb61 100644 --- a/internal/service/wafv2/flex.go +++ b/internal/service/wafv2/flex.go @@ -780,6 +780,244 @@ func expandHeaderMatchPattern(l []interface{}) *wafv2.HeaderMatchPattern { return f } +func expandWebACLRules(l []interface{}) []*wafv2.Rule { + if len(l) == 0 || l[0] == nil { + return nil + } + + rules := make([]*wafv2.Rule, 0) + + for _, rule := range l { + if rule == nil { + continue + } + rules = append(rules, expandWebACLRule(rule.(map[string]interface{}))) + } + + return rules +} + +func expandWebACLRule(m map[string]interface{}) *wafv2.Rule { + if m == nil { + return nil + } + + rule := &wafv2.Rule{ + Name: aws.String(m["name"].(string)), + Priority: aws.Int64(int64(m["priority"].(int))), + Action: expandRuleAction(m["action"].([]interface{})), + OverrideAction: expandOverrideAction(m["override_action"].([]interface{})), + Statement: expandWebACLRootStatement(m["statement"].([]interface{})), + VisibilityConfig: expandVisibilityConfig(m["visibility_config"].([]interface{})), + } + + if v, ok := m["rule_label"].(*schema.Set); ok && v.Len() > 0 { + rule.RuleLabels = expandRuleLabels(v.List()) + } + + return rule +} + +func expandOverrideAction(l []interface{}) *wafv2.OverrideAction { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + action := &wafv2.OverrideAction{} + + if v, ok := m["count"]; ok && len(v.([]interface{})) > 0 { + action.Count = &wafv2.CountAction{} + } + + if v, ok := m["none"]; ok && len(v.([]interface{})) > 0 { + action.None = &wafv2.NoneAction{} + } + + return action +} + +func expandDefaultAction(l []interface{}) *wafv2.DefaultAction { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + action := &wafv2.DefaultAction{} + + if v, ok := m["allow"]; ok && len(v.([]interface{})) > 0 { + action.Allow = expandAllowAction(v.([]interface{})) + } + + if v, ok := m["block"]; ok && len(v.([]interface{})) > 0 { + action.Block = expandBlockAction(v.([]interface{})) + } + + return action +} + +func expandWebACLRootStatement(l []interface{}) *wafv2.Statement { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + return expandWebACLStatement(m) +} + +func expandWebACLStatement(m map[string]interface{}) *wafv2.Statement { + if m == nil { + return nil + } + + statement := &wafv2.Statement{} + + if v, ok := m["and_statement"]; ok { + statement.AndStatement = expandAndStatement(v.([]interface{})) + } + + if v, ok := m["byte_match_statement"]; ok { + statement.ByteMatchStatement = expandByteMatchStatement(v.([]interface{})) + } + + if v, ok := m["ip_set_reference_statement"]; ok { + statement.IPSetReferenceStatement = expandIPSetReferenceStatement(v.([]interface{})) + } + + if v, ok := m["geo_match_statement"]; ok { + statement.GeoMatchStatement = expandGeoMatchStatement(v.([]interface{})) + } + + if v, ok := m["label_match_statement"]; ok { + statement.LabelMatchStatement = expandLabelMatchStatement(v.([]interface{})) + } + + if v, ok := m["managed_rule_group_statement"]; ok { + statement.ManagedRuleGroupStatement = expandManagedRuleGroupStatement(v.([]interface{})) + } + + if v, ok := m["not_statement"]; ok { + statement.NotStatement = expandNotStatement(v.([]interface{})) + } + + if v, ok := m["or_statement"]; ok { + statement.OrStatement = expandOrStatement(v.([]interface{})) + } + + if v, ok := m["rate_based_statement"]; ok { + statement.RateBasedStatement = expandRateBasedStatement(v.([]interface{})) + } + + if v, ok := m["regex_pattern_set_reference_statement"]; ok { + statement.RegexPatternSetReferenceStatement = expandRegexPatternSetReferenceStatement(v.([]interface{})) + } + + if v, ok := m["rule_group_reference_statement"]; ok { + statement.RuleGroupReferenceStatement = expandRuleGroupReferenceStatement(v.([]interface{})) + } + + if v, ok := m["size_constraint_statement"]; ok { + statement.SizeConstraintStatement = expandSizeConstraintStatement(v.([]interface{})) + } + + if v, ok := m["sqli_match_statement"]; ok { + statement.SqliMatchStatement = expandSQLiMatchStatement(v.([]interface{})) + } + + if v, ok := m["xss_match_statement"]; ok { + statement.XssMatchStatement = expandXSSMatchStatement(v.([]interface{})) + } + + return statement +} + +func expandManagedRuleGroupStatement(l []interface{}) *wafv2.ManagedRuleGroupStatement { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + r := &wafv2.ManagedRuleGroupStatement{ + ExcludedRules: expandExcludedRules(m["excluded_rule"].([]interface{})), + Name: aws.String(m["name"].(string)), + VendorName: aws.String(m["vendor_name"].(string)), + } + + if s, ok := m["scope_down_statement"].([]interface{}); ok && len(s) > 0 && s[0] != nil { + r.ScopeDownStatement = expandStatement(s[0].(map[string]interface{})) + } + + if v, ok := m["version"]; ok && v != "" { + r.Version = aws.String(v.(string)) + } + + return r +} + +func expandRateBasedStatement(l []interface{}) *wafv2.RateBasedStatement { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + r := &wafv2.RateBasedStatement{ + AggregateKeyType: aws.String(m["aggregate_key_type"].(string)), + Limit: aws.Int64(int64(m["limit"].(int))), + } + + if v, ok := m["forwarded_ip_config"]; ok { + r.ForwardedIPConfig = expandForwardedIPConfig(v.([]interface{})) + } + + s := m["scope_down_statement"].([]interface{}) + if len(s) > 0 && s[0] != nil { + r.ScopeDownStatement = expandStatement(s[0].(map[string]interface{})) + } + + return r +} + +func expandRuleGroupReferenceStatement(l []interface{}) *wafv2.RuleGroupReferenceStatement { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + return &wafv2.RuleGroupReferenceStatement{ + ARN: aws.String(m["arn"].(string)), + ExcludedRules: expandExcludedRules(m["excluded_rule"].([]interface{})), + } +} + +func expandExcludedRules(l []interface{}) []*wafv2.ExcludedRule { + if len(l) == 0 || l[0] == nil { + return nil + } + + rules := make([]*wafv2.ExcludedRule, 0) + + for _, rule := range l { + if rule == nil { + continue + } + rules = append(rules, expandExcludedRule(rule.(map[string]interface{}))) + } + + return rules +} + +func expandExcludedRule(m map[string]interface{}) *wafv2.ExcludedRule { + if m == nil { + return nil + } + + return &wafv2.ExcludedRule{ + Name: aws.String(m["name"].(string)), + } +} + func flattenRules(r []*wafv2.Rule) interface{} { out := make([]map[string]interface{}, len(r)) for i, rule := range r { @@ -1414,3 +1652,210 @@ func flattenHeaderMatchPattern(s *wafv2.HeaderMatchPattern) interface{} { return []interface{}{m} } + +func flattenWebACLRootStatement(s *wafv2.Statement) interface{} { + if s == nil { + return []interface{}{} + } + + return []interface{}{flattenWebACLStatement(s)} +} + +func flattenWebACLStatement(s *wafv2.Statement) map[string]interface{} { + if s == nil { + return map[string]interface{}{} + } + + m := map[string]interface{}{} + + if s.AndStatement != nil { + m["and_statement"] = flattenAndStatement(s.AndStatement) + } + + if s.ByteMatchStatement != nil { + m["byte_match_statement"] = flattenByteMatchStatement(s.ByteMatchStatement) + } + + if s.IPSetReferenceStatement != nil { + m["ip_set_reference_statement"] = flattenIPSetReferenceStatement(s.IPSetReferenceStatement) + } + + if s.GeoMatchStatement != nil { + m["geo_match_statement"] = flattenGeoMatchStatement(s.GeoMatchStatement) + } + + if s.LabelMatchStatement != nil { + m["label_match_statement"] = flattenLabelMatchStatement(s.LabelMatchStatement) + } + + if s.ManagedRuleGroupStatement != nil { + m["managed_rule_group_statement"] = flattenManagedRuleGroupStatement(s.ManagedRuleGroupStatement) + } + + if s.NotStatement != nil { + m["not_statement"] = flattenNotStatement(s.NotStatement) + } + + if s.OrStatement != nil { + m["or_statement"] = flattenOrStatement(s.OrStatement) + } + + if s.RateBasedStatement != nil { + m["rate_based_statement"] = flattenRateBasedStatement(s.RateBasedStatement) + } + + if s.RegexPatternSetReferenceStatement != nil { + m["regex_pattern_set_reference_statement"] = flattenRegexPatternSetReferenceStatement(s.RegexPatternSetReferenceStatement) + } + + if s.RuleGroupReferenceStatement != nil { + m["rule_group_reference_statement"] = flattenRuleGroupReferenceStatement(s.RuleGroupReferenceStatement) + } + + if s.SizeConstraintStatement != nil { + m["size_constraint_statement"] = flattenSizeConstraintStatement(s.SizeConstraintStatement) + } + + if s.SqliMatchStatement != nil { + m["sqli_match_statement"] = flattenSQLiMatchStatement(s.SqliMatchStatement) + } + + if s.XssMatchStatement != nil { + m["xss_match_statement"] = flattenXSSMatchStatement(s.XssMatchStatement) + } + + return m +} + +func flattenWebACLRules(r []*wafv2.Rule) interface{} { + out := make([]map[string]interface{}, len(r)) + for i, rule := range r { + m := make(map[string]interface{}) + m["action"] = flattenRuleAction(rule.Action) + m["override_action"] = flattenOverrideAction(rule.OverrideAction) + m["name"] = aws.StringValue(rule.Name) + m["priority"] = int(aws.Int64Value(rule.Priority)) + m["rule_label"] = flattenRuleLabels(rule.RuleLabels) + m["statement"] = flattenWebACLRootStatement(rule.Statement) + m["visibility_config"] = flattenVisibilityConfig(rule.VisibilityConfig) + out[i] = m + } + + return out +} + +func flattenOverrideAction(a *wafv2.OverrideAction) interface{} { + if a == nil { + return []interface{}{} + } + + m := map[string]interface{}{} + + if a.Count != nil { + m["count"] = make([]map[string]interface{}, 1) + } + + if a.None != nil { + m["none"] = make([]map[string]interface{}, 1) + } + + return []interface{}{m} +} + +func flattenDefaultAction(a *wafv2.DefaultAction) interface{} { + if a == nil { + return []interface{}{} + } + + m := map[string]interface{}{} + + if a.Allow != nil { + m["allow"] = flattenAllow(a.Allow) + } + + if a.Block != nil { + m["block"] = flattenBlock(a.Block) + } + + return []interface{}{m} +} + +func flattenManagedRuleGroupStatement(apiObject *wafv2.ManagedRuleGroupStatement) interface{} { + if apiObject == nil { + return []interface{}{} + } + + tfMap := map[string]interface{}{} + + if apiObject.ExcludedRules != nil { + tfMap["excluded_rule"] = flattenExcludedRules(apiObject.ExcludedRules) + } + + if apiObject.Name != nil { + tfMap["name"] = aws.StringValue(apiObject.Name) + } + + if apiObject.ScopeDownStatement != nil { + tfMap["scope_down_statement"] = []interface{}{flattenStatement(apiObject.ScopeDownStatement)} + } + + if apiObject.VendorName != nil { + tfMap["vendor_name"] = aws.StringValue(apiObject.VendorName) + } + + if apiObject.Version != nil { + tfMap["version"] = aws.StringValue(apiObject.Version) + } + + return []interface{}{tfMap} +} + +func flattenRateBasedStatement(apiObject *wafv2.RateBasedStatement) interface{} { + if apiObject == nil { + return []interface{}{} + } + + tfMap := map[string]interface{}{} + + if apiObject.AggregateKeyType != nil { + tfMap["aggregate_key_type"] = aws.StringValue(apiObject.AggregateKeyType) + } + + if apiObject.ForwardedIPConfig != nil { + tfMap["forwarded_ip_config"] = flattenForwardedIPConfig(apiObject.ForwardedIPConfig) + } + + if apiObject.Limit != nil { + tfMap["limit"] = int(aws.Int64Value(apiObject.Limit)) + } + + if apiObject.ScopeDownStatement != nil { + tfMap["scope_down_statement"] = []interface{}{flattenStatement(apiObject.ScopeDownStatement)} + } + + return []interface{}{tfMap} +} + +func flattenRuleGroupReferenceStatement(r *wafv2.RuleGroupReferenceStatement) interface{} { + if r == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "excluded_rule": flattenExcludedRules(r.ExcludedRules), + "arn": aws.StringValue(r.ARN), + } + + return []interface{}{m} +} + +func flattenExcludedRules(r []*wafv2.ExcludedRule) interface{} { + out := make([]map[string]interface{}, len(r)) + for i, rule := range r { + m := make(map[string]interface{}) + m["name"] = aws.StringValue(rule.Name) + out[i] = m + } + + return out +} diff --git a/internal/service/wafv2/schemas.go b/internal/service/wafv2/schemas.go index 8de2eb8b8af..3ff84be029e 100644 --- a/internal/service/wafv2/schemas.go +++ b/internal/service/wafv2/schemas.go @@ -772,3 +772,140 @@ func headersMatchPatternBaseSchema() *schema.Schema { }, } } + +func webACLRootStatementSchema(level int) *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "and_statement": statementSchema(level), + "byte_match_statement": byteMatchStatementSchema(), + "geo_match_statement": geoMatchStatementSchema(), + "ip_set_reference_statement": ipSetReferenceStatementSchema(), + "label_match_statement": labelMatchStatementSchema(), + "managed_rule_group_statement": managedRuleGroupStatementSchema(level), + "not_statement": statementSchema(level), + "or_statement": statementSchema(level), + "rate_based_statement": rateBasedStatementSchema(level), + "regex_pattern_set_reference_statement": regexPatternSetReferenceStatementSchema(), + "rule_group_reference_statement": ruleGroupReferenceStatementSchema(), + "size_constraint_statement": sizeConstraintSchema(), + "sqli_match_statement": sqliMatchStatementSchema(), + "xss_match_statement": xssMatchStatementSchema(), + }, + }, + } +} + +func managedRuleGroupStatementSchema(level int) *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "excluded_rule": excludedRuleSchema(), + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 128), + }, + "scope_down_statement": scopeDownStatementSchema(level - 1), + "vendor_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 128), + }, + "version": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 128), + }, + }, + }, + } +} + +func excludedRuleSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 128), + }, + }, + }, + } +} + +func rateBasedStatementSchema(level int) *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "aggregate_key_type": { + Type: schema.TypeString, + Optional: true, + Default: wafv2.RateBasedStatementAggregateKeyTypeIp, + ValidateFunc: validation.StringInSlice(wafv2.RateBasedStatementAggregateKeyType_Values(), false), + }, + "forwarded_ip_config": forwardedIPConfigSchema(), + "limit": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(100, 2000000000), + }, + "scope_down_statement": scopeDownStatementSchema(level - 1), + }, + }, + } +} + +func scopeDownStatementSchema(level int) *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "and_statement": statementSchema(level), + "byte_match_statement": byteMatchStatementSchema(), + "geo_match_statement": geoMatchStatementSchema(), + "label_match_statement": labelMatchStatementSchema(), + "ip_set_reference_statement": ipSetReferenceStatementSchema(), + "not_statement": statementSchema(level), + "or_statement": statementSchema(level), + "regex_pattern_set_reference_statement": regexPatternSetReferenceStatementSchema(), + "size_constraint_statement": sizeConstraintSchema(), + "sqli_match_statement": sqliMatchStatementSchema(), + "xss_match_statement": xssMatchStatementSchema(), + }, + }, + } +} + +func ruleGroupReferenceStatementSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Required: true, + ValidateFunc: verify.ValidARN, + }, + "excluded_rule": excludedRuleSchema(), + }, + }, + } +} diff --git a/internal/service/wafv2/web_acl.go b/internal/service/wafv2/web_acl.go index 3f2e799df1c..ddc1a7dfda5 100644 --- a/internal/service/wafv2/web_acl.go +++ b/internal/service/wafv2/web_acl.go @@ -1,6 +1,7 @@ package wafv2 import ( + "context" "fmt" "log" "regexp" @@ -10,6 +11,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/wafv2" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -27,10 +29,11 @@ const ( func ResourceWebACL() *schema.Resource { return &schema.Resource{ - Create: resourceWebACLCreate, - Read: resourceWebACLRead, - Update: resourceWebACLUpdate, - Delete: resourceWebACLDelete, + CreateWithoutTimeout: resourceWebACLCreate, + ReadWithoutTimeout: resourceWebACLRead, + UpdateWithoutTimeout: resourceWebACLUpdate, + DeleteWithoutTimeout: resourceWebACLDelete, + Importer: &schema.ResourceImporter{ State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { idParts := strings.Split(d.Id(), "/") @@ -145,803 +148,202 @@ func ResourceWebACL() *schema.Resource { } } -func resourceWebACLCreate(d *schema.ResourceData, meta interface{}) error { +func resourceWebACLCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).WAFV2Conn defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{}))) - var resp *wafv2.CreateWebACLOutput - params := &wafv2.CreateWebACLInput{ - Name: aws.String(d.Get("name").(string)), - Scope: aws.String(d.Get("scope").(string)), + name := d.Get("name").(string) + input := &wafv2.CreateWebACLInput{ DefaultAction: expandDefaultAction(d.Get("default_action").([]interface{})), + Name: aws.String(name), Rules: expandWebACLRules(d.Get("rule").(*schema.Set).List()), + Scope: aws.String(d.Get("scope").(string)), VisibilityConfig: expandVisibilityConfig(d.Get("visibility_config").([]interface{})), } if v, ok := d.GetOk("custom_response_body"); ok && v.(*schema.Set).Len() > 0 { - params.CustomResponseBodies = expandCustomResponseBodies(v.(*schema.Set).List()) + input.CustomResponseBodies = expandCustomResponseBodies(v.(*schema.Set).List()) } if v, ok := d.GetOk("description"); ok { - params.Description = aws.String(v.(string)) + input.Description = aws.String(v.(string)) } if len(tags) > 0 { - params.Tags = Tags(tags.IgnoreAWS()) + input.Tags = Tags(tags.IgnoreAWS()) } - err := resource.Retry(webACLCreateTimeout, func() *resource.RetryError { - var err error - resp, err = conn.CreateWebACL(params) - if err != nil { - if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFUnavailableEntityException) { - return resource.RetryableError(err) - } - return resource.NonRetryableError(err) - } - return nil - }) - - if tfresource.TimedOut(err) { - resp, err = conn.CreateWebACL(params) - } + log.Printf("[INFO] Creating WAFv2 WebACL: %s", input) + outputRaw, err := tfresource.RetryWhenAWSErrCodeEqualsContext(ctx, webACLCreateTimeout, func() (interface{}, error) { + return conn.CreateWebACLWithContext(ctx, input) + }, wafv2.ErrCodeWAFUnavailableEntityException) if err != nil { - return fmt.Errorf("Error creating WAFv2 WebACL: %w", err) + return diag.Errorf("creating WAFv2 WebACL (%s): %s", name, err) } - if resp == nil || resp.Summary == nil { - return fmt.Errorf("Error creating WAFv2 WebACL") - } + output := outputRaw.(*wafv2.CreateWebACLOutput) - d.SetId(aws.StringValue(resp.Summary.Id)) + d.SetId(aws.StringValue(output.Summary.Id)) - return resourceWebACLRead(d, meta) + return resourceWebACLRead(ctx, d, meta) } -func resourceWebACLRead(d *schema.ResourceData, meta interface{}) error { +func resourceWebACLRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).WAFV2Conn defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - params := &wafv2.GetWebACLInput{ - Id: aws.String(d.Id()), - Name: aws.String(d.Get("name").(string)), - Scope: aws.String(d.Get("scope").(string)), + output, err := FindWebACLByThreePartKey(ctx, conn, d.Id(), d.Get("name").(string), d.Get("scope").(string)) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] WAFv2 WebACL (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil } - resp, err := conn.GetWebACL(params) if err != nil { - if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFNonexistentItemException) { - log.Printf("[WARN] WAFv2 WebACL (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } - return err + return diag.Errorf("reading WAFv2 WebACL (%s): %s", d.Id(), err) } - if resp == nil || resp.WebACL == nil { - return fmt.Errorf("Error getting WAFv2 WebACL") + webACL := output.WebACL + arn := aws.StringValue(webACL.ARN) + d.Set("arn", arn) + d.Set("capacity", webACL.Capacity) + if err := d.Set("custom_response_body", flattenCustomResponseBodies(webACL.CustomResponseBodies)); err != nil { + return diag.Errorf("setting custom_response_body: %s", err) } - - d.Set("name", resp.WebACL.Name) - d.Set("capacity", resp.WebACL.Capacity) - d.Set("description", resp.WebACL.Description) - d.Set("arn", resp.WebACL.ARN) - d.Set("lock_token", resp.LockToken) - - if err := d.Set("custom_response_body", flattenCustomResponseBodies(resp.WebACL.CustomResponseBodies)); err != nil { - return fmt.Errorf("Error setting custom_response_body: %w", err) + if err := d.Set("default_action", flattenDefaultAction(webACL.DefaultAction)); err != nil { + return diag.Errorf("setting default_action: %s", err) } - - if err := d.Set("default_action", flattenDefaultAction(resp.WebACL.DefaultAction)); err != nil { - return fmt.Errorf("Error setting default_action: %w", err) + d.Set("description", webACL.Description) + d.Set("lock_token", output.LockToken) + d.Set("name", webACL.Name) + if err := d.Set("rule", flattenWebACLRules(webACL.Rules)); err != nil { + return diag.Errorf("setting rule: %s", err) } - - if err := d.Set("rule", flattenWebACLRules(resp.WebACL.Rules)); err != nil { - return fmt.Errorf("Error setting rule: %w", err) + if err := d.Set("visibility_config", flattenVisibilityConfig(webACL.VisibilityConfig)); err != nil { + return diag.Errorf("setting visibility_config: %s", err) } - if err := d.Set("visibility_config", flattenVisibilityConfig(resp.WebACL.VisibilityConfig)); err != nil { - return fmt.Errorf("Error setting visibility_config: %w", err) - } + tags, err := ListTagsWithContext(ctx, conn, arn) - arn := aws.StringValue(resp.WebACL.ARN) - tags, err := ListTags(conn, arn) if err != nil { - return fmt.Errorf("Error listing tags for WAFv2 WebACL (%s): %w", arn, err) + return diag.Errorf("listing tags for WAFv2 WebACL (%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) + return diag.Errorf("setting tags: %s", err) } if err := d.Set("tags_all", tags.Map()); err != nil { - return fmt.Errorf("error setting tags_all: %w", err) + return diag.Errorf("setting tags_all: %s", err) } return nil } -func resourceWebACLUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceWebACLUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).WAFV2Conn - if d.HasChanges("custom_response_body", "default_action", "description", "rule", "visibility_config") { - u := &wafv2.UpdateWebACLInput{ + if d.HasChangesExcept("tags", "tags_all") { + input := &wafv2.UpdateWebACLInput{ + DefaultAction: expandDefaultAction(d.Get("default_action").([]interface{})), Id: aws.String(d.Id()), - Name: aws.String(d.Get("name").(string)), - Scope: aws.String(d.Get("scope").(string)), LockToken: aws.String(d.Get("lock_token").(string)), - DefaultAction: expandDefaultAction(d.Get("default_action").([]interface{})), + Name: aws.String(d.Get("name").(string)), Rules: expandWebACLRules(d.Get("rule").(*schema.Set).List()), + Scope: aws.String(d.Get("scope").(string)), VisibilityConfig: expandVisibilityConfig(d.Get("visibility_config").([]interface{})), } if v, ok := d.GetOk("custom_response_body"); ok && v.(*schema.Set).Len() > 0 { - u.CustomResponseBodies = expandCustomResponseBodies(v.(*schema.Set).List()) + input.CustomResponseBodies = expandCustomResponseBodies(v.(*schema.Set).List()) } if v, ok := d.GetOk("description"); ok { - u.Description = aws.String(v.(string)) + input.Description = aws.String(v.(string)) } - err := resource.Retry(webACLUpdateTimeout, func() *resource.RetryError { - _, err := conn.UpdateWebACL(u) - if err != nil { - if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFUnavailableEntityException) { - return resource.RetryableError(err) - } - return resource.NonRetryableError(err) - } - return nil - }) + log.Printf("[INFO] Updating WAFv2 WebACL: %s", input) + _, err := tfresource.RetryWhenAWSErrCodeEqualsContext(ctx, webACLUpdateTimeout, func() (interface{}, error) { + return conn.UpdateWebACLWithContext(ctx, input) + }, wafv2.ErrCodeWAFUnavailableEntityException) - if tfresource.TimedOut(err) { - _, err = conn.UpdateWebACL(u) + if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFOptimisticLockException) { + return diag.Errorf("updating WAFv2 WebACL (%s), resource has changed since last refresh please run a new plan before applying again: %s", d.Id(), err) } if err != nil { - if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFOptimisticLockException) { - return fmt.Errorf("Error updating WAFv2 WebACL, resource has changed since last refresh please run a new plan before applying again: %w", err) - } - return fmt.Errorf("Error updating WAFv2 WebACL: %w", err) + return diag.Errorf("updating WAFv2 WebACL (%s): %s", d.Id(), err) } } if d.HasChange("tags_all") { o, n := d.GetChange("tags_all") - if err := UpdateTags(conn, d.Get("arn").(string), o, n); err != nil { - return fmt.Errorf("error updating tags: %w", err) + arn := d.Get("arn").(string) + + if err := UpdateTagsWithContext(ctx, conn, arn, o, n); err != nil { + return diag.Errorf("updating tags for WAFv2 WebACL (%s): %s", arn, err) } } - return resourceWebACLRead(d, meta) + return resourceWebACLRead(ctx, d, meta) } -func resourceWebACLDelete(d *schema.ResourceData, meta interface{}) error { +func resourceWebACLDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).WAFV2Conn - log.Printf("[INFO] Deleting WAFv2 WebACL %s", d.Id()) - - r := &wafv2.DeleteWebACLInput{ + input := &wafv2.DeleteWebACLInput{ Id: aws.String(d.Id()), + LockToken: aws.String(d.Get("lock_token").(string)), Name: aws.String(d.Get("name").(string)), Scope: aws.String(d.Get("scope").(string)), - LockToken: aws.String(d.Get("lock_token").(string)), } - err := resource.Retry(webACLDeleteTimeout, func() *resource.RetryError { - _, err := conn.DeleteWebACL(r) - if err != nil { - if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFAssociatedItemException) { - return resource.RetryableError(err) - } - if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFUnavailableEntityException) { - return resource.RetryableError(err) - } - return resource.NonRetryableError(err) - } - return nil - }) - - if tfresource.TimedOut(err) { - _, err = conn.DeleteWebACL(r) - } + log.Printf("[INFO] Deleting WAFv2 WebACL: %s", d.Id()) + _, err := tfresource.RetryWhenAWSErrCodeEqualsContext(ctx, webACLDeleteTimeout, func() (interface{}, error) { + return conn.DeleteWebACLWithContext(ctx, input) + }, wafv2.ErrCodeWAFAssociatedItemException, wafv2.ErrCodeWAFUnavailableEntityException) if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFNonexistentItemException) { return nil } if err != nil { - return fmt.Errorf("Error deleting WAFv2 WebACL: %w", err) + return diag.Errorf("deleting WAFv2 WebACL (%s): %s", d.Id(), err) } return nil } -func webACLRootStatementSchema(level int) *schema.Schema { - return &schema.Schema{ - Type: schema.TypeList, - Required: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "and_statement": statementSchema(level), - "byte_match_statement": byteMatchStatementSchema(), - "geo_match_statement": geoMatchStatementSchema(), - "ip_set_reference_statement": ipSetReferenceStatementSchema(), - "label_match_statement": labelMatchStatementSchema(), - "managed_rule_group_statement": managedRuleGroupStatementSchema(level), - "not_statement": statementSchema(level), - "or_statement": statementSchema(level), - "rate_based_statement": rateBasedStatementSchema(level), - "regex_pattern_set_reference_statement": regexPatternSetReferenceStatementSchema(), - "rule_group_reference_statement": ruleGroupReferenceStatementSchema(), - "size_constraint_statement": sizeConstraintSchema(), - "sqli_match_statement": sqliMatchStatementSchema(), - "xss_match_statement": xssMatchStatementSchema(), - }, - }, - } -} - -func managedRuleGroupStatementSchema(level int) *schema.Schema { - return &schema.Schema{ - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "excluded_rule": excludedRuleSchema(), - "name": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringLenBetween(1, 128), - }, - "scope_down_statement": scopeDownStatementSchema(level - 1), - "vendor_name": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringLenBetween(1, 128), - }, - "version": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringLenBetween(1, 128), - }, - }, - }, - } -} - -func excludedRuleSchema() *schema.Schema { - return &schema.Schema{ - Type: schema.TypeList, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringLenBetween(1, 128), - }, - }, - }, - } -} - -func rateBasedStatementSchema(level int) *schema.Schema { - return &schema.Schema{ - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - // Required field - "aggregate_key_type": { - Type: schema.TypeString, - Optional: true, - Default: wafv2.RateBasedStatementAggregateKeyTypeIp, - ValidateFunc: validation.StringInSlice(wafv2.RateBasedStatementAggregateKeyType_Values(), false), - }, - "forwarded_ip_config": forwardedIPConfigSchema(), - "limit": { - Type: schema.TypeInt, - Required: true, - ValidateFunc: validation.IntBetween(100, 2000000000), - }, - "scope_down_statement": scopeDownStatementSchema(level - 1), - }, - }, - } -} - -func scopeDownStatementSchema(level int) *schema.Schema { - return &schema.Schema{ - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "and_statement": statementSchema(level), - "byte_match_statement": byteMatchStatementSchema(), - "geo_match_statement": geoMatchStatementSchema(), - "label_match_statement": labelMatchStatementSchema(), - "ip_set_reference_statement": ipSetReferenceStatementSchema(), - "not_statement": statementSchema(level), - "or_statement": statementSchema(level), - "regex_pattern_set_reference_statement": regexPatternSetReferenceStatementSchema(), - "size_constraint_statement": sizeConstraintSchema(), - "sqli_match_statement": sqliMatchStatementSchema(), - "xss_match_statement": xssMatchStatementSchema(), - }, - }, - } -} - -func ruleGroupReferenceStatementSchema() *schema.Schema { - return &schema.Schema{ - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "arn": { - Type: schema.TypeString, - Required: true, - ValidateFunc: verify.ValidARN, - }, - "excluded_rule": excludedRuleSchema(), - }, - }, - } -} - -func expandWebACLRules(l []interface{}) []*wafv2.Rule { - if len(l) == 0 || l[0] == nil { - return nil - } - - rules := make([]*wafv2.Rule, 0) - - for _, rule := range l { - if rule == nil { - continue - } - rules = append(rules, expandWebACLRule(rule.(map[string]interface{}))) - } - - return rules -} - -func expandWebACLRule(m map[string]interface{}) *wafv2.Rule { - if m == nil { - return nil - } - - rule := &wafv2.Rule{ - Name: aws.String(m["name"].(string)), - Priority: aws.Int64(int64(m["priority"].(int))), - Action: expandRuleAction(m["action"].([]interface{})), - OverrideAction: expandOverrideAction(m["override_action"].([]interface{})), - Statement: expandWebACLRootStatement(m["statement"].([]interface{})), - VisibilityConfig: expandVisibilityConfig(m["visibility_config"].([]interface{})), - } - - if v, ok := m["rule_label"].(*schema.Set); ok && v.Len() > 0 { - rule.RuleLabels = expandRuleLabels(v.List()) - } - - return rule -} - -func expandOverrideAction(l []interface{}) *wafv2.OverrideAction { - if len(l) == 0 || l[0] == nil { - return nil - } - - m := l[0].(map[string]interface{}) - action := &wafv2.OverrideAction{} - - if v, ok := m["count"]; ok && len(v.([]interface{})) > 0 { - action.Count = &wafv2.CountAction{} - } - - if v, ok := m["none"]; ok && len(v.([]interface{})) > 0 { - action.None = &wafv2.NoneAction{} - } - - return action -} - -func expandDefaultAction(l []interface{}) *wafv2.DefaultAction { - if len(l) == 0 || l[0] == nil { - return nil - } - - m := l[0].(map[string]interface{}) - action := &wafv2.DefaultAction{} - - if v, ok := m["allow"]; ok && len(v.([]interface{})) > 0 { - action.Allow = expandAllowAction(v.([]interface{})) - } - - if v, ok := m["block"]; ok && len(v.([]interface{})) > 0 { - action.Block = expandBlockAction(v.([]interface{})) - } - - return action -} - -func expandWebACLRootStatement(l []interface{}) *wafv2.Statement { - if len(l) == 0 || l[0] == nil { - return nil - } - - m := l[0].(map[string]interface{}) - - return expandWebACLStatement(m) -} - -func expandWebACLStatement(m map[string]interface{}) *wafv2.Statement { - if m == nil { - return nil +func FindWebACLByThreePartKey(ctx context.Context, conn *wafv2.WAFV2, id, name, scope string) (*wafv2.GetWebACLOutput, error) { + input := &wafv2.GetWebACLInput{ + Id: aws.String(id), + Name: aws.String(name), + Scope: aws.String(scope), } - statement := &wafv2.Statement{} - - if v, ok := m["and_statement"]; ok { - statement.AndStatement = expandAndStatement(v.([]interface{})) - } - - if v, ok := m["byte_match_statement"]; ok { - statement.ByteMatchStatement = expandByteMatchStatement(v.([]interface{})) - } - - if v, ok := m["ip_set_reference_statement"]; ok { - statement.IPSetReferenceStatement = expandIPSetReferenceStatement(v.([]interface{})) - } - - if v, ok := m["geo_match_statement"]; ok { - statement.GeoMatchStatement = expandGeoMatchStatement(v.([]interface{})) - } + output, err := conn.GetWebACLWithContext(ctx, input) - if v, ok := m["label_match_statement"]; ok { - statement.LabelMatchStatement = expandLabelMatchStatement(v.([]interface{})) - } - - if v, ok := m["managed_rule_group_statement"]; ok { - statement.ManagedRuleGroupStatement = expandManagedRuleGroupStatement(v.([]interface{})) - } - - if v, ok := m["not_statement"]; ok { - statement.NotStatement = expandNotStatement(v.([]interface{})) - } - - if v, ok := m["or_statement"]; ok { - statement.OrStatement = expandOrStatement(v.([]interface{})) - } - - if v, ok := m["rate_based_statement"]; ok { - statement.RateBasedStatement = expandRateBasedStatement(v.([]interface{})) - } - - if v, ok := m["regex_pattern_set_reference_statement"]; ok { - statement.RegexPatternSetReferenceStatement = expandRegexPatternSetReferenceStatement(v.([]interface{})) - } - - if v, ok := m["rule_group_reference_statement"]; ok { - statement.RuleGroupReferenceStatement = expandRuleGroupReferenceStatement(v.([]interface{})) - } - - if v, ok := m["size_constraint_statement"]; ok { - statement.SizeConstraintStatement = expandSizeConstraintStatement(v.([]interface{})) - } - - if v, ok := m["sqli_match_statement"]; ok { - statement.SqliMatchStatement = expandSQLiMatchStatement(v.([]interface{})) - } - - if v, ok := m["xss_match_statement"]; ok { - statement.XssMatchStatement = expandXSSMatchStatement(v.([]interface{})) - } - - return statement -} - -func expandManagedRuleGroupStatement(l []interface{}) *wafv2.ManagedRuleGroupStatement { - if len(l) == 0 || l[0] == nil { - return nil - } - - m := l[0].(map[string]interface{}) - r := &wafv2.ManagedRuleGroupStatement{ - ExcludedRules: expandExcludedRules(m["excluded_rule"].([]interface{})), - Name: aws.String(m["name"].(string)), - VendorName: aws.String(m["vendor_name"].(string)), - } - - if s, ok := m["scope_down_statement"].([]interface{}); ok && len(s) > 0 && s[0] != nil { - r.ScopeDownStatement = expandStatement(s[0].(map[string]interface{})) - } - - if v, ok := m["version"]; ok && v != "" { - r.Version = aws.String(v.(string)) - } - - return r -} - -func expandRateBasedStatement(l []interface{}) *wafv2.RateBasedStatement { - if len(l) == 0 || l[0] == nil { - return nil - } - - m := l[0].(map[string]interface{}) - r := &wafv2.RateBasedStatement{ - AggregateKeyType: aws.String(m["aggregate_key_type"].(string)), - Limit: aws.Int64(int64(m["limit"].(int))), - } - - if v, ok := m["forwarded_ip_config"]; ok { - r.ForwardedIPConfig = expandForwardedIPConfig(v.([]interface{})) - } - - s := m["scope_down_statement"].([]interface{}) - if len(s) > 0 && s[0] != nil { - r.ScopeDownStatement = expandStatement(s[0].(map[string]interface{})) - } - - return r -} - -func expandRuleGroupReferenceStatement(l []interface{}) *wafv2.RuleGroupReferenceStatement { - if len(l) == 0 || l[0] == nil { - return nil - } - - m := l[0].(map[string]interface{}) - - return &wafv2.RuleGroupReferenceStatement{ - ARN: aws.String(m["arn"].(string)), - ExcludedRules: expandExcludedRules(m["excluded_rule"].([]interface{})), - } -} - -func expandExcludedRules(l []interface{}) []*wafv2.ExcludedRule { - if len(l) == 0 || l[0] == nil { - return nil - } - - rules := make([]*wafv2.ExcludedRule, 0) - - for _, rule := range l { - if rule == nil { - continue + if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFNonexistentItemException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, } - rules = append(rules, expandExcludedRule(rule.(map[string]interface{}))) - } - - return rules -} - -func expandExcludedRule(m map[string]interface{}) *wafv2.ExcludedRule { - if m == nil { - return nil - } - - return &wafv2.ExcludedRule{ - Name: aws.String(m["name"].(string)), - } -} - -func flattenWebACLRootStatement(s *wafv2.Statement) interface{} { - if s == nil { - return []interface{}{} - } - - return []interface{}{flattenWebACLStatement(s)} -} - -func flattenWebACLStatement(s *wafv2.Statement) map[string]interface{} { - if s == nil { - return map[string]interface{}{} - } - - m := map[string]interface{}{} - - if s.AndStatement != nil { - m["and_statement"] = flattenAndStatement(s.AndStatement) - } - - if s.ByteMatchStatement != nil { - m["byte_match_statement"] = flattenByteMatchStatement(s.ByteMatchStatement) - } - - if s.IPSetReferenceStatement != nil { - m["ip_set_reference_statement"] = flattenIPSetReferenceStatement(s.IPSetReferenceStatement) - } - - if s.GeoMatchStatement != nil { - m["geo_match_statement"] = flattenGeoMatchStatement(s.GeoMatchStatement) - } - - if s.LabelMatchStatement != nil { - m["label_match_statement"] = flattenLabelMatchStatement(s.LabelMatchStatement) - } - - if s.ManagedRuleGroupStatement != nil { - m["managed_rule_group_statement"] = flattenManagedRuleGroupStatement(s.ManagedRuleGroupStatement) - } - - if s.NotStatement != nil { - m["not_statement"] = flattenNotStatement(s.NotStatement) - } - - if s.OrStatement != nil { - m["or_statement"] = flattenOrStatement(s.OrStatement) } - if s.RateBasedStatement != nil { - m["rate_based_statement"] = flattenRateBasedStatement(s.RateBasedStatement) - } - - if s.RegexPatternSetReferenceStatement != nil { - m["regex_pattern_set_reference_statement"] = flattenRegexPatternSetReferenceStatement(s.RegexPatternSetReferenceStatement) - } - - if s.RuleGroupReferenceStatement != nil { - m["rule_group_reference_statement"] = flattenRuleGroupReferenceStatement(s.RuleGroupReferenceStatement) - } - - if s.SizeConstraintStatement != nil { - m["size_constraint_statement"] = flattenSizeConstraintStatement(s.SizeConstraintStatement) - } - - if s.SqliMatchStatement != nil { - m["sqli_match_statement"] = flattenSQLiMatchStatement(s.SqliMatchStatement) - } - - if s.XssMatchStatement != nil { - m["xss_match_statement"] = flattenXSSMatchStatement(s.XssMatchStatement) - } - - return m -} - -func flattenWebACLRules(r []*wafv2.Rule) interface{} { - out := make([]map[string]interface{}, len(r)) - for i, rule := range r { - m := make(map[string]interface{}) - m["action"] = flattenRuleAction(rule.Action) - m["override_action"] = flattenOverrideAction(rule.OverrideAction) - m["name"] = aws.StringValue(rule.Name) - m["priority"] = int(aws.Int64Value(rule.Priority)) - m["rule_label"] = flattenRuleLabels(rule.RuleLabels) - m["statement"] = flattenWebACLRootStatement(rule.Statement) - m["visibility_config"] = flattenVisibilityConfig(rule.VisibilityConfig) - out[i] = m - } - - return out -} - -func flattenOverrideAction(a *wafv2.OverrideAction) interface{} { - if a == nil { - return []interface{}{} - } - - m := map[string]interface{}{} - - if a.Count != nil { - m["count"] = make([]map[string]interface{}, 1) - } - - if a.None != nil { - m["none"] = make([]map[string]interface{}, 1) - } - - return []interface{}{m} -} - -func flattenDefaultAction(a *wafv2.DefaultAction) interface{} { - if a == nil { - return []interface{}{} - } - - m := map[string]interface{}{} - - if a.Allow != nil { - m["allow"] = flattenAllow(a.Allow) - } - - if a.Block != nil { - m["block"] = flattenBlock(a.Block) - } - - return []interface{}{m} -} - -func flattenManagedRuleGroupStatement(apiObject *wafv2.ManagedRuleGroupStatement) interface{} { - if apiObject == nil { - return []interface{}{} - } - - tfMap := map[string]interface{}{} - - if apiObject.ExcludedRules != nil { - tfMap["excluded_rule"] = flattenExcludedRules(apiObject.ExcludedRules) - } - - if apiObject.Name != nil { - tfMap["name"] = aws.StringValue(apiObject.Name) - } - - if apiObject.ScopeDownStatement != nil { - tfMap["scope_down_statement"] = []interface{}{flattenStatement(apiObject.ScopeDownStatement)} - } - - if apiObject.VendorName != nil { - tfMap["vendor_name"] = aws.StringValue(apiObject.VendorName) - } - - if apiObject.Version != nil { - tfMap["version"] = aws.StringValue(apiObject.Version) - } - - return []interface{}{tfMap} -} - -func flattenRateBasedStatement(apiObject *wafv2.RateBasedStatement) interface{} { - if apiObject == nil { - return []interface{}{} - } - - tfMap := map[string]interface{}{} - - if apiObject.AggregateKeyType != nil { - tfMap["aggregate_key_type"] = aws.StringValue(apiObject.AggregateKeyType) - } - - if apiObject.ForwardedIPConfig != nil { - tfMap["forwarded_ip_config"] = flattenForwardedIPConfig(apiObject.ForwardedIPConfig) - } - - if apiObject.Limit != nil { - tfMap["limit"] = int(aws.Int64Value(apiObject.Limit)) - } - - if apiObject.ScopeDownStatement != nil { - tfMap["scope_down_statement"] = []interface{}{flattenStatement(apiObject.ScopeDownStatement)} - } - - return []interface{}{tfMap} -} - -func flattenRuleGroupReferenceStatement(r *wafv2.RuleGroupReferenceStatement) interface{} { - if r == nil { - return []interface{}{} - } - - m := map[string]interface{}{ - "excluded_rule": flattenExcludedRules(r.ExcludedRules), - "arn": aws.StringValue(r.ARN), + if err != nil { + return nil, err } - return []interface{}{m} -} - -func flattenExcludedRules(r []*wafv2.ExcludedRule) interface{} { - out := make([]map[string]interface{}, len(r)) - for i, rule := range r { - m := make(map[string]interface{}) - m["name"] = aws.StringValue(rule.Name) - out[i] = m + if output == nil || output.WebACL == nil { + return nil, tfresource.NewEmptyResultError(input) } - return out + return output, nil } diff --git a/internal/service/wafv2/web_acl_test.go b/internal/service/wafv2/web_acl_test.go index 300b7f3f6a7..404353fd9be 100644 --- a/internal/service/wafv2/web_acl_test.go +++ b/internal/service/wafv2/web_acl_test.go @@ -1,19 +1,19 @@ package wafv2_test import ( + "context" "fmt" "regexp" "testing" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/wafv2" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfwafv2 "github.com/hashicorp/terraform-provider-aws/internal/service/wafv2" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccWAFV2WebACL_basic(t *testing.T) { @@ -1775,28 +1775,18 @@ func testAccCheckWebACLDestroy(s *terraform.State) error { } conn := acctest.Provider.Meta().(*conns.AWSClient).WAFV2Conn - resp, err := conn.GetWebACL( - &wafv2.GetWebACLInput{ - Id: aws.String(rs.Primary.ID), - Name: aws.String(rs.Primary.Attributes["name"]), - Scope: aws.String(rs.Primary.Attributes["scope"]), - }) - - if err == nil { - if resp == nil || resp.WebACL == nil { - return fmt.Errorf("Error getting WAFv2 WebACL") - } - if aws.StringValue(resp.WebACL.Id) == rs.Primary.ID { - return fmt.Errorf("WAFv2 WebACL %s still exists", rs.Primary.ID) - } + + _, err := tfwafv2.FindWebACLByThreePartKey(context.Background(), conn, rs.Primary.ID, rs.Primary.Attributes["name"], rs.Primary.Attributes["scope"]) + + if tfresource.NotFound(err) { + continue } - // Return nil if the WebACL is already destroyed - if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFNonexistentItemException) { - return nil + if err != nil { + return err } - return err + return fmt.Errorf("WAFv2 WebACL %s still exists", rs.Primary.ID) } return nil @@ -1814,26 +1804,16 @@ func testAccCheckWebACLExists(n string, v *wafv2.WebACL) resource.TestCheckFunc } conn := acctest.Provider.Meta().(*conns.AWSClient).WAFV2Conn - resp, err := conn.GetWebACL(&wafv2.GetWebACLInput{ - Id: aws.String(rs.Primary.ID), - Name: aws.String(rs.Primary.Attributes["name"]), - Scope: aws.String(rs.Primary.Attributes["scope"]), - }) + + output, err := tfwafv2.FindWebACLByThreePartKey(context.Background(), conn, rs.Primary.ID, rs.Primary.Attributes["name"], rs.Primary.Attributes["scope"]) if err != nil { return err } - if resp == nil || resp.WebACL == nil { - return fmt.Errorf("Error getting WAFv2 WebACL") - } - - if aws.StringValue(resp.WebACL.Id) == rs.Primary.ID { - *v = *resp.WebACL - return nil - } + *v = *output.WebACL - return fmt.Errorf("WAFv2 WebACL (%s) not found", rs.Primary.ID) + return nil } } From 15d101e4ac16d47432ce8780f077e211b3743368 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 27 Sep 2022 16:31:25 -0400 Subject: [PATCH 36/52] WAFv2: Simplify sweepers. Acceptance test output: % make sweep SWEEPARGS=-sweep-run=aws_wafv2_ip_set,aws_wafv2_regex_pattern_set SWEEP=us-west-2,us-east-1,us-east-2 WARNING: This will destroy infrastructure. Use only in development accounts. go test ./internal/sweep -v -tags=sweep -sweep=us-west-2,us-east-1,us-east-2 -sweep-run=aws_wafv2_ip_set,aws_wafv2_regex_pattern_set -timeout 60m 2022/09/27 16:24:18 [DEBUG] Running Sweepers for region (us-west-2): 2022/09/27 16:24:18 [DEBUG] Sweeper (aws_wafv2_ip_set) has dependency (aws_wafv2_rule_group), running.. 2022/09/27 16:24:18 [DEBUG] Sweeper (aws_wafv2_rule_group) has dependency (aws_wafv2_web_acl), running.. 2022/09/27 16:24:18 [DEBUG] Running Sweeper (aws_wafv2_web_acl) in region (us-west-2) 2022/09/27 16:24:18 [INFO] Retrieved credentials from "EnvConfigCredentials" 2022/09/27 16:24:18 [DEBUG] Trying to get account information via sts:GetCallerIdentity 2022/09/27 16:24:18 [DEBUG] Trying to get account information via sts:GetCallerIdentity 2022/09/27 16:24:19 [WARN] Skipping WAFv2 Web ACL: FMManagedWebACLV2geo-monitor1618325431341 2022/09/27 16:24:19 [WARN] Skipping WAFv2 Web ACL: FMManagedWebACLV2geo-restriction1618325439490 2022/09/27 16:24:19 [DEBUG] Completed Sweeper (aws_wafv2_web_acl) in region (us-west-2) in 1.594607143s 2022/09/27 16:24:19 [DEBUG] Running Sweeper (aws_wafv2_rule_group) in region (us-west-2) 2022/09/27 16:24:20 [DEBUG] Completed Sweeper (aws_wafv2_rule_group) in region (us-west-2) in 97.142267ms 2022/09/27 16:24:20 [DEBUG] Sweeper (aws_wafv2_ip_set) has dependency (aws_wafv2_web_acl), running.. 2022/09/27 16:24:20 [DEBUG] Sweeper (aws_wafv2_web_acl) already ran in region (us-west-2) 2022/09/27 16:24:20 [DEBUG] Running Sweeper (aws_wafv2_ip_set) in region (us-west-2) 2022/09/27 16:24:20 [DEBUG] Completed Sweeper (aws_wafv2_ip_set) in region (us-west-2) in 98.726895ms 2022/09/27 16:24:20 [DEBUG] Sweeper (aws_wafv2_rule_group) has dependency (aws_wafv2_web_acl), running.. 2022/09/27 16:24:20 [DEBUG] Sweeper (aws_wafv2_web_acl) already ran in region (us-west-2) 2022/09/27 16:24:20 [DEBUG] Sweeper (aws_wafv2_rule_group) already ran in region (us-west-2) 2022/09/27 16:24:20 [DEBUG] Sweeper (aws_wafv2_web_acl) already ran in region (us-west-2) 2022/09/27 16:24:20 [DEBUG] Sweeper (aws_wafv2_regex_pattern_set) has dependency (aws_wafv2_rule_group), running.. 2022/09/27 16:24:20 [DEBUG] Sweeper (aws_wafv2_rule_group) has dependency (aws_wafv2_web_acl), running.. 2022/09/27 16:24:20 [DEBUG] Sweeper (aws_wafv2_web_acl) already ran in region (us-west-2) 2022/09/27 16:24:20 [DEBUG] Sweeper (aws_wafv2_rule_group) already ran in region (us-west-2) 2022/09/27 16:24:20 [DEBUG] Sweeper (aws_wafv2_regex_pattern_set) has dependency (aws_wafv2_web_acl), running.. 2022/09/27 16:24:20 [DEBUG] Sweeper (aws_wafv2_web_acl) already ran in region (us-west-2) 2022/09/27 16:24:20 [DEBUG] Running Sweeper (aws_wafv2_regex_pattern_set) in region (us-west-2) 2022/09/27 16:24:20 [DEBUG] Completed Sweeper (aws_wafv2_regex_pattern_set) in region (us-west-2) in 97.908865ms 2022/09/27 16:24:20 Completed Sweepers for region (us-west-2) in 1.888746085s 2022/09/27 16:24:20 Sweeper Tests for region (us-west-2) ran successfully: - aws_wafv2_rule_group - aws_wafv2_ip_set - aws_wafv2_regex_pattern_set - aws_wafv2_web_acl 2022/09/27 16:24:20 [DEBUG] Running Sweepers for region (us-east-1): 2022/09/27 16:24:20 [DEBUG] Sweeper (aws_wafv2_ip_set) has dependency (aws_wafv2_rule_group), running.. 2022/09/27 16:24:20 [DEBUG] Sweeper (aws_wafv2_rule_group) has dependency (aws_wafv2_web_acl), running.. 2022/09/27 16:24:20 [DEBUG] Running Sweeper (aws_wafv2_web_acl) in region (us-east-1) 2022/09/27 16:24:20 [INFO] Retrieved credentials from "EnvConfigCredentials" 2022/09/27 16:24:20 [DEBUG] Trying to get account information via sts:GetCallerIdentity 2022/09/27 16:24:20 [DEBUG] Trying to get account information via sts:GetCallerIdentity 2022/09/27 16:24:22 [WARN] Skipping WAFv2 Web ACL: FMManagedWebACLV2geo-monitor1618326194265 2022/09/27 16:24:22 [WARN] Skipping WAFv2 Web ACL: FMManagedWebACLV2geo-restriction1618325735588 2022/09/27 16:24:22 [DEBUG] Completed Sweeper (aws_wafv2_web_acl) in region (us-east-1) in 2.586829216s 2022/09/27 16:24:22 [DEBUG] Running Sweeper (aws_wafv2_rule_group) in region (us-east-1) 2022/09/27 16:24:22 [DEBUG] Completed Sweeper (aws_wafv2_rule_group) in region (us-east-1) in 36.456855ms 2022/09/27 16:24:22 [DEBUG] Sweeper (aws_wafv2_ip_set) has dependency (aws_wafv2_web_acl), running.. 2022/09/27 16:24:22 [DEBUG] Sweeper (aws_wafv2_web_acl) already ran in region (us-east-1) 2022/09/27 16:24:22 [DEBUG] Running Sweeper (aws_wafv2_ip_set) in region (us-east-1) 2022/09/27 16:24:22 [DEBUG] Completed Sweeper (aws_wafv2_ip_set) in region (us-east-1) in 29.891282ms 2022/09/27 16:24:22 [DEBUG] Sweeper (aws_wafv2_rule_group) has dependency (aws_wafv2_web_acl), running.. 2022/09/27 16:24:22 [DEBUG] Sweeper (aws_wafv2_web_acl) already ran in region (us-east-1) 2022/09/27 16:24:22 [DEBUG] Sweeper (aws_wafv2_rule_group) already ran in region (us-east-1) 2022/09/27 16:24:22 [DEBUG] Sweeper (aws_wafv2_web_acl) already ran in region (us-east-1) 2022/09/27 16:24:22 [DEBUG] Sweeper (aws_wafv2_regex_pattern_set) has dependency (aws_wafv2_rule_group), running.. 2022/09/27 16:24:22 [DEBUG] Sweeper (aws_wafv2_rule_group) has dependency (aws_wafv2_web_acl), running.. 2022/09/27 16:24:22 [DEBUG] Sweeper (aws_wafv2_web_acl) already ran in region (us-east-1) 2022/09/27 16:24:22 [DEBUG] Sweeper (aws_wafv2_rule_group) already ran in region (us-east-1) 2022/09/27 16:24:22 [DEBUG] Sweeper (aws_wafv2_regex_pattern_set) has dependency (aws_wafv2_web_acl), running.. 2022/09/27 16:24:22 [DEBUG] Sweeper (aws_wafv2_web_acl) already ran in region (us-east-1) 2022/09/27 16:24:22 [DEBUG] Running Sweeper (aws_wafv2_regex_pattern_set) in region (us-east-1) 2022/09/27 16:24:22 [DEBUG] Completed Sweeper (aws_wafv2_regex_pattern_set) in region (us-east-1) in 27.529384ms 2022/09/27 16:24:22 Completed Sweepers for region (us-east-1) in 2.680838281s 2022/09/27 16:24:22 Sweeper Tests for region (us-east-1) ran successfully: - aws_wafv2_ip_set - aws_wafv2_regex_pattern_set - aws_wafv2_web_acl - aws_wafv2_rule_group 2022/09/27 16:24:22 [DEBUG] Running Sweepers for region (us-east-2): 2022/09/27 16:24:22 [DEBUG] Running Sweeper (aws_wafv2_web_acl) in region (us-east-2) 2022/09/27 16:24:22 [INFO] Retrieved credentials from "EnvConfigCredentials" 2022/09/27 16:24:22 [DEBUG] Trying to get account information via sts:GetCallerIdentity 2022/09/27 16:24:23 [DEBUG] Trying to get account information via sts:GetCallerIdentity 2022/09/27 16:24:23 [DEBUG] Completed Sweeper (aws_wafv2_web_acl) in region (us-east-2) in 786.921855ms 2022/09/27 16:24:23 [DEBUG] Sweeper (aws_wafv2_regex_pattern_set) has dependency (aws_wafv2_rule_group), running.. 2022/09/27 16:24:23 [DEBUG] Sweeper (aws_wafv2_rule_group) has dependency (aws_wafv2_web_acl), running.. 2022/09/27 16:24:23 [DEBUG] Sweeper (aws_wafv2_web_acl) already ran in region (us-east-2) 2022/09/27 16:24:23 [DEBUG] Running Sweeper (aws_wafv2_rule_group) in region (us-east-2) 2022/09/27 16:24:23 [DEBUG] Completed Sweeper (aws_wafv2_rule_group) in region (us-east-2) in 49.200091ms 2022/09/27 16:24:23 [DEBUG] Sweeper (aws_wafv2_regex_pattern_set) has dependency (aws_wafv2_web_acl), running.. 2022/09/27 16:24:23 [DEBUG] Sweeper (aws_wafv2_web_acl) already ran in region (us-east-2) 2022/09/27 16:24:23 [DEBUG] Running Sweeper (aws_wafv2_regex_pattern_set) in region (us-east-2) 2022/09/27 16:24:23 [DEBUG] Completed Sweeper (aws_wafv2_regex_pattern_set) in region (us-east-2) in 41.34805ms 2022/09/27 16:24:23 [DEBUG] Sweeper (aws_wafv2_ip_set) has dependency (aws_wafv2_rule_group), running.. 2022/09/27 16:24:23 [DEBUG] Sweeper (aws_wafv2_rule_group) has dependency (aws_wafv2_web_acl), running.. 2022/09/27 16:24:23 [DEBUG] Sweeper (aws_wafv2_web_acl) already ran in region (us-east-2) 2022/09/27 16:24:23 [DEBUG] Sweeper (aws_wafv2_rule_group) already ran in region (us-east-2) 2022/09/27 16:24:23 [DEBUG] Sweeper (aws_wafv2_ip_set) has dependency (aws_wafv2_web_acl), running.. 2022/09/27 16:24:23 [DEBUG] Sweeper (aws_wafv2_web_acl) already ran in region (us-east-2) 2022/09/27 16:24:23 [DEBUG] Running Sweeper (aws_wafv2_ip_set) in region (us-east-2) 2022/09/27 16:24:23 [DEBUG] Completed Sweeper (aws_wafv2_ip_set) in region (us-east-2) in 40.091123ms 2022/09/27 16:24:23 [DEBUG] Sweeper (aws_wafv2_rule_group) has dependency (aws_wafv2_web_acl), running.. 2022/09/27 16:24:23 [DEBUG] Sweeper (aws_wafv2_web_acl) already ran in region (us-east-2) 2022/09/27 16:24:23 [DEBUG] Sweeper (aws_wafv2_rule_group) already ran in region (us-east-2) 2022/09/27 16:24:23 Completed Sweepers for region (us-east-2) in 917.662351ms 2022/09/27 16:24:23 Sweeper Tests for region (us-east-2) ran successfully: - aws_wafv2_rule_group - aws_wafv2_regex_pattern_set - aws_wafv2_ip_set - aws_wafv2_web_acl ok github.com/hashicorp/terraform-provider-aws/internal/sweep 9.393s --- internal/service/wafv2/sweep.go | 147 ++++++++++++++------------------ 1 file changed, 63 insertions(+), 84 deletions(-) diff --git a/internal/service/wafv2/sweep.go b/internal/service/wafv2/sweep.go index 28b0e8b7cd1..dad2c73eb1a 100644 --- a/internal/service/wafv2/sweep.go +++ b/internal/service/wafv2/sweep.go @@ -10,7 +10,6 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/wafv2" - "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/sweep" @@ -55,50 +54,46 @@ func sweepIPSets(region string) error { return fmt.Errorf("error getting client: %s", err) } conn := client.(*conns.AWSClient).WAFV2Conn - - var sweeperErrs *multierror.Error - input := &wafv2.ListIPSetsInput{ Scope: aws.String(wafv2.ScopeRegional), } + sweepResources := make([]sweep.Sweepable, 0) err = listIPSetsPages(conn, input, func(page *wafv2.ListIPSetsOutput, lastPage bool) bool { if page == nil { return !lastPage } - for _, ipSet := range page.IPSets { - id := aws.StringValue(ipSet.Id) - + for _, v := range page.IPSets { r := ResourceIPSet() d := r.Data(nil) - d.SetId(id) - d.Set("lock_token", ipSet.LockToken) - d.Set("name", ipSet.Name) + d.SetId(aws.StringValue(v.Id)) + d.Set("lock_token", v.LockToken) + d.Set("name", v.Name) d.Set("scope", input.Scope) - err := r.Delete(d, client) - if err != nil { - sweeperErr := fmt.Errorf("error deleting WAFv2 IP Set (%s): %w", id, err) - log.Printf("[ERROR] %s", sweeperErr) - sweeperErrs = multierror.Append(sweeperErrs, sweeperErr) - continue - } + sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } return !lastPage }) if sweep.SkipSweepError(err) { - log.Printf("[WARN] Skipping WAFv2 IP Set sweep for %s: %s", region, err) - return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors + log.Printf("[WARN] Skipping WAFv2 IPSet sweep for %s: %s", region, err) + return nil + } + + if err != nil { + return fmt.Errorf("error listing WAFv2 IPSets (%s): %w", region, err) } + err = sweep.SweepOrchestrator(sweepResources) + if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error describing WAFv2 IP Sets: %w", err)) + return fmt.Errorf("error sweeping WAFv2 IPSets (%s): %w", region, err) } - return sweeperErrs.ErrorOrNil() + return nil } func sweepRegexPatternSets(region string) error { @@ -107,50 +102,46 @@ func sweepRegexPatternSets(region string) error { return fmt.Errorf("error getting client: %s", err) } conn := client.(*conns.AWSClient).WAFV2Conn - - var sweeperErrs *multierror.Error - input := &wafv2.ListRegexPatternSetsInput{ Scope: aws.String(wafv2.ScopeRegional), } + sweepResources := make([]sweep.Sweepable, 0) err = listRegexPatternSetsPages(conn, input, func(page *wafv2.ListRegexPatternSetsOutput, lastPage bool) bool { if page == nil { return !lastPage } - for _, regexPatternSet := range page.RegexPatternSets { - id := aws.StringValue(regexPatternSet.Id) - + for _, v := range page.RegexPatternSets { r := ResourceRegexPatternSet() d := r.Data(nil) - d.SetId(id) - d.Set("lock_token", regexPatternSet.LockToken) - d.Set("name", regexPatternSet.Name) + d.SetId(aws.StringValue(v.Id)) + d.Set("lock_token", v.LockToken) + d.Set("name", v.Name) d.Set("scope", input.Scope) - err := r.Delete(d, client) - if err != nil { - sweeperErr := fmt.Errorf("error deleting WAFv2 Regex Pattern Set (%s): %w", id, err) - log.Printf("[ERROR] %s", sweeperErr) - sweeperErrs = multierror.Append(sweeperErrs, sweeperErr) - continue - } + sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } return !lastPage }) if sweep.SkipSweepError(err) { - log.Printf("[WARN] Skipping WAFv2 Regex Pattern Set sweep for %s: %s", region, err) - return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors + log.Printf("[WARN] Skipping WAFv2 RegexPatternSet sweep for %s: %s", region, err) + return nil } if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error describing WAFv2 Regex Pattern Sets: %w", err)) + return fmt.Errorf("error listing WAFv2 RegexPatternSets (%s): %w", region, err) } - return sweeperErrs.ErrorOrNil() + err = sweep.SweepOrchestrator(sweepResources) + + if err != nil { + return fmt.Errorf("error sweeping WAFv2 RegexPatternSets (%s): %w", region, err) + } + + return nil } func sweepRuleGroups(region string) error { @@ -159,78 +150,66 @@ func sweepRuleGroups(region string) error { return fmt.Errorf("error getting client: %s", err) } conn := client.(*conns.AWSClient).WAFV2Conn - - var sweeperErrs *multierror.Error - input := &wafv2.ListRuleGroupsInput{ Scope: aws.String(wafv2.ScopeRegional), } + sweepResources := make([]sweep.Sweepable, 0) err = listRuleGroupsPages(conn, input, func(page *wafv2.ListRuleGroupsOutput, lastPage bool) bool { if page == nil { return !lastPage } - for _, ruleGroup := range page.RuleGroups { - id := aws.StringValue(ruleGroup.Id) - + for _, v := range page.RuleGroups { r := ResourceRuleGroup() d := r.Data(nil) - d.SetId(id) - d.Set("lock_token", ruleGroup.LockToken) - d.Set("name", ruleGroup.Name) + d.SetId(aws.StringValue(v.Id)) + d.Set("lock_token", v.LockToken) + d.Set("name", v.Name) d.Set("scope", input.Scope) - err := r.Delete(d, client) - if err != nil { - sweeperErr := fmt.Errorf("error deleting WAFv2 Rule Group (%s): %w", id, err) - log.Printf("[ERROR] %s", sweeperErr) - sweeperErrs = multierror.Append(sweeperErrs, sweeperErr) - continue - } + sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } return !lastPage }) if sweep.SkipSweepError(err) { - log.Printf("[WARN] Skipping WAFv2 Rule Group sweep for %s: %s", region, err) - return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors + log.Printf("[WARN] Skipping WAFv2 RuleGroup sweep for %s: %s", region, err) + return nil } if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error describing WAFv2 Rule Groups: %w", err)) + return fmt.Errorf("error listing WAFv2 RuleGroups (%s): %w", region, err) } - return sweeperErrs.ErrorOrNil() + err = sweep.SweepOrchestrator(sweepResources) + + if err != nil { + return fmt.Errorf("error sweeping WAFv2 RuleGroups (%s): %w", region, err) + } + + return nil } func sweepWebACLs(region string) error { client, err := sweep.SharedRegionalSweepClient(region) - if err != nil { return fmt.Errorf("error getting client: %s", err) } - conn := client.(*conns.AWSClient).WAFV2Conn - sweepResources := make([]sweep.Sweepable, 0) - var errs *multierror.Error - input := &wafv2.ListWebACLsInput{ Scope: aws.String(wafv2.ScopeRegional), } + sweepResources := make([]sweep.Sweepable, 0) err = listWebACLsPages(conn, input, func(page *wafv2.ListWebACLsOutput, lastPage bool) bool { if page == nil { return !lastPage } - for _, webAcl := range page.WebACLs { - if webAcl == nil { - continue - } - - name := aws.StringValue(webAcl.Name) + for _, v := range page.WebACLs { + name := aws.StringValue(v.Name) // Exclude WebACLs managed by Firewall Manager as deletion returns AccessDeniedException. // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/19149 @@ -240,12 +219,10 @@ func sweepWebACLs(region string) error { continue } - id := aws.StringValue(webAcl.Id) - r := ResourceWebACL() d := r.Data(nil) - d.SetId(id) - d.Set("lock_token", webAcl.LockToken) + d.SetId(aws.StringValue(v.Id)) + d.Set("lock_token", v.LockToken) d.Set("name", name) d.Set("scope", input.Scope) @@ -255,18 +232,20 @@ func sweepWebACLs(region string) error { return !lastPage }) - if err != nil { - errs = multierror.Append(errs, fmt.Errorf("error describing WAFv2 Web ACLs for %s: %w", region, err)) + if sweep.SkipSweepError(err) { + log.Printf("[WARN] Skipping WAFv2 WebACL sweep for %s: %s", region, err) + return nil } - if err := sweep.SweepOrchestrator(sweepResources); err != nil { - errs = multierror.Append(errs, fmt.Errorf("error sweeping WAFv2 Web ACLs for %s: %w", region, err)) + if err != nil { + return fmt.Errorf("error listing WAFv2 WebACLs (%s): %w", region, err) } - if sweep.SkipSweepError(errs.ErrorOrNil()) { - log.Printf("[WARN] Skipping WAFv2 Web ACLs sweep for %s: %s", region, errs) - return nil + err = sweep.SweepOrchestrator(sweepResources) + + if err != nil { + return fmt.Errorf("error sweeping WAFv2 WebACLs (%s): %w", region, err) } - return errs.ErrorOrNil() + return nil } From 57d7277f912b0daaa4168a7d87da3ce781f0900e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 28 Sep 2022 08:57:33 -0400 Subject: [PATCH 37/52] oversize_handling may be Optional. In that case return an empty Body. --- internal/service/wafv2/flex.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/internal/service/wafv2/flex.go b/internal/service/wafv2/flex.go index 63a72afeb61..7b5267244fc 100644 --- a/internal/service/wafv2/flex.go +++ b/internal/service/wafv2/flex.go @@ -728,14 +728,15 @@ func expandXSSMatchStatement(l []interface{}) *wafv2.XssMatchStatement { } func expandBody(l []interface{}) *wafv2.Body { + apiObject := &wafv2.Body{} + + // oversize_handling may be Optional. In that case return an empty Body. if len(l) == 0 || l[0] == nil { - return nil + return apiObject } m := l[0].(map[string]interface{}) - apiObject := &wafv2.Body{} - if v, ok := m["oversize_handling"].(string); ok && v != "" { apiObject.OversizeHandling = aws.String(v) } From ad1720d150e6634e047dbe7f98648bee4f416cfa Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 28 Sep 2022 08:57:41 -0400 Subject: [PATCH 38/52] Revert "oversize_handling may be Optional. In that case return an empty Body." This reverts commit 57d7277f912b0daaa4168a7d87da3ce781f0900e. --- internal/service/wafv2/flex.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/internal/service/wafv2/flex.go b/internal/service/wafv2/flex.go index 7b5267244fc..63a72afeb61 100644 --- a/internal/service/wafv2/flex.go +++ b/internal/service/wafv2/flex.go @@ -728,15 +728,14 @@ func expandXSSMatchStatement(l []interface{}) *wafv2.XssMatchStatement { } func expandBody(l []interface{}) *wafv2.Body { - apiObject := &wafv2.Body{} - - // oversize_handling may be Optional. In that case return an empty Body. if len(l) == 0 || l[0] == nil { - return apiObject + return nil } m := l[0].(map[string]interface{}) + apiObject := &wafv2.Body{} + if v, ok := m["oversize_handling"].(string); ok && v != "" { apiObject.OversizeHandling = aws.String(v) } From 8372d2048f6335f78b921a3c71281f1283a2ec50 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 28 Sep 2022 09:20:17 -0400 Subject: [PATCH 39/52] r/aws_wafv2_ip_set: Switch to 'WithoutTimeout' CRUD handlers (#15090). Acceptance test output: % make testacc TESTARGS='-run=TestAccWAFV2IPSet_' PKG=wafv2 ACCTEST_PARALLELISM=2 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/wafv2/... -v -count 1 -parallel 2 -run=TestAccWAFV2IPSet_ -timeout 180m === RUN TestAccWAFV2IPSet_basic === PAUSE TestAccWAFV2IPSet_basic === RUN TestAccWAFV2IPSet_disappears === PAUSE TestAccWAFV2IPSet_disappears === RUN TestAccWAFV2IPSet_ipv6 === PAUSE TestAccWAFV2IPSet_ipv6 === RUN TestAccWAFV2IPSet_minimal === PAUSE TestAccWAFV2IPSet_minimal === RUN TestAccWAFV2IPSet_changeNameForceNew === PAUSE TestAccWAFV2IPSet_changeNameForceNew === RUN TestAccWAFV2IPSet_tags === PAUSE TestAccWAFV2IPSet_tags === RUN TestAccWAFV2IPSet_large === PAUSE TestAccWAFV2IPSet_large === CONT TestAccWAFV2IPSet_basic === CONT TestAccWAFV2IPSet_changeNameForceNew --- PASS: TestAccWAFV2IPSet_changeNameForceNew (31.66s) === CONT TestAccWAFV2IPSet_large --- PASS: TestAccWAFV2IPSet_basic (37.46s) === CONT TestAccWAFV2IPSet_tags --- PASS: TestAccWAFV2IPSet_large (19.25s) === CONT TestAccWAFV2IPSet_ipv6 --- PASS: TestAccWAFV2IPSet_ipv6 (18.65s) === CONT TestAccWAFV2IPSet_minimal --- PASS: TestAccWAFV2IPSet_tags (43.24s) === CONT TestAccWAFV2IPSet_disappears --- PASS: TestAccWAFV2IPSet_minimal (19.33s) --- PASS: TestAccWAFV2IPSet_disappears (15.55s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/wafv2 100.908s --- internal/service/wafv2/ip_set.go | 193 ++++++++++++++------------ internal/service/wafv2/ip_set_test.go | 89 +++++------- 2 files changed, 140 insertions(+), 142 deletions(-) diff --git a/internal/service/wafv2/ip_set.go b/internal/service/wafv2/ip_set.go index aa39db7b2cb..4d0c14e6cbf 100644 --- a/internal/service/wafv2/ip_set.go +++ b/internal/service/wafv2/ip_set.go @@ -1,6 +1,7 @@ package wafv2 import ( + "context" "fmt" "log" "strings" @@ -9,6 +10,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/wafv2" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -19,12 +21,17 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/verify" ) +const ( + ipSetDeleteTimeout = 5 * time.Minute +) + func ResourceIPSet() *schema.Resource { return &schema.Resource{ - Create: resourceIPSetCreate, - Read: resourceIPSetRead, - Update: resourceIPSetUpdate, - Delete: resourceIPSetDelete, + CreateWithoutTimeout: resourceIPSetCreate, + ReadWithoutTimeout: resourceIPSetRead, + UpdateWithoutTimeout: resourceIPSetUpdate, + DeleteWithoutTimeout: resourceIPSetDelete, + Importer: &schema.ResourceImporter{ State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { idParts := strings.Split(d.Id(), "/") @@ -108,166 +115,178 @@ func ResourceIPSet() *schema.Resource { } } -func resourceIPSetCreate(d *schema.ResourceData, meta interface{}) error { +func resourceIPSetCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).WAFV2Conn defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{}))) - params := &wafv2.CreateIPSetInput{ + + name := d.Get("name").(string) + input := &wafv2.CreateIPSetInput{ Addresses: aws.StringSlice([]string{}), IPAddressVersion: aws.String(d.Get("ip_address_version").(string)), - Name: aws.String(d.Get("name").(string)), + Name: aws.String(name), Scope: aws.String(d.Get("scope").(string)), } if v, ok := d.GetOk("addresses"); ok && v.(*schema.Set).Len() > 0 { - params.Addresses = flex.ExpandStringSet(v.(*schema.Set)) + input.Addresses = flex.ExpandStringSet(v.(*schema.Set)) } if v, ok := d.GetOk("description"); ok { - params.Description = aws.String(v.(string)) + input.Description = aws.String(v.(string)) } if len(tags) > 0 { - params.Tags = Tags(tags.IgnoreAWS()) + input.Tags = Tags(tags.IgnoreAWS()) } - resp, err := conn.CreateIPSet(params) + log.Printf("[INFO] Creating WAFv2 IPSet: %s", input) + output, err := conn.CreateIPSetWithContext(ctx, input) if err != nil { - return fmt.Errorf("Error creating WAFv2 IPSet: %s", err) + return diag.Errorf("creating WAFv2 IPSet (%s): %s", name, err) } - if resp == nil || resp.Summary == nil { - return fmt.Errorf("Error creating WAFv2 IPSet") - } - - d.SetId(aws.StringValue(resp.Summary.Id)) + d.SetId(aws.StringValue(output.Summary.Id)) - return resourceIPSetRead(d, meta) + return resourceIPSetRead(ctx, d, meta) } -func resourceIPSetRead(d *schema.ResourceData, meta interface{}) error { +func resourceIPSetRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).WAFV2Conn defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - params := &wafv2.GetIPSetInput{ - Id: aws.String(d.Id()), - Name: aws.String(d.Get("name").(string)), - Scope: aws.String(d.Get("scope").(string)), - } + output, err := FindIPSetByThreePartKey(ctx, conn, d.Id(), d.Get("name").(string), d.Get("scope").(string)) - resp, err := conn.GetIPSet(params) - if err != nil { - if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFNonexistentItemException) { - log.Printf("[WARN] WAFv2 IPSet (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } - return err + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] WAFv2 IPSet (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil } - if resp == nil || resp.IPSet == nil { - return fmt.Errorf("Error reading WAFv2 IPSet") + if err != nil { + return diag.Errorf("reading WAFv2 IPSet (%s): %s", d.Id(), err) } - d.Set("name", resp.IPSet.Name) - d.Set("description", resp.IPSet.Description) - d.Set("ip_address_version", resp.IPSet.IPAddressVersion) - d.Set("arn", resp.IPSet.ARN) - d.Set("lock_token", resp.LockToken) + ipSet := output.IPSet + d.Set("addresses", aws.StringValueSlice(ipSet.Addresses)) + arn := aws.StringValue(ipSet.ARN) + d.Set("arn", arn) + d.Set("description", ipSet.Description) + d.Set("ip_address_version", ipSet.IPAddressVersion) + d.Set("lock_token", output.LockToken) + d.Set("name", ipSet.Name) - if err := d.Set("addresses", flex.FlattenStringSet(resp.IPSet.Addresses)); err != nil { - return fmt.Errorf("Error setting addresses: %s", err) - } + tags, err := ListTagsWithContext(ctx, conn, arn) - arn := aws.StringValue(resp.IPSet.ARN) - tags, err := ListTags(conn, arn) if err != nil { - return fmt.Errorf("Error listing tags for WAFv2 IpSet (%s): %s", arn, err) + return diag.Errorf("listing tags for WAFv2 IPSet (%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) + return diag.Errorf("setting tags: %s", err) } if err := d.Set("tags_all", tags.Map()); err != nil { - return fmt.Errorf("error setting tags_all: %w", err) + return diag.Errorf("setting tags_all: %s", err) } return nil } -func resourceIPSetUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceIPSetUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).WAFV2Conn - log.Printf("[INFO] Updating WAFv2 IPSet %s", d.Id()) - - params := &wafv2.UpdateIPSetInput{ - Id: aws.String(d.Id()), - Name: aws.String(d.Get("name").(string)), - Scope: aws.String(d.Get("scope").(string)), - Addresses: aws.StringSlice([]string{}), - LockToken: aws.String(d.Get("lock_token").(string)), - } + if d.HasChangesExcept("tags", "tags_all") { + input := &wafv2.UpdateIPSetInput{ + Addresses: aws.StringSlice([]string{}), + Id: aws.String(d.Id()), + LockToken: aws.String(d.Get("lock_token").(string)), + Name: aws.String(d.Get("name").(string)), + Scope: aws.String(d.Get("scope").(string)), + } - if v, ok := d.GetOk("addresses"); ok && v.(*schema.Set).Len() > 0 { - params.Addresses = flex.ExpandStringSet(v.(*schema.Set)) - } + if v, ok := d.GetOk("addresses"); ok && v.(*schema.Set).Len() > 0 { + input.Addresses = flex.ExpandStringSet(v.(*schema.Set)) + } - if v, ok := d.GetOk("description"); ok { - params.Description = aws.String(v.(string)) - } + if v, ok := d.GetOk("description"); ok { + input.Description = aws.String(v.(string)) + } - _, err := conn.UpdateIPSet(params) + log.Printf("[INFO] Updating WAFv2 IPSet: %s", input) + _, err := conn.UpdateIPSetWithContext(ctx, input) - if err != nil { - return fmt.Errorf("Error updating WAFv2 IPSet: %s", err) + if err != nil { + return diag.Errorf("updating WAFv2 IPSet (%s): %s", d.Id(), err) + } } if d.HasChange("tags_all") { o, n := d.GetChange("tags_all") - if err := UpdateTags(conn, d.Get("arn").(string), o, n); err != nil { - return fmt.Errorf("Error updating tags: %s", err) + arn := d.Get("arn").(string) + + if err := UpdateTagsWithContext(ctx, conn, arn, o, n); err != nil { + return diag.Errorf("updating tags for WAFv2 IPSet (%s): %s", arn, err) } } - return resourceIPSetRead(d, meta) + return resourceIPSetRead(ctx, d, meta) } -func resourceIPSetDelete(d *schema.ResourceData, meta interface{}) error { +func resourceIPSetDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).WAFV2Conn - log.Printf("[INFO] Deleting WAFv2 IPSet %s", d.Id()) - - params := &wafv2.DeleteIPSetInput{ + input := &wafv2.DeleteIPSetInput{ Id: aws.String(d.Id()), + LockToken: aws.String(d.Get("lock_token").(string)), Name: aws.String(d.Get("name").(string)), Scope: aws.String(d.Get("scope").(string)), - LockToken: aws.String(d.Get("lock_token").(string)), } - err := resource.Retry(5*time.Minute, func() *resource.RetryError { - _, err := conn.DeleteIPSet(params) - if err != nil { - if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFAssociatedItemException) { - return resource.RetryableError(err) - } - return resource.NonRetryableError(err) - } - return nil - }) + log.Printf("[INFO] Deleting WAFv2 IPSet: %s", d.Id()) + _, err := tfresource.RetryWhenAWSErrCodeEqualsContext(ctx, ipSetDeleteTimeout, func() (interface{}, error) { + return conn.DeleteIPSetWithContext(ctx, input) + }, wafv2.ErrCodeWAFAssociatedItemException) - if tfresource.TimedOut(err) { - _, err = conn.DeleteIPSet(params) + if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFNonexistentItemException) { + return nil } if err != nil { - return fmt.Errorf("Error deleting WAFv2 IPSet: %s", err) + return diag.Errorf("deleting WAFv2 IPSet (%s): %s", d.Id(), err) } return nil } + +func FindIPSetByThreePartKey(ctx context.Context, conn *wafv2.WAFV2, id, name, scope string) (*wafv2.GetIPSetOutput, error) { + input := &wafv2.GetIPSetInput{ + Id: aws.String(id), + Name: aws.String(name), + Scope: aws.String(scope), + } + + output, err := conn.GetIPSetWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFNonexistentItemException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.IPSet == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, nil +} diff --git a/internal/service/wafv2/ip_set_test.go b/internal/service/wafv2/ip_set_test.go index 13a57b286aa..6056fd6c3aa 100644 --- a/internal/service/wafv2/ip_set_test.go +++ b/internal/service/wafv2/ip_set_test.go @@ -1,19 +1,19 @@ package wafv2_test import ( + "context" "fmt" "regexp" "testing" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/wafv2" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfwafv2 "github.com/hashicorp/terraform-provider-aws/internal/service/wafv2" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccWAFV2IPSet_basic(t *testing.T) { @@ -285,29 +285,18 @@ func testAccCheckIPSetDestroy(s *terraform.State) error { } conn := acctest.Provider.Meta().(*conns.AWSClient).WAFV2Conn - resp, err := conn.GetIPSet( - &wafv2.GetIPSetInput{ - Id: aws.String(rs.Primary.ID), - Name: aws.String(rs.Primary.Attributes["name"]), - Scope: aws.String(rs.Primary.Attributes["scope"]), - }) - - if err == nil { - if resp == nil || resp.IPSet == nil { - return fmt.Errorf("Error getting WAFv2 IPSet") - } - if aws.StringValue(resp.IPSet.Id) == rs.Primary.ID { - return fmt.Errorf("WAFv2 IPSet %s still exists", rs.Primary.ID) - } - return nil + + _, err := tfwafv2.FindIPSetByThreePartKey(context.Background(), conn, rs.Primary.ID, rs.Primary.Attributes["name"], rs.Primary.Attributes["scope"]) + + if tfresource.NotFound(err) { + continue } - // Return nil if the IPSet is already destroyed - if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFNonexistentItemException) { - return nil + if err != nil { + return err } - return err + return fmt.Errorf("WAFv2 IPSet %s still exists", rs.Primary.ID) } return nil @@ -325,34 +314,24 @@ func testAccCheckIPSetExists(n string, v *wafv2.IPSet) resource.TestCheckFunc { } conn := acctest.Provider.Meta().(*conns.AWSClient).WAFV2Conn - resp, err := conn.GetIPSet(&wafv2.GetIPSetInput{ - Id: aws.String(rs.Primary.ID), - Name: aws.String(rs.Primary.Attributes["name"]), - Scope: aws.String(rs.Primary.Attributes["scope"]), - }) + + output, err := tfwafv2.FindIPSetByThreePartKey(context.Background(), conn, rs.Primary.ID, rs.Primary.Attributes["name"], rs.Primary.Attributes["scope"]) if err != nil { return err } - if resp == nil || resp.IPSet == nil { - return fmt.Errorf("Error getting WAFv2 IPSet") - } - - if aws.StringValue(resp.IPSet.Id) == rs.Primary.ID { - *v = *resp.IPSet - return nil - } + *v = *output.IPSet - return fmt.Errorf("WAFv2 IPSet (%s) not found", rs.Primary.ID) + return nil } } func testAccIPSetConfig_basic(name string) string { return fmt.Sprintf(` resource "aws_wafv2_ip_set" "ip_set" { - name = "%s" - description = "%s" + name = %[1]q + description = %[1]q scope = "REGIONAL" ip_address_version = "IPV4" addresses = ["1.2.3.4/32", "5.6.7.8/32"] @@ -362,13 +341,13 @@ resource "aws_wafv2_ip_set" "ip_set" { Tag2 = "Value2" } } -`, name, name) +`, name) } func testAccIPSetConfig_update(name string) string { return fmt.Sprintf(` resource "aws_wafv2_ip_set" "ip_set" { - name = "%s" + name = %[1]q description = "Updated" scope = "REGIONAL" ip_address_version = "IPV4" @@ -380,8 +359,8 @@ resource "aws_wafv2_ip_set" "ip_set" { func testAccIPSetConfig_v6(name string) string { return fmt.Sprintf(` resource "aws_wafv2_ip_set" "ip_set" { - name = "%s" - description = "%s" + name = %[1]q + description = %[1]q scope = "REGIONAL" ip_address_version = "IPV6" addresses = [ @@ -390,13 +369,13 @@ resource "aws_wafv2_ip_set" "ip_set" { "2001:db8::/32" ] } -`, name, name) +`, name) } func testAccIPSetConfig_minimal(name string) string { return fmt.Sprintf(` resource "aws_wafv2_ip_set" "ip_set" { - name = "%s" + name = %[1]q scope = "REGIONAL" ip_address_version = "IPV4" } @@ -406,41 +385,41 @@ resource "aws_wafv2_ip_set" "ip_set" { func testAccIPSetConfig_oneTag(name, tagKey, tagValue string) string { return fmt.Sprintf(` resource "aws_wafv2_ip_set" "ip_set" { - name = "%s" - description = "%s" + name = %[1]q + description = %[1]q scope = "REGIONAL" ip_address_version = "IPV4" addresses = ["1.2.3.4/32", "5.6.7.8/32"] tags = { - "%s" = "%s" + %[2]q = %[3]q } } -`, name, name, tagKey, tagValue) +`, name, tagKey, tagValue) } func testAccIPSetConfig_twoTags(name, tag1Key, tag1Value, tag2Key, tag2Value string) string { return fmt.Sprintf(` resource "aws_wafv2_ip_set" "ip_set" { - name = "%s" - description = "%s" + name = %[1]q + description = %[1]q scope = "REGIONAL" ip_address_version = "IPV4" addresses = ["1.2.3.4/32", "5.6.7.8/32"] tags = { - "%s" = "%s" - "%s" = "%s" + %[2]q = %[3]q + %[4]q = %[5]q } } -`, name, name, tag1Key, tag1Value, tag2Key, tag2Value) +`, name, tag1Key, tag1Value, tag2Key, tag2Value) } func testAccIPSetConfig_large(name string) string { return fmt.Sprintf(` resource "aws_wafv2_ip_set" "ip_set" { - name = "%s" - description = "%s" + name = %[1]q + description = %[1]q scope = "REGIONAL" ip_address_version = "IPV4" addresses = [ @@ -456,7 +435,7 @@ resource "aws_wafv2_ip_set" "ip_set" { "1.1.1.20/32", "2.2.2.25/32", "1.1.1.45/32", "1.1.1.2/32", "2.2.2.98/32" ] } -`, name, name) +`, name) } func testAccIPSetImportStateIdFunc(resourceName string) resource.ImportStateIdFunc { From ef76ded0b17ca082cbd7279a36b1f871032fbda6 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 28 Sep 2022 10:35:42 -0400 Subject: [PATCH 40/52] r/aws_wafv2_regex_pattern_set: Switch to 'WithoutTimeout' CRUD handlers (#15090). Acceptance test output: % make testacc TESTARGS='-run=TestAccWAFV2RegexPatternSet_' PKG=wafv2 ACCTEST_PARALLELISM=2 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/wafv2/... -v -count 1 -parallel 2 -run=TestAccWAFV2RegexPatternSet_ -timeout 180m === RUN TestAccWAFV2RegexPatternSet_basic === PAUSE TestAccWAFV2RegexPatternSet_basic === RUN TestAccWAFV2RegexPatternSet_disappears === PAUSE TestAccWAFV2RegexPatternSet_disappears === RUN TestAccWAFV2RegexPatternSet_minimal === PAUSE TestAccWAFV2RegexPatternSet_minimal === RUN TestAccWAFV2RegexPatternSet_changeNameForceNew === PAUSE TestAccWAFV2RegexPatternSet_changeNameForceNew === RUN TestAccWAFV2RegexPatternSet_tags === PAUSE TestAccWAFV2RegexPatternSet_tags === CONT TestAccWAFV2RegexPatternSet_basic === CONT TestAccWAFV2RegexPatternSet_changeNameForceNew --- PASS: TestAccWAFV2RegexPatternSet_changeNameForceNew (31.91s) === CONT TestAccWAFV2RegexPatternSet_minimal --- PASS: TestAccWAFV2RegexPatternSet_basic (34.79s) === CONT TestAccWAFV2RegexPatternSet_tags --- PASS: TestAccWAFV2RegexPatternSet_minimal (17.47s) === CONT TestAccWAFV2RegexPatternSet_disappears --- PASS: TestAccWAFV2RegexPatternSet_disappears (14.38s) --- PASS: TestAccWAFV2RegexPatternSet_tags (47.03s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/wafv2 86.036s --- internal/service/wafv2/flex.go | 46 ++++ internal/service/wafv2/regex_pattern_set.go | 217 ++++++++---------- .../service/wafv2/regex_pattern_set_test.go | 74 +++--- 3 files changed, 172 insertions(+), 165 deletions(-) diff --git a/internal/service/wafv2/flex.go b/internal/service/wafv2/flex.go index 63a72afeb61..d0bd2e1f65c 100644 --- a/internal/service/wafv2/flex.go +++ b/internal/service/wafv2/flex.go @@ -1018,6 +1018,32 @@ func expandExcludedRule(m map[string]interface{}) *wafv2.ExcludedRule { } } +func expandRegexPatternSet(l []interface{}) []*wafv2.Regex { + if len(l) == 0 || l[0] == nil { + return nil + } + + regexPatterns := make([]*wafv2.Regex, 0) + for _, regexPattern := range l { + if regexPattern == nil { + continue + } + regexPatterns = append(regexPatterns, expandRegex(regexPattern.(map[string]interface{}))) + } + + return regexPatterns +} + +func expandRegex(m map[string]interface{}) *wafv2.Regex { + if m == nil { + return nil + } + + return &wafv2.Regex{ + RegexString: aws.String(m["regex_string"].(string)), + } +} + func flattenRules(r []*wafv2.Rule) interface{} { out := make([]map[string]interface{}, len(r)) for i, rule := range r { @@ -1859,3 +1885,23 @@ func flattenExcludedRules(r []*wafv2.ExcludedRule) interface{} { return out } + +func flattenRegexPatternSet(r []*wafv2.Regex) interface{} { + if r == nil { + return []interface{}{} + } + + regexPatterns := make([]interface{}, 0) + + for _, regexPattern := range r { + if regexPattern == nil { + continue + } + d := map[string]interface{}{ + "regex_string": aws.StringValue(regexPattern.RegexString), + } + regexPatterns = append(regexPatterns, d) + } + + return regexPatterns +} diff --git a/internal/service/wafv2/regex_pattern_set.go b/internal/service/wafv2/regex_pattern_set.go index d88b1989d20..e93e9d1d721 100644 --- a/internal/service/wafv2/regex_pattern_set.go +++ b/internal/service/wafv2/regex_pattern_set.go @@ -1,6 +1,7 @@ package wafv2 import ( + "context" "fmt" "log" "strings" @@ -9,6 +10,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/wafv2" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -18,12 +20,17 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/verify" ) +const ( + regexPatternSetDeleteTimeout = 5 * time.Minute +) + func ResourceRegexPatternSet() *schema.Resource { return &schema.Resource{ - Create: resourceRegexPatternSetCreate, - Read: resourceRegexPatternSetRead, - Update: resourceRegexPatternSetUpdate, - Delete: resourceRegexPatternSetDelete, + CreateWithoutTimeout: resourceRegexPatternSetCreate, + ReadWithoutTimeout: resourceRegexPatternSetRead, + UpdateWithoutTimeout: resourceRegexPatternSetUpdate, + DeleteWithoutTimeout: resourceRegexPatternSetDelete, + Importer: &schema.ResourceImporter{ State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { idParts := strings.Split(d.Id(), "/") @@ -88,206 +95,178 @@ func ResourceRegexPatternSet() *schema.Resource { } } -func resourceRegexPatternSetCreate(d *schema.ResourceData, meta interface{}) error { +func resourceRegexPatternSetCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).WAFV2Conn defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{}))) - params := &wafv2.CreateRegexPatternSetInput{ - Name: aws.String(d.Get("name").(string)), - Scope: aws.String(d.Get("scope").(string)), + + name := d.Get("name").(string) + input := &wafv2.CreateRegexPatternSetInput{ + Name: aws.String(name), RegularExpressionList: []*wafv2.Regex{}, + Scope: aws.String(d.Get("scope").(string)), } if v, ok := d.GetOk("description"); ok { - params.Description = aws.String(v.(string)) + input.Description = aws.String(v.(string)) } if v, ok := d.GetOk("regular_expression"); ok && v.(*schema.Set).Len() > 0 { - params.RegularExpressionList = expandRegexPatternSet(v.(*schema.Set).List()) + input.RegularExpressionList = expandRegexPatternSet(v.(*schema.Set).List()) } if len(tags) > 0 { - params.Tags = Tags(tags.IgnoreAWS()) + input.Tags = Tags(tags.IgnoreAWS()) } - resp, err := conn.CreateRegexPatternSet(params) + log.Printf("[INFO] Creating WAFv2 RegexPatternSet: %s", input) + output, err := conn.CreateRegexPatternSetWithContext(ctx, input) if err != nil { - return fmt.Errorf("Error creating WAFv2 RegexPatternSet: %s", err) + return diag.Errorf("creating WAFv2 RegexPatternSet (%s): %s", name, err) } - if resp == nil || resp.Summary == nil { - return fmt.Errorf("Error creating WAFv2 RegexPatternSet") - } + d.SetId(aws.StringValue(output.Summary.Id)) - d.SetId(aws.StringValue(resp.Summary.Id)) - - return resourceRegexPatternSetRead(d, meta) + return resourceRegexPatternSetRead(ctx, d, meta) } -func resourceRegexPatternSetRead(d *schema.ResourceData, meta interface{}) error { +func resourceRegexPatternSetRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).WAFV2Conn defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - params := &wafv2.GetRegexPatternSetInput{ - Id: aws.String(d.Id()), - Name: aws.String(d.Get("name").(string)), - Scope: aws.String(d.Get("scope").(string)), + + output, err := FindRegexPatternSetByThreePartKey(ctx, conn, d.Id(), d.Get("name").(string), d.Get("scope").(string)) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] WAFv2 RegexPatternSet (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil } - resp, err := conn.GetRegexPatternSet(params) if err != nil { - if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFNonexistentItemException) { - log.Printf("[WARN] WAFv2 RegexPatternSet (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } - return err + return diag.Errorf("reading WAFv2 RegexPatternSet (%s): %s", d.Id(), err) } - if resp == nil || resp.RegexPatternSet == nil { - return fmt.Errorf("Error getting WAFv2 RegexPatternSet") + regexPatternSet := output.RegexPatternSet + arn := aws.StringValue(regexPatternSet.ARN) + d.Set("arn", arn) + d.Set("description", regexPatternSet.Description) + d.Set("lock_token", output.LockToken) + d.Set("name", regexPatternSet.Name) + if err := d.Set("regular_expression", flattenRegexPatternSet(regexPatternSet.RegularExpressionList)); err != nil { + return diag.Errorf("setting regular_expression: %s", err) } - d.Set("name", resp.RegexPatternSet.Name) - d.Set("description", resp.RegexPatternSet.Description) - d.Set("arn", resp.RegexPatternSet.ARN) - d.Set("lock_token", resp.LockToken) + tags, err := ListTagsWithContext(ctx, conn, arn) - if err := d.Set("regular_expression", flattenRegexPatternSet(resp.RegexPatternSet.RegularExpressionList)); err != nil { - return fmt.Errorf("Error setting regular_expression: %s", err) - } - - tags, err := ListTags(conn, aws.StringValue(resp.RegexPatternSet.ARN)) if err != nil { - return fmt.Errorf("Error listing tags for WAFv2 RegexPatternSet (%s): %s", aws.StringValue(resp.RegexPatternSet.ARN), err) + return diag.Errorf("listing tags for WAFv2 RegexPatternSet (%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) + return diag.Errorf("setting tags: %s", err) } if err := d.Set("tags_all", tags.Map()); err != nil { - return fmt.Errorf("error setting tags_all: %w", err) + return diag.Errorf("setting tags_all: %s", err) } return nil } -func resourceRegexPatternSetUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceRegexPatternSetUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).WAFV2Conn - log.Printf("[INFO] Updating WAFv2 RegexPatternSet %s", d.Id()) - - params := &wafv2.UpdateRegexPatternSetInput{ - Id: aws.String(d.Id()), - Name: aws.String(d.Get("name").(string)), - Scope: aws.String(d.Get("scope").(string)), - LockToken: aws.String(d.Get("lock_token").(string)), - RegularExpressionList: []*wafv2.Regex{}, - } + if d.HasChangesExcept("tags", "tags_all") { + input := &wafv2.UpdateRegexPatternSetInput{ + Id: aws.String(d.Id()), + LockToken: aws.String(d.Get("lock_token").(string)), + Name: aws.String(d.Get("name").(string)), + RegularExpressionList: []*wafv2.Regex{}, + Scope: aws.String(d.Get("scope").(string)), + } - if v, ok := d.GetOk("regular_expression"); ok && v.(*schema.Set).Len() > 0 { - params.RegularExpressionList = expandRegexPatternSet(v.(*schema.Set).List()) - } + if v, ok := d.GetOk("description"); ok { + input.Description = aws.String(v.(string)) + } - if v, ok := d.GetOk("description"); ok { - params.Description = aws.String(v.(string)) - } + if v, ok := d.GetOk("regular_expression"); ok && v.(*schema.Set).Len() > 0 { + input.RegularExpressionList = expandRegexPatternSet(v.(*schema.Set).List()) + } - _, err := conn.UpdateRegexPatternSet(params) + log.Printf("[INFO] Updating WAFv2 RegexPatternSet: %s", input) + _, err := conn.UpdateRegexPatternSetWithContext(ctx, input) - if err != nil { - return fmt.Errorf("Error updating WAFv2 RegexPatternSet: %s", err) + if err != nil { + return diag.Errorf("updating WAFv2 RegexPatternSet (%s): %s", d.Id(), err) + } } if d.HasChange("tags_all") { o, n := d.GetChange("tags_all") - if err := UpdateTags(conn, d.Get("arn").(string), o, n); err != nil { - return fmt.Errorf("Error updating tags: %s", err) + arn := d.Get("arn").(string) + + if err := UpdateTagsWithContext(ctx, conn, arn, o, n); err != nil { + return diag.Errorf("updating tags for WAFv2 RegexPatternSet (%s): %s", arn, err) } } - return resourceRegexPatternSetRead(d, meta) + return resourceRegexPatternSetRead(ctx, d, meta) } -func resourceRegexPatternSetDelete(d *schema.ResourceData, meta interface{}) error { +func resourceRegexPatternSetDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).WAFV2Conn - log.Printf("[INFO] Deleting WAFv2 RegexPatternSet %s", d.Id()) - params := &wafv2.DeleteRegexPatternSetInput{ + input := &wafv2.DeleteRegexPatternSetInput{ Id: aws.String(d.Id()), + LockToken: aws.String(d.Get("lock_token").(string)), Name: aws.String(d.Get("name").(string)), Scope: aws.String(d.Get("scope").(string)), - LockToken: aws.String(d.Get("lock_token").(string)), } - err := resource.Retry(5*time.Minute, func() *resource.RetryError { - _, err := conn.DeleteRegexPatternSet(params) - if err != nil { - if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFAssociatedItemException) { - return resource.RetryableError(err) - } - return resource.NonRetryableError(err) - } - return nil - }) - if tfresource.TimedOut(err) { - _, err = conn.DeleteRegexPatternSet(params) + log.Printf("[INFO] Deleting WAFv2 RegexPatternSet: %s", d.Id()) + _, err := tfresource.RetryWhenAWSErrCodeEqualsContext(ctx, regexPatternSetDeleteTimeout, func() (interface{}, error) { + return conn.DeleteRegexPatternSetWithContext(ctx, input) + }, wafv2.ErrCodeWAFAssociatedItemException) + + if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFNonexistentItemException) { + return nil } if err != nil { - return fmt.Errorf("Error deleting WAFv2 RegexPatternSet: %s", err) + return diag.Errorf("deleting WAFv2 RegexPatternSet (%s): %s", d.Id(), err) } return nil } -func expandRegexPatternSet(l []interface{}) []*wafv2.Regex { - if len(l) == 0 || l[0] == nil { - return nil +func FindRegexPatternSetByThreePartKey(ctx context.Context, conn *wafv2.WAFV2, id, name, scope string) (*wafv2.GetRegexPatternSetOutput, error) { + input := &wafv2.GetRegexPatternSetInput{ + Id: aws.String(id), + Name: aws.String(name), + Scope: aws.String(scope), } - regexPatterns := make([]*wafv2.Regex, 0) - for _, regexPattern := range l { - if regexPattern == nil { - continue - } - regexPatterns = append(regexPatterns, expandRegex(regexPattern.(map[string]interface{}))) - } - - return regexPatterns -} - -func expandRegex(m map[string]interface{}) *wafv2.Regex { - if m == nil { - return nil - } + output, err := conn.GetRegexPatternSetWithContext(ctx, input) - return &wafv2.Regex{ - RegexString: aws.String(m["regex_string"].(string)), + if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFNonexistentItemException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } } -} -func flattenRegexPatternSet(r []*wafv2.Regex) interface{} { - if r == nil { - return []interface{}{} + if err != nil { + return nil, err } - regexPatterns := make([]interface{}, 0) - - for _, regexPattern := range r { - if regexPattern == nil { - continue - } - d := map[string]interface{}{ - "regex_string": aws.StringValue(regexPattern.RegexString), - } - regexPatterns = append(regexPatterns, d) + if output == nil || output.RegexPatternSet == nil { + return nil, tfresource.NewEmptyResultError(input) } - return regexPatterns + return output, nil } diff --git a/internal/service/wafv2/regex_pattern_set_test.go b/internal/service/wafv2/regex_pattern_set_test.go index 4eff2f29a1c..a8ce65eeb2f 100644 --- a/internal/service/wafv2/regex_pattern_set_test.go +++ b/internal/service/wafv2/regex_pattern_set_test.go @@ -1,19 +1,19 @@ package wafv2_test import ( + "context" "fmt" "regexp" "testing" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/wafv2" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfwafv2 "github.com/hashicorp/terraform-provider-aws/internal/service/wafv2" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccWAFV2RegexPatternSet_basic(t *testing.T) { @@ -219,26 +219,18 @@ func testAccCheckRegexPatternSetDestroy(s *terraform.State) error { } conn := acctest.Provider.Meta().(*conns.AWSClient).WAFV2Conn - resp, err := conn.GetRegexPatternSet( - &wafv2.GetRegexPatternSetInput{ - Id: aws.String(rs.Primary.ID), - Name: aws.String(rs.Primary.Attributes["name"]), - Scope: aws.String(rs.Primary.Attributes["scope"]), - }) - - if err == nil { - if resp != nil && resp.RegexPatternSet != nil && aws.StringValue(resp.RegexPatternSet.Id) == rs.Primary.ID { - return fmt.Errorf("WAFv2 RegexPatternSet %s still exists", rs.Primary.ID) - } - return nil + + _, err := tfwafv2.FindRegexPatternSetByThreePartKey(context.Background(), conn, rs.Primary.ID, rs.Primary.Attributes["name"], rs.Primary.Attributes["scope"]) + + if tfresource.NotFound(err) { + continue } - // Return nil if the RegexPatternSet is already destroyed - if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFNonexistentItemException) { - return nil + if err != nil { + return err } - return err + return fmt.Errorf("WAFv2 RegexPatternSet %s still exists", rs.Primary.ID) } return nil @@ -256,34 +248,24 @@ func testAccCheckRegexPatternSetExists(n string, v *wafv2.RegexPatternSet) resou } conn := acctest.Provider.Meta().(*conns.AWSClient).WAFV2Conn - resp, err := conn.GetRegexPatternSet(&wafv2.GetRegexPatternSetInput{ - Id: aws.String(rs.Primary.ID), - Name: aws.String(rs.Primary.Attributes["name"]), - Scope: aws.String(rs.Primary.Attributes["scope"]), - }) + + output, err := tfwafv2.FindRegexPatternSetByThreePartKey(context.Background(), conn, rs.Primary.ID, rs.Primary.Attributes["name"], rs.Primary.Attributes["scope"]) if err != nil { return err } - if resp == nil || resp.RegexPatternSet == nil { - return fmt.Errorf("Error getting WAFv2 RegexPatternSet for %s", rs.Primary.ID) - } + *v = *output.RegexPatternSet - if aws.StringValue(resp.RegexPatternSet.Id) == rs.Primary.ID { - *v = *resp.RegexPatternSet - return nil - } - - return fmt.Errorf("WAFv2 RegexPatternSet (%s) not found", rs.Primary.ID) + return nil } } func testAccRegexPatternSetConfig_basic(name string) string { return fmt.Sprintf(` resource "aws_wafv2_regex_pattern_set" "test" { - name = "%s" - description = "%s" + name = %[1]q + description = %[1]q scope = "REGIONAL" regular_expression { @@ -294,13 +276,13 @@ resource "aws_wafv2_regex_pattern_set" "test" { regex_string = "two" } } -`, name, name) +`, name) } func testAccRegexPatternSetConfig_update(name string) string { return fmt.Sprintf(` resource "aws_wafv2_regex_pattern_set" "test" { - name = "%s" + name = %[1]q description = "Updated" scope = "REGIONAL" @@ -322,7 +304,7 @@ resource "aws_wafv2_regex_pattern_set" "test" { func testAccRegexPatternSetConfig_minimal(name string) string { return fmt.Sprintf(` resource "aws_wafv2_regex_pattern_set" "test" { - name = "%s" + name = %[1]q scope = "REGIONAL" } `, name) @@ -331,8 +313,8 @@ resource "aws_wafv2_regex_pattern_set" "test" { func testAccRegexPatternSetConfig_oneTag(name, tagKey, tagValue string) string { return fmt.Sprintf(` resource "aws_wafv2_regex_pattern_set" "test" { - name = "%s" - description = "%s" + name = %[1]q + description = %[1]q scope = "REGIONAL" regular_expression { @@ -344,17 +326,17 @@ resource "aws_wafv2_regex_pattern_set" "test" { } tags = { - "%s" = "%s" + %[2]q = %[3]q } } -`, name, name, tagKey, tagValue) +`, name, tagKey, tagValue) } func testAccRegexPatternSetConfig_twoTags(name, tag1Key, tag1Value, tag2Key, tag2Value string) string { return fmt.Sprintf(` resource "aws_wafv2_regex_pattern_set" "test" { - name = "%s" - description = "%s" + name = %[1]q + description = %[1]q scope = "REGIONAL" regular_expression { @@ -362,11 +344,11 @@ resource "aws_wafv2_regex_pattern_set" "test" { } tags = { - "%s" = "%s" - "%s" = "%s" + %[2]q = %[3]q + %[4]q = %[5]q } } -`, name, name, tag1Key, tag1Value, tag2Key, tag2Value) +`, name, tag1Key, tag1Value, tag2Key, tag2Value) } func testAccRegexPatternSetImportStateIdFunc(resourceName string) resource.ImportStateIdFunc { From 34367c9b8b5c617a924a11b825d6525d624ec8bf Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 28 Sep 2022 11:25:07 -0400 Subject: [PATCH 41/52] r/aws_wafv2_web_acl_association: Switch to 'WithoutTimeout' CRUD handlers (#15090). Acceptance test output: % make testacc TESTARGS='-run=TestAccWAFV2WebACLAssociation_' PKG=wafv2 ACCTEST_PARALLELISM=2 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/wafv2/... -v -count 1 -parallel 2 -run=TestAccWAFV2WebACLAssociation_ -timeout 180m === RUN TestAccWAFV2WebACLAssociation_basic === PAUSE TestAccWAFV2WebACLAssociation_basic === RUN TestAccWAFV2WebACLAssociation_disappears === PAUSE TestAccWAFV2WebACLAssociation_disappears === CONT TestAccWAFV2WebACLAssociation_basic === CONT TestAccWAFV2WebACLAssociation_disappears --- PASS: TestAccWAFV2WebACLAssociation_basic (63.08s) --- PASS: TestAccWAFV2WebACLAssociation_disappears (107.36s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/wafv2 111.576s --- internal/service/wafv2/web_acl_association.go | 151 +++++++++++------- .../service/wafv2/web_acl_association_test.go | 95 +++++------ 2 files changed, 140 insertions(+), 106 deletions(-) diff --git a/internal/service/wafv2/web_acl_association.go b/internal/service/wafv2/web_acl_association.go index 07bb76288ef..2226070ccab 100644 --- a/internal/service/wafv2/web_acl_association.go +++ b/internal/service/wafv2/web_acl_association.go @@ -1,6 +1,7 @@ package wafv2 import ( + "context" "fmt" "log" "strings" @@ -9,6 +10,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/wafv2" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -17,24 +19,17 @@ import ( ) const ( - WebACLAssociationCreateTimeout = 5 * time.Minute + webACLAssociationCreateTimeout = 5 * time.Minute ) func ResourceWebACLAssociation() *schema.Resource { return &schema.Resource{ - Create: resourceWebACLAssociationCreate, - Read: resourceWebACLAssociationRead, - Delete: resourceWebACLAssociationDelete, + CreateWithoutTimeout: resourceWebACLAssociationCreate, + ReadWithoutTimeout: resourceWebACLAssociationRead, + DeleteWithoutTimeout: resourceWebACLAssociationDelete, + Importer: &schema.ResourceImporter{ - State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - webAclArn, resourceArn, err := resourceACLAssociationDecodeID(d.Id()) - if err != nil { - return nil, fmt.Errorf("Error reading resource ID: %s", err) - } - d.Set("resource_arn", resourceArn) - d.Set("web_acl_arn", webAclArn) - return []*schema.ResourceData{d}, nil - }, + State: schema.ImportStatePassthrough, }, Schema: map[string]*schema.Schema{ @@ -54,86 +49,122 @@ func ResourceWebACLAssociation() *schema.Resource { } } -func resourceWebACLAssociationCreate(d *schema.ResourceData, meta interface{}) error { +func resourceWebACLAssociationCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).WAFV2Conn - resourceArn := d.Get("resource_arn").(string) - webAclArn := d.Get("web_acl_arn").(string) - params := &wafv2.AssociateWebACLInput{ - ResourceArn: aws.String(resourceArn), - WebACLArn: aws.String(webAclArn), - } - err := resource.Retry(WebACLAssociationCreateTimeout, func() *resource.RetryError { - _, err := conn.AssociateWebACL(params) - if err != nil { - if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFUnavailableEntityException) { - return resource.RetryableError(err) - } - return resource.NonRetryableError(err) - } - return nil - }) - - if tfresource.TimedOut(err) { - _, err = conn.AssociateWebACL(params) + webACLARN := d.Get("web_acl_arn").(string) + resourceARN := d.Get("resource_arn").(string) + id := WebACLAssociationCreateResourceID(webACLARN, resourceARN) + input := &wafv2.AssociateWebACLInput{ + ResourceArn: aws.String(resourceARN), + WebACLArn: aws.String(webACLARN), } + log.Printf("[INFO] Creating WAFv2 WebACL Association: %s", input) + _, err := tfresource.RetryWhenAWSErrCodeEqualsContext(ctx, webACLAssociationCreateTimeout, func() (interface{}, error) { + return conn.AssociateWebACLWithContext(ctx, input) + }, wafv2.ErrCodeWAFUnavailableEntityException) + if err != nil { - return err + return diag.Errorf("creating WAFv2 WebACL Association (%s): %s", id, err) } - d.SetId(fmt.Sprintf("%s,%s", webAclArn, resourceArn)) - return resourceWebACLAssociationRead(d, meta) + d.SetId(id) + + return resourceWebACLAssociationRead(ctx, d, meta) } -func resourceWebACLAssociationRead(d *schema.ResourceData, meta interface{}) error { +func resourceWebACLAssociationRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).WAFV2Conn - resourceArn := d.Get("resource_arn").(string) - webAclArn := d.Get("web_acl_arn").(string) - params := &wafv2.GetWebACLForResourceInput{ - ResourceArn: aws.String(resourceArn), - } - resp, err := conn.GetWebACLForResource(params) + _, resourceARN, err := WebACLAssociationParseResourceID(d.Id()) + if err != nil { - if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFNonexistentItemException) { - log.Printf("[WARN] WAFv2 Web ACL (%s) not found, removing from state", webAclArn) - d.SetId("") - return nil - } - return err + return diag.FromErr(err) } - if resp == nil || resp.WebACL == nil { - log.Printf("[WARN] WAFv2 Web ACL associated resource (%s) not found, removing from state", resourceArn) + webACL, err := FindWebACLByResourceARN(ctx, conn, resourceARN) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] WAFv2 WebACL Association (%s) not found, removing from state", d.Id()) d.SetId("") + return nil + } + + if err != nil { + return diag.Errorf("reading WAFv2 WebACL Association (%s): %s", d.Id(), err) } + d.Set("resource_arn", resourceARN) + d.Set("web_acl_arn", webACL.ARN) + return nil } -func resourceWebACLAssociationDelete(d *schema.ResourceData, meta interface{}) error { +func resourceWebACLAssociationDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).WAFV2Conn - log.Printf("[INFO] Deleting WAFv2 Web ACL Association %s", d.Id()) + _, resourceARN, err := WebACLAssociationParseResourceID(d.Id()) - params := &wafv2.DisassociateWebACLInput{ - ResourceArn: aws.String(d.Get("resource_arn").(string)), + if err != nil { + return diag.FromErr(err) + } + + log.Printf("[INFO] Deleting WAFv2 WebACL Association: %s", d.Id()) + _, err = conn.DisassociateWebACLWithContext(ctx, &wafv2.DisassociateWebACLInput{ + ResourceArn: aws.String(resourceARN), + }) + + if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFNonexistentItemException) { + return nil } - _, err := conn.DisassociateWebACL(params) if err != nil { - return fmt.Errorf("Error disassociating WAFv2 Web ACL: %s", err) + return diag.Errorf("deleting WAFv2 WebACL Association (%s): %s", d.Id(), err) } return nil } -func resourceACLAssociationDecodeID(id string) (string, string, error) { - parts := strings.SplitN(id, ",", 2) +func FindWebACLByResourceARN(ctx context.Context, conn *wafv2.WAFV2, arn string) (*wafv2.WebACL, error) { + input := &wafv2.GetWebACLForResourceInput{ + ResourceArn: aws.String(arn), + } + + output, err := conn.GetWebACLForResourceWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFNonexistentItemException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.WebACL == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.WebACL, nil +} + +const webACLAssociationIDSeparator = "," + +func WebACLAssociationCreateResourceID(webACLARN, resourceARN string) string { + parts := []string{webACLARN, resourceARN} + id := strings.Join(parts, webACLAssociationIDSeparator) + + return id +} + +func WebACLAssociationParseResourceID(id string) (string, string, error) { + parts := strings.SplitN(id, webACLAssociationIDSeparator, 2) if len(parts) != 2 || parts[0] == "" || parts[1] == "" { - return "", "", fmt.Errorf("Unexpected format of ID (%s), expected WEB-ACL-ARN,RESOURCE-ARN", id) + return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected WEB-ACL-ARN%[2]sRESOURCE-ARN", id, webACLAssociationIDSeparator) } return parts[0], parts[1], nil diff --git a/internal/service/wafv2/web_acl_association_test.go b/internal/service/wafv2/web_acl_association_test.go index 3a7be9a8bcd..c3904b79e0f 100644 --- a/internal/service/wafv2/web_acl_association_test.go +++ b/internal/service/wafv2/web_acl_association_test.go @@ -1,13 +1,12 @@ package wafv2_test import ( + "context" "fmt" "regexp" "testing" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/wafv2" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "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" @@ -15,10 +14,11 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/service/apigateway" tfwafv2 "github.com/hashicorp/terraform-provider-aws/internal/service/wafv2" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccWAFV2WebACLAssociation_basic(t *testing.T) { - testName := fmt.Sprintf("web-acl-association-%s", sdkacctest.RandString(5)) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_wafv2_web_acl_association.test" resource.ParallelTest(t, resource.TestCase{ @@ -32,25 +32,24 @@ func TestAccWAFV2WebACLAssociation_basic(t *testing.T) { CheckDestroy: testAccCheckWebACLAssociationDestroy, Steps: []resource.TestStep{ { - Config: testAccWebACLAssociationConfig_basic(testName), + Config: testAccWebACLAssociationConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckWebACLAssociationExists(resourceName), - acctest.MatchResourceAttrRegionalARNNoAccount(resourceName, "resource_arn", "apigateway", regexp.MustCompile(fmt.Sprintf("/restapis/.*/stages/%s", testName))), - acctest.MatchResourceAttrRegionalARN(resourceName, "web_acl_arn", "wafv2", regexp.MustCompile(fmt.Sprintf("regional/webacl/%s/.*", testName))), + acctest.MatchResourceAttrRegionalARNNoAccount(resourceName, "resource_arn", "apigateway", regexp.MustCompile(fmt.Sprintf("/restapis/.*/stages/%s", rName))), + acctest.MatchResourceAttrRegionalARN(resourceName, "web_acl_arn", "wafv2", regexp.MustCompile(fmt.Sprintf("regional/webacl/%s/.*", rName))), ), }, { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateIdFunc: testAccWebACLAssociationImportStateIdFunc(resourceName), }, }, }) } func TestAccWAFV2WebACLAssociation_disappears(t *testing.T) { - testName := fmt.Sprintf("web-acl-association-%s", sdkacctest.RandString(5)) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_wafv2_web_acl_association.test" resource.ParallelTest(t, resource.TestCase{ @@ -64,7 +63,7 @@ func TestAccWAFV2WebACLAssociation_disappears(t *testing.T) { CheckDestroy: testAccCheckWebACLAssociationDestroy, Steps: []resource.TestStep{ { - Config: testAccWebACLAssociationConfig_basic(testName), + Config: testAccWebACLAssociationConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckWebACLAssociationExists(resourceName), acctest.CheckResourceDisappears(acctest.Provider, tfwafv2.ResourceWebACLAssociation(), resourceName), @@ -72,7 +71,7 @@ func TestAccWAFV2WebACLAssociation_disappears(t *testing.T) { ExpectNonEmptyPlan: true, }, { - Config: testAccWebACLAssociationConfig_basic(testName), + Config: testAccWebACLAssociationConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckWebACLAssociationExists(resourceName), acctest.CheckResourceDisappears(acctest.Provider, apigateway.ResourceStage(), "aws_api_gateway_stage.test"), @@ -89,40 +88,55 @@ func testAccCheckWebACLAssociationDestroy(s *terraform.State) error { continue } + _, resourceARN, err := tfwafv2.WebACLAssociationParseResourceID(rs.Primary.ID) + + if err != nil { + return err + } + conn := acctest.Provider.Meta().(*conns.AWSClient).WAFV2Conn - resp, err := conn.GetWebACLForResource(&wafv2.GetWebACLForResourceInput{ - ResourceArn: aws.String(rs.Primary.Attributes["resource_arn"]), - }) - - if err == nil { - if resp == nil || resp.WebACL == nil { - return fmt.Errorf("Error getting WAFv2 WebACLAssociation") - } - - id := fmt.Sprintf("%s,%s", aws.StringValue(resp.WebACL.ARN), rs.Primary.Attributes["resource_arn"]) - if id == rs.Primary.ID { - return fmt.Errorf("WAFv2 WebACLAssociation %s still exists", rs.Primary.ID) - } - return nil + + _, err = tfwafv2.FindWebACLByResourceARN(context.Background(), conn, resourceARN) + + if tfresource.NotFound(err) { + continue } - // Return nil if the Web ACL Association is already destroyed - if tfawserr.ErrCodeEquals(err, wafv2.ErrCodeWAFNonexistentItemException) { - return nil + if err != nil { + return err } - return err + return fmt.Errorf("WAFv2 WebACL Association %s still exists", rs.Primary.ID) } return nil } -func testAccCheckWebACLAssociationExists(name string) resource.TestCheckFunc { +func testAccCheckWebACLAssociationExists(n string) resource.TestCheckFunc { return func(s *terraform.State) error { - _, ok := s.RootModule().Resources[name] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("Not found: %s", name) + return fmt.Errorf("Not found: %s", n) } + + if rs.Primary.ID == "" { + return fmt.Errorf("No WAFv2 WebACL Association ID is set") + } + + _, resourceARN, err := tfwafv2.WebACLAssociationParseResourceID(rs.Primary.ID) + + if err != nil { + return err + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).WAFV2Conn + + _, err = tfwafv2.FindWebACLByResourceARN(context.Background(), conn, resourceARN) + + if err != nil { + return err + } + return nil } } @@ -130,13 +144,13 @@ func testAccCheckWebACLAssociationExists(name string) resource.TestCheckFunc { func testAccWebACLAssociationConfig_basic(name string) string { return fmt.Sprintf(` resource "aws_api_gateway_stage" "test" { - stage_name = "%s" + stage_name = %[1]q rest_api_id = aws_api_gateway_rest_api.test.id deployment_id = aws_api_gateway_deployment.test.id } resource "aws_api_gateway_rest_api" "test" { - name = "%s" + name = %[1]q } resource "aws_api_gateway_deployment" "test" { @@ -165,7 +179,7 @@ resource "aws_api_gateway_method" "test" { } resource "aws_wafv2_web_acl" "test" { - name = "%s" + name = %[1]q scope = "REGIONAL" default_action { @@ -183,16 +197,5 @@ resource "aws_wafv2_web_acl_association" "test" { resource_arn = aws_api_gateway_stage.test.arn web_acl_arn = aws_wafv2_web_acl.test.arn } -`, name, name, name) -} - -func testAccWebACLAssociationImportStateIdFunc(resourceName string) resource.ImportStateIdFunc { - return func(s *terraform.State) (string, error) { - rs, ok := s.RootModule().Resources[resourceName] - if !ok { - return "", fmt.Errorf("Not found: %s", resourceName) - } - - return fmt.Sprintf("%s,%s", rs.Primary.Attributes["web_acl_arn"], rs.Primary.Attributes["resource_arn"]), nil - } +`, name) } From 9af776002f7e076676a12071ba8c8f4457398c3f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 28 Sep 2022 11:43:35 -0400 Subject: [PATCH 42/52] Fix semgrep 'dgryski.semgrep-go.errnilcheck.err-nil-check'. --- internal/service/wafv2/web_acl_association_test.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/internal/service/wafv2/web_acl_association_test.go b/internal/service/wafv2/web_acl_association_test.go index c3904b79e0f..18c1407c809 100644 --- a/internal/service/wafv2/web_acl_association_test.go +++ b/internal/service/wafv2/web_acl_association_test.go @@ -133,11 +133,7 @@ func testAccCheckWebACLAssociationExists(n string) resource.TestCheckFunc { _, err = tfwafv2.FindWebACLByResourceARN(context.Background(), conn, resourceARN) - if err != nil { - return err - } - - return nil + return err } } From 01bfe91de0e5b991ec95779d63bf9028f0f5ac21 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 28 Sep 2022 11:45:25 -0400 Subject: [PATCH 43/52] expandRules: Ignore rule with empty 'name'. --- internal/service/wafv2/flex.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/internal/service/wafv2/flex.go b/internal/service/wafv2/flex.go index d0bd2e1f65c..f48fb4d6147 100644 --- a/internal/service/wafv2/flex.go +++ b/internal/service/wafv2/flex.go @@ -18,7 +18,14 @@ func expandRules(l []interface{}) []*wafv2.Rule { if rule == nil { continue } - rules = append(rules, expandRule(rule.(map[string]interface{}))) + + rule := rule.(map[string]interface{}) + + // if rule["name"].(string) == "" { + // continue + // } + + rules = append(rules, expandRule(rule)) } return rules From c81b7cac75ffaee79c4e26ad37f092a3a5596d8f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 28 Sep 2022 11:45:44 -0400 Subject: [PATCH 44/52] Revert "expandRules: Ignore rule with empty 'name'." This reverts commit 01bfe91de0e5b991ec95779d63bf9028f0f5ac21. --- internal/service/wafv2/flex.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/internal/service/wafv2/flex.go b/internal/service/wafv2/flex.go index f48fb4d6147..d0bd2e1f65c 100644 --- a/internal/service/wafv2/flex.go +++ b/internal/service/wafv2/flex.go @@ -18,14 +18,7 @@ func expandRules(l []interface{}) []*wafv2.Rule { if rule == nil { continue } - - rule := rule.(map[string]interface{}) - - // if rule["name"].(string) == "" { - // continue - // } - - rules = append(rules, expandRule(rule)) + rules = append(rules, expandRule(rule.(map[string]interface{}))) } return rules From 8e8046e2a13b0c24532cdba754af30c88eadf854 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 28 Sep 2022 11:47:43 -0400 Subject: [PATCH 45/52] WAFv2: 'body.oversize_handling' is Required (breaking change). --- internal/service/wafv2/schemas.go | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/internal/service/wafv2/schemas.go b/internal/service/wafv2/schemas.go index 3ff84be029e..76e21512186 100644 --- a/internal/service/wafv2/schemas.go +++ b/internal/service/wafv2/schemas.go @@ -397,7 +397,7 @@ func jsonBodySchema() *schema.Schema { Required: true, ValidateFunc: validation.StringInSlice(wafv2.JsonMatchScope_Values(), false), }, - "oversize_handling": oversizeHandlingRequiredSchema(), + "oversize_handling": oversizeHandlingSchema(), }, }, } @@ -664,7 +664,7 @@ func bodySchema() *schema.Schema { MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "oversize_handling": oversizeHandlingOptionalSchema(), + "oversize_handling": oversizeHandlingSchema(), }, }, } @@ -678,7 +678,7 @@ func cookiesSchema() *schema.Schema { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "match_scope": matchScopeSchema(), - "oversize_handling": oversizeHandlingRequiredSchema(), + "oversize_handling": oversizeHandlingSchema(), "match_pattern": cookiesMatchPatternSchema(), }, }, @@ -707,15 +707,7 @@ func cookiesMatchPatternSchema() *schema.Schema { } } -func oversizeHandlingOptionalSchema() *schema.Schema { - return &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice(wafv2.OversizeHandling_Values(), false), - } -} - -func oversizeHandlingRequiredSchema() *schema.Schema { +func oversizeHandlingSchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeString, Required: true, @@ -751,7 +743,7 @@ func headersSchema() *schema.Schema { }, }, "match_scope": matchScopeSchema(), - "oversize_handling": oversizeHandlingRequiredSchema(), + "oversize_handling": oversizeHandlingSchema(), }, }, } From 1d826f4dc4f2af2cd6f7097fbe92c4854e6bdd39 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 28 Sep 2022 12:54:56 -0400 Subject: [PATCH 46/52] WAFv2: Add required 'body.oversize_handling' values. --- internal/service/wafv2/rule_group_test.go | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/internal/service/wafv2/rule_group_test.go b/internal/service/wafv2/rule_group_test.go index 2907e05586f..eed9afd903b 100644 --- a/internal/service/wafv2/rule_group_test.go +++ b/internal/service/wafv2/rule_group_test.go @@ -2575,7 +2575,9 @@ resource "aws_wafv2_rule_group" "test" { search_string = "word" field_to_match { - body {} + body { + oversize_handling = "CONTINUE" + } } text_transformation { @@ -3706,7 +3708,9 @@ resource "aws_wafv2_rule_group" "test" { arn = aws_wafv2_regex_pattern_set.test.arn field_to_match { - body {} + body { + oversize_handling = "CONTINUE" + } } text_transformation { @@ -3898,7 +3902,9 @@ resource "aws_wafv2_rule_group" "test" { statement { sqli_match_statement { field_to_match { - body {} + body { + oversize_handling = "CONTINUE" + } } text_transformation { @@ -3952,7 +3958,9 @@ resource "aws_wafv2_rule_group" "test" { statement { xss_match_statement { field_to_match { - body {} + body { + oversize_handling = "CONTINUE" + } } text_transformation { @@ -3996,7 +4004,9 @@ resource "aws_wafv2_rule_group" "test" { statement { xss_match_statement { field_to_match { - body {} + body { + oversize_handling = "CONTINUE" + } } text_transformation { From 422c3dd24d92e3f5f682fbf1b4b658f99ddd848c Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 28 Sep 2022 13:05:56 -0400 Subject: [PATCH 47/52] WAFv2: Document required 'body.oversize_handling' value. --- website/docs/r/wafv2_rule_group.html.markdown | 3 +-- website/docs/r/wafv2_web_acl.html.markdown | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/website/docs/r/wafv2_rule_group.html.markdown b/website/docs/r/wafv2_rule_group.html.markdown index fbee3118b12..7206706086a 100644 --- a/website/docs/r/wafv2_rule_group.html.markdown +++ b/website/docs/r/wafv2_rule_group.html.markdown @@ -29,7 +29,6 @@ resource "aws_wafv2_rule_group" "example" { } statement { - geo_match_statement { country_codes = ["US", "NL"] } @@ -535,7 +534,7 @@ Inspect the request body, which immediately follows the request headers. The `body` block supports the following arguments: -* `oversize_handling` - (Optional) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. +* `oversize_handling` - (Required) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. ### Headers diff --git a/website/docs/r/wafv2_web_acl.html.markdown b/website/docs/r/wafv2_web_acl.html.markdown index 39bd256dd09..14b4f891330 100644 --- a/website/docs/r/wafv2_web_acl.html.markdown +++ b/website/docs/r/wafv2_web_acl.html.markdown @@ -586,7 +586,7 @@ Inspect the request body, which immediately follows the request headers. The `body` block supports the following arguments: -* `oversize_handling` - (Optional) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. +* `oversize_handling` - (Required) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. ### Headers From e8aa32c2c58b9181cfb0d90cdfa556fbc669c204 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 28 Sep 2022 13:08:00 -0400 Subject: [PATCH 48/52] Breaking change: Add Required 'oversize_handling' attribute to the 'field_to_match.body' block. --- .changelog/26506.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.changelog/26506.txt b/.changelog/26506.txt index 57420ebfe0c..9f4b9465df4 100644 --- a/.changelog/26506.txt +++ b/.changelog/26506.txt @@ -1,9 +1,9 @@ -```release-note:enhancement -resource/aws_wafv2_web_acl: Add `oversize_handling` attribute to the `field_to_match.body` block +```release-note:breaking-change +resource/aws_wafv2_web_acl: Add Required `oversize_handling` attribute to the `field_to_match.body` block ``` -```release-note:enhancement -resource/aws_wafv2_rule_group: Add `oversize_handling` attribute to the `field_to_match.body` block +```release-note:breaking-change +resource/aws_wafv2_rule_group: Add Required `oversize_handling` attribute to the `field_to_match.body` block ``` ```release-note:enhancement From 7a292ba5ebfa288c242f5238d4d584384b7bb4d9 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 3 Oct 2022 14:44:23 -0400 Subject: [PATCH 49/52] WAFv2: Remove 'body.oversize_handling'. --- .changelog/26506.txt | 8 ----- internal/service/wafv2/flex.go | 34 ++----------------- internal/service/wafv2/schemas.go | 15 +------- website/docs/r/wafv2_rule_group.html.markdown | 17 +++------- website/docs/r/wafv2_web_acl.html.markdown | 10 +----- 5 files changed, 8 insertions(+), 76 deletions(-) diff --git a/.changelog/26506.txt b/.changelog/26506.txt index 9f4b9465df4..bd2a48a5453 100644 --- a/.changelog/26506.txt +++ b/.changelog/26506.txt @@ -1,11 +1,3 @@ -```release-note:breaking-change -resource/aws_wafv2_web_acl: Add Required `oversize_handling` attribute to the `field_to_match.body` block -``` - -```release-note:breaking-change -resource/aws_wafv2_rule_group: Add Required `oversize_handling` attribute to the `field_to_match.body` block -``` - ```release-note:enhancement resource/aws_wafv2_web_acl: Add `headers` attribute to the `field_to_match` block ``` diff --git a/internal/service/wafv2/flex.go b/internal/service/wafv2/flex.go index d0bd2e1f65c..db125d50b13 100644 --- a/internal/service/wafv2/flex.go +++ b/internal/service/wafv2/flex.go @@ -392,7 +392,7 @@ func expandFieldToMatch(l []interface{}) *wafv2.FieldToMatch { } if v, ok := m["body"]; ok && len(v.([]interface{})) > 0 { - f.Body = expandBody(m["body"].([]interface{})) + f.Body = &wafv2.Body{} } if v, ok := m["cookies"]; ok && len(v.([]interface{})) > 0 { @@ -727,22 +727,6 @@ func expandXSSMatchStatement(l []interface{}) *wafv2.XssMatchStatement { } } -func expandBody(l []interface{}) *wafv2.Body { - if len(l) == 0 || l[0] == nil { - return nil - } - - m := l[0].(map[string]interface{}) - - apiObject := &wafv2.Body{} - - if v, ok := m["oversize_handling"].(string); ok && v != "" { - apiObject.OversizeHandling = aws.String(v) - } - - return apiObject -} - func expandHeaders(l []interface{}) *wafv2.Headers { if len(l) == 0 || l[0] == nil { return nil @@ -1335,7 +1319,7 @@ func flattenFieldToMatch(f *wafv2.FieldToMatch) interface{} { } if f.Body != nil { - m["body"] = flattenBody(f.Body) + m["body"] = make([]map[string]interface{}, 1) } if f.Cookies != nil { @@ -1629,20 +1613,6 @@ func flattenVisibilityConfig(config *wafv2.VisibilityConfig) interface{} { return []interface{}{m} } -func flattenBody(s *wafv2.Body) interface{} { - if s == nil { - return []interface{}{} - } - - m := map[string]interface{}{} - - if v := s.OversizeHandling; v != nil { - m["oversize_handling"] = aws.StringValue(v) - } - - return []interface{}{m} -} - func flattenHeaders(s *wafv2.Headers) interface{} { if s == nil { return []interface{}{} diff --git a/internal/service/wafv2/schemas.go b/internal/service/wafv2/schemas.go index 76e21512186..ab4cce2e5e2 100644 --- a/internal/service/wafv2/schemas.go +++ b/internal/service/wafv2/schemas.go @@ -321,7 +321,7 @@ func fieldToMatchBaseSchema() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ "all_query_arguments": emptySchema(), - "body": bodySchema(), + "body": emptySchema(), "cookies": cookiesSchema(), "headers": headersSchema(), "json_body": jsonBodySchema(), @@ -657,19 +657,6 @@ func customResponseBodySchema() *schema.Schema { } } -func bodySchema() *schema.Schema { - return &schema.Schema{ - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "oversize_handling": oversizeHandlingSchema(), - }, - }, - } -} - func cookiesSchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeList, diff --git a/website/docs/r/wafv2_rule_group.html.markdown b/website/docs/r/wafv2_rule_group.html.markdown index 7206706086a..e902b32b17c 100644 --- a/website/docs/r/wafv2_rule_group.html.markdown +++ b/website/docs/r/wafv2_rule_group.html.markdown @@ -29,6 +29,7 @@ resource "aws_wafv2_rule_group" "example" { } statement { + geo_match_statement { country_codes = ["US", "NL"] } @@ -144,9 +145,7 @@ resource "aws_wafv2_rule_group" "example" { sqli_match_statement { field_to_match { - body { - oversize_handling = "MATCH" - } + body {} } text_transformation { @@ -494,10 +493,10 @@ 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`, `cookies`, `headers`, `json_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`, `method`, or `query_string` attributes. +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. See [Body](#body) below for details. +* `body` - (Optional) Inspect the request body, which immediately follows the request headers. * `cookies` - (Optional) Inspect the cookies in the web request. See [Cookies](#cookies) below for details. * `headers` - (Optional) Inspect the request headers. See [Headers](#headers) below for details. * `json_body` - (Optional) Inspect the request body as JSON. See [JSON Body](#json-body) for details. @@ -528,14 +527,6 @@ The `ip_set_forwarded_ip_config` block supports the following arguments: * `header_name` - (Required) - The name of the HTTP header to use for the IP address. * `position` - (Required) - The position in the header to search for the IP address. Valid values include: `FIRST`, `LAST`, or `ANY`. If `ANY` is specified and the header contains more than 10 IP addresses, AWS WAFv2 inspects the last 10. -### Body - -Inspect the request body, which immediately follows the request headers. - -The `body` block supports the following arguments: - -* `oversize_handling` - (Required) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. - ### Headers Inspect the request headers. diff --git a/website/docs/r/wafv2_web_acl.html.markdown b/website/docs/r/wafv2_web_acl.html.markdown index 14b4f891330..520f0d7413a 100644 --- a/website/docs/r/wafv2_web_acl.html.markdown +++ b/website/docs/r/wafv2_web_acl.html.markdown @@ -546,7 +546,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`, `cookies`, `headers`, `json_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`, `method`, or `query_string` attributes. +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. See [Body](#body) below for details. @@ -580,14 +580,6 @@ The `ip_set_forwarded_ip_config` block supports the following arguments: * `header_name` - (Required) - Name of the HTTP header to use for the IP address. * `position` - (Required) - Position in the header to search for the IP address. Valid values include: `FIRST`, `LAST`, or `ANY`. If `ANY` is specified and the header contains more than 10 IP addresses, AWS WAFv2 inspects the last 10. -### Body - -Inspect the request body, which immediately follows the request headers. - -The `body` block supports the following arguments: - -* `oversize_handling` - (Required) Oversize handling tells AWS WAF what to do with a web request when the request component that the rule inspects is over the limits. Valid values include the following: `CONTINUE`, `MATCH`, `NO_MATCH`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-oversize-handling.html) for more information. - ### Headers Inspect the request headers. From 7960ee3166c48567a32b34dd82433e4bfe6af7a5 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 3 Oct 2022 14:44:29 -0400 Subject: [PATCH 50/52] Revert "WAFv2: Add required 'body.oversize_handling' values." This reverts commit 1d826f4dc4f2af2cd6f7097fbe92c4854e6bdd39. --- internal/service/wafv2/rule_group_test.go | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/internal/service/wafv2/rule_group_test.go b/internal/service/wafv2/rule_group_test.go index eed9afd903b..2907e05586f 100644 --- a/internal/service/wafv2/rule_group_test.go +++ b/internal/service/wafv2/rule_group_test.go @@ -2575,9 +2575,7 @@ resource "aws_wafv2_rule_group" "test" { search_string = "word" field_to_match { - body { - oversize_handling = "CONTINUE" - } + body {} } text_transformation { @@ -3708,9 +3706,7 @@ resource "aws_wafv2_rule_group" "test" { arn = aws_wafv2_regex_pattern_set.test.arn field_to_match { - body { - oversize_handling = "CONTINUE" - } + body {} } text_transformation { @@ -3902,9 +3898,7 @@ resource "aws_wafv2_rule_group" "test" { statement { sqli_match_statement { field_to_match { - body { - oversize_handling = "CONTINUE" - } + body {} } text_transformation { @@ -3958,9 +3952,7 @@ resource "aws_wafv2_rule_group" "test" { statement { xss_match_statement { field_to_match { - body { - oversize_handling = "CONTINUE" - } + body {} } text_transformation { @@ -4004,9 +3996,7 @@ resource "aws_wafv2_rule_group" "test" { statement { xss_match_statement { field_to_match { - body { - oversize_handling = "CONTINUE" - } + body {} } text_transformation { From fb891e46cdec926e29db1768d34e6682fe7531fd Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 3 Oct 2022 14:47:59 -0400 Subject: [PATCH 51/52] r/aws_wafv2_rule_group: Remove 'testAccRuleGroupConfig_byteMatchStatementFieldToMatchBodyOversizeHandling'. --- internal/service/wafv2/rule_group_test.go | 73 ----------------------- 1 file changed, 73 deletions(-) diff --git a/internal/service/wafv2/rule_group_test.go b/internal/service/wafv2/rule_group_test.go index 2907e05586f..774d085350f 100644 --- a/internal/service/wafv2/rule_group_test.go +++ b/internal/service/wafv2/rule_group_test.go @@ -414,30 +414,6 @@ func TestAccWAFV2RuleGroup_ByteMatchStatement_fieldToMatch(t *testing.T) { }), ), }, - { - Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchBodyOversizeHandling(ruleGroupName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName, &v), - acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), - resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), - resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ - "statement.#": "1", - "statement.0.byte_match_statement.#": "1", - "statement.0.byte_match_statement.0.field_to_match.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.body.#": "1", - "statement.0.byte_match_statement.0.field_to_match.0.body.0.oversize_handling": "MATCH", - "statement.0.byte_match_statement.0.field_to_match.0.cookies.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.headers.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.json_body.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.method.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.query_string.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.single_header.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#": "0", - "statement.0.byte_match_statement.0.field_to_match.0.uri_path.#": "0", - }), - ), - }, { Config: testAccRuleGroupConfig_byteMatchStatementFieldToMatchCookies(ruleGroupName), Check: resource.ComposeTestCheckFunc( @@ -2655,55 +2631,6 @@ resource "aws_wafv2_rule_group" "test" { `, name) } -func testAccRuleGroupConfig_byteMatchStatementFieldToMatchBodyOversizeHandling(name string) string { - return fmt.Sprintf(` -resource "aws_wafv2_rule_group" "test" { - capacity = 15 - name = "%s" - scope = "REGIONAL" - - rule { - name = "rule-1" - priority = 1 - - action { - allow {} - } - - statement { - byte_match_statement { - positional_constraint = "CONTAINS" - search_string = "word" - - field_to_match { - body { - oversize_handling = "MATCH" - } - } - - text_transformation { - priority = 1 - type = "NONE" - } - } - } - - visibility_config { - cloudwatch_metrics_enabled = false - metric_name = "friendly-rule-metric-name" - sampled_requests_enabled = false - } - } - - visibility_config { - cloudwatch_metrics_enabled = false - metric_name = "friendly-metric-name" - sampled_requests_enabled = false - } -} -`, name) -} - func testAccRuleGroupConfig_byteMatchStatementFieldToMatchHeadersInvalidConfiguration(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { From ccf72ab5c3d7d6de5e3fd00c97cb9af6ff17515e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 3 Oct 2022 15:05:03 -0400 Subject: [PATCH 52/52] WAFv2: Restore 'json_body.oversize_handling' to Optional. --- internal/service/wafv2/schemas.go | 17 +++++++++++++---- website/docs/r/wafv2_rule_group.html.markdown | 2 +- website/docs/r/wafv2_web_acl.html.markdown | 2 +- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/internal/service/wafv2/schemas.go b/internal/service/wafv2/schemas.go index ab4cce2e5e2..ae5dfde884e 100644 --- a/internal/service/wafv2/schemas.go +++ b/internal/service/wafv2/schemas.go @@ -397,7 +397,7 @@ func jsonBodySchema() *schema.Schema { Required: true, ValidateFunc: validation.StringInSlice(wafv2.JsonMatchScope_Values(), false), }, - "oversize_handling": oversizeHandlingSchema(), + "oversize_handling": oversizeHandlingOptionalSchema(wafv2.OversizeHandlingContinue), }, }, } @@ -665,7 +665,7 @@ func cookiesSchema() *schema.Schema { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "match_scope": matchScopeSchema(), - "oversize_handling": oversizeHandlingSchema(), + "oversize_handling": oversizeHandlingRequiredSchema(), "match_pattern": cookiesMatchPatternSchema(), }, }, @@ -694,7 +694,16 @@ func cookiesMatchPatternSchema() *schema.Schema { } } -func oversizeHandlingSchema() *schema.Schema { +func oversizeHandlingOptionalSchema(defaultValue string) *schema.Schema { + return &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: defaultValue, + ValidateFunc: validation.StringInSlice(wafv2.OversizeHandling_Values(), false), + } +} + +func oversizeHandlingRequiredSchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeString, Required: true, @@ -730,7 +739,7 @@ func headersSchema() *schema.Schema { }, }, "match_scope": matchScopeSchema(), - "oversize_handling": oversizeHandlingSchema(), + "oversize_handling": oversizeHandlingRequiredSchema(), }, }, } diff --git a/website/docs/r/wafv2_rule_group.html.markdown b/website/docs/r/wafv2_rule_group.html.markdown index e902b32b17c..ec02fafd14b 100644 --- a/website/docs/r/wafv2_rule_group.html.markdown +++ b/website/docs/r/wafv2_rule_group.html.markdown @@ -547,7 +547,7 @@ The `json_body` block supports the following arguments: * `invalid_fallback_behavior` - (Optional) What to do when JSON parsing fails. Defaults to evaluating up to the first parsing failure. Valid values are `EVALUATE_AS_STRING`, `MATCH` and `NO_MATCH`. * `match_pattern` - (Required) The patterns to look for in the JSON body. You must specify exactly one setting: either `all` or `included_paths`. See [JsonMatchPattern](https://docs.aws.amazon.com/waf/latest/APIReference/API_JsonMatchPattern.html) for details. * `match_scope` - (Required) The parts of the JSON to match against using the `match_pattern`. Valid values are `ALL`, `KEY` and `VALUE`. -* `oversize_handling` - (Required) What to do if the body is larger than can be inspected. Valid values are `CONTINUE`, `MATCH` and `NO_MATCH`. +* `oversize_handling` - (Optional) What to do if the body is larger than can be inspected. Valid values are `CONTINUE` (default), `MATCH` and `NO_MATCH`. ### Single Header diff --git a/website/docs/r/wafv2_web_acl.html.markdown b/website/docs/r/wafv2_web_acl.html.markdown index 520f0d7413a..32cfdbd6007 100644 --- a/website/docs/r/wafv2_web_acl.html.markdown +++ b/website/docs/r/wafv2_web_acl.html.markdown @@ -600,7 +600,7 @@ The `json_body` block supports the following arguments: * `invalid_fallback_behavior` - (Optional) What to do when JSON parsing fails. Defaults to evaluating up to the first parsing failure. Valid values are `EVALUATE_AS_STRING`, `MATCH` and `NO_MATCH`. * `match_pattern` - (Required) The patterns to look for in the JSON body. You must specify exactly one setting: either `all` or `included_paths`. See [JsonMatchPattern](https://docs.aws.amazon.com/waf/latest/APIReference/API_JsonMatchPattern.html) for details. * `match_scope` - (Required) The parts of the JSON to match against using the `match_pattern`. Valid values are `ALL`, `KEY` and `VALUE`. -* `oversize_handling` - (Required) What to do if the body is larger than can be inspected. Valid values are `CONTINUE`, `MATCH` and `NO_MATCH`. +* `oversize_handling` - (Optional) What to do if the body is larger than can be inspected. Valid values are `CONTINUE` (default), `MATCH` and `NO_MATCH`. ### Single Header