diff --git a/.changelog/28305.txt b/.changelog/28305.txt new file mode 100644 index 000000000000..168658b3eb31 --- /dev/null +++ b/.changelog/28305.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_wafv2_web_acl: Add `rule.action.challenge` argument +``` diff --git a/internal/service/wafv2/flex.go b/internal/service/wafv2/flex.go index c4067292dd82..1ddbf4da4994 100644 --- a/internal/service/wafv2/flex.go +++ b/internal/service/wafv2/flex.go @@ -84,6 +84,10 @@ func expandRuleAction(l []interface{}) *wafv2.RuleAction { action.Captcha = expandCaptchaAction(v.([]interface{})) } + if v, ok := m["challenge"]; ok && len(v.([]interface{})) > 0 { + action.Challenge = expandChallengeAction(v.([]interface{})) + } + if v, ok := m["count"]; ok && len(v.([]interface{})) > 0 { action.Count = expandCountAction(v.([]interface{})) } @@ -148,6 +152,25 @@ func expandCaptchaAction(l []interface{}) *wafv2.CaptchaAction { return action } +func expandChallengeAction(l []interface{}) *wafv2.ChallengeAction { + action := &wafv2.ChallengeAction{} + + if len(l) == 0 || l[0] == nil { + return action + } + + m, ok := l[0].(map[string]interface{}) + if !ok { + return action + } + + if v, ok := m["custom_request_handling"].([]interface{}); ok && len(v) > 0 { + action.CustomRequestHandling = expandCustomRequestHandling(v) + } + + return action +} + func expandCountAction(l []interface{}) *wafv2.CountAction { action := &wafv2.CountAction{} @@ -1118,6 +1141,10 @@ func flattenRuleAction(a *wafv2.RuleAction) interface{} { m["captcha"] = flattenCaptcha(a.Captcha) } + if a.Challenge != nil { + m["challenge"] = flattenChallenge(a.Challenge) + } + if a.Count != nil { m["count"] = flattenCount(a.Count) } @@ -1166,6 +1193,20 @@ func flattenCaptcha(a *wafv2.CaptchaAction) []interface{} { return []interface{}{m} } +func flattenChallenge(a *wafv2.ChallengeAction) []interface{} { + if a == nil { + return []interface{}{} + } + + m := map[string]interface{}{} + + if a.CustomRequestHandling != nil { + m["custom_request_handling"] = flattenCustomRequestHandling(a.CustomRequestHandling) + } + + return []interface{}{m} +} + func flattenCount(a *wafv2.CountAction) []interface{} { if a == nil { return []interface{}{} diff --git a/internal/service/wafv2/schemas.go b/internal/service/wafv2/schemas.go index 5e0a6b5705fc..830492766b2b 100644 --- a/internal/service/wafv2/schemas.go +++ b/internal/service/wafv2/schemas.go @@ -549,6 +549,19 @@ func captchaConfigSchema() *schema.Schema { } } +func challengeConfigSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "custom_request_handling": customRequestHandlingSchema(), + }, + }, + } +} + func countConfigSchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeList, diff --git a/internal/service/wafv2/web_acl.go b/internal/service/wafv2/web_acl.go index fad8847bf490..78fab219d9e2 100644 --- a/internal/service/wafv2/web_acl.go +++ b/internal/service/wafv2/web_acl.go @@ -100,10 +100,11 @@ func ResourceWebACL() *schema.Resource { MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "allow": allowConfigSchema(), - "block": blockConfigSchema(), - "captcha": captchaConfigSchema(), - "count": countConfigSchema(), + "allow": allowConfigSchema(), + "block": blockConfigSchema(), + "captcha": captchaConfigSchema(), + "challenge": challengeConfigSchema(), + "count": countConfigSchema(), }, }, }, diff --git a/internal/service/wafv2/web_acl_test.go b/internal/service/wafv2/web_acl_test.go index 93bc9e921edd..57435b7b1295 100644 --- a/internal/service/wafv2/web_acl_test.go +++ b/internal/service/wafv2/web_acl_test.go @@ -1466,10 +1466,11 @@ func TestAccWAFV2WebACL_Custom_requestHandling(t *testing.T) { "action.0.allow.0.custom_request_handling.0.insert_header.0.value": "test-value-1", "action.0.allow.0.custom_request_handling.0.insert_header.1.name": "x-hdr2", "action.0.allow.0.custom_request_handling.0.insert_header.1.value": "test-value-2", - "action.0.block.#": "0", - "action.0.captcha.#": "0", - "action.0.count.#": "0", - "priority": "1", + "action.0.block.#": "0", + "action.0.captcha.#": "0", + "action.0.challenge.#": "0", + "action.0.count.#": "0", + "priority": "1", }), resource.TestCheckResourceAttr(resourceName, "visibility_config.#", "1"), resource.TestCheckResourceAttr(resourceName, "visibility_config.0.cloudwatch_metrics_enabled", "false"), @@ -1495,12 +1496,13 @@ func TestAccWAFV2WebACL_Custom_requestHandling(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "scope", "REGIONAL"), resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ - "name": "rule-1", - "action.#": "1", - "action.0.allow.#": "0", - "action.0.block.#": "0", - "action.0.captcha.#": "0", - "action.0.count.#": "1", + "name": "rule-1", + "action.#": "1", + "action.0.allow.#": "0", + "action.0.block.#": "0", + "action.0.captcha.#": "0", + "action.0.challenge.#": "0", + "action.0.count.#": "1", "action.0.count.0.custom_request_handling.#": "1", "action.0.count.0.custom_request_handling.0.insert_header.#": "2", "action.0.count.0.custom_request_handling.0.insert_header.0.name": "x-hdr1", @@ -1527,11 +1529,12 @@ func TestAccWAFV2WebACL_Custom_requestHandling(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "scope", "REGIONAL"), resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ - "name": "rule-1", - "action.#": "1", - "action.0.allow.#": "0", - "action.0.block.#": "0", - "action.0.captcha.#": "1", + "name": "rule-1", + "action.#": "1", + "action.0.allow.#": "0", + "action.0.block.#": "0", + "action.0.captcha.#": "1", + "action.0.challenge.#": "0", "action.0.captcha.0.custom_request_handling.#": "1", "action.0.captcha.0.custom_request_handling.0.insert_header.#": "2", "action.0.captcha.0.custom_request_handling.0.insert_header.0.name": "x-hdr1", @@ -1547,6 +1550,33 @@ func TestAccWAFV2WebACL_Custom_requestHandling(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "visibility_config.0.sampled_requests_enabled", "false"), ), }, + { + Config: testAccWebACLConfig_customRequestHandlingChallenge(webACLName), + Check: resource.ComposeTestCheckFunc( + testAccCheckWebACLExists(resourceName, &v), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), + resource.TestCheckResourceAttr(resourceName, "name", webACLName), + resource.TestCheckResourceAttr(resourceName, "default_action.#", "1"), + resource.TestCheckResourceAttr(resourceName, "default_action.0.allow.#", "1"), + resource.TestCheckResourceAttr(resourceName, "default_action.0.block.#", "0"), + resource.TestCheckResourceAttr(resourceName, "scope", "REGIONAL"), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "name": "rule-1", + "action.#": "1", + "action.0.allow.#": "0", + "action.0.block.#": "0", + "action.0.captcha.#": "0", + "action.0.challenge.#": "1", + "action.0.count.#": "0", + "priority": "1", + }), + resource.TestCheckResourceAttr(resourceName, "visibility_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "visibility_config.0.cloudwatch_metrics_enabled", "false"), + resource.TestCheckResourceAttr(resourceName, "visibility_config.0.metric_name", "friendly-metric-name"), + resource.TestCheckResourceAttr(resourceName, "visibility_config.0.sampled_requests_enabled", "false"), + ), + }, }, }) } @@ -2383,6 +2413,47 @@ resource "aws_wafv2_web_acl" "test" { `, name, firstHeader, secondHeader) } +func testAccWebACLConfig_customRequestHandlingChallenge(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_web_acl" "test" { + name = %[1]q + description = %[1]q + scope = "REGIONAL" + + default_action { + allow {} + } + + rule { + name = "rule-1" + priority = 1 + + action { + challenge {} + } + + statement { + geo_match_statement { + country_codes = ["US", "CA"] + } + } + + 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 testAccWebACLConfig_customRequestHandlingCount(name, firstHeader string, secondHeader string) string { return fmt.Sprintf(` resource "aws_wafv2_web_acl" "test" { diff --git a/website/docs/r/wafv2_web_acl.html.markdown b/website/docs/r/wafv2_web_acl.html.markdown index ab8189743ea6..d94b3cc5829b 100644 --- a/website/docs/r/wafv2_web_acl.html.markdown +++ b/website/docs/r/wafv2_web_acl.html.markdown @@ -318,6 +318,7 @@ The `action` block supports the following arguments: * `allow` - (Optional) Instructs AWS WAF to allow the web request. See [Allow](#action) below for details. * `block` - (Optional) Instructs AWS WAF to block the web request. See [Block](#block) below for details. * `captcha` - (Optional) Instructs AWS WAF to run a Captcha check against the web request. See [Captcha](#captcha) below for details. +* `challenge` - (Optional) Instructs AWS WAF to run a check against the request to verify that the request is coming from a legitimate client session. See [Challenge](#challenge) below for details. * `count` - (Optional) Instructs AWS WAF to count the web request and allow it. See [Count](#count) below for details. ### Override Action @@ -347,6 +348,12 @@ The `captcha` block supports the following arguments: * `custom_request_handling` - (Optional) Defines custom handling for the web request. See [Custom Request Handling](#custom-request-handling) below for details. +### Challenge + +The `challenge` block supports the following arguments: + +* `custom_request_handling` - (Optional) Defines custom handling for the web request. See [Custom Request Handling](#custom-request-handling) below for details. + ### Count The `count` block supports the following arguments: