diff --git a/aws/resource_aws_wafv2_web_acl.go b/aws/resource_aws_wafv2_web_acl.go index fc3e7db7d969..437f4e669b0b 100644 --- a/aws/resource_aws_wafv2_web_acl.go +++ b/aws/resource_aws_wafv2_web_acl.go @@ -110,7 +110,6 @@ func resourceAwsWafv2WebACL() *schema.Resource { "name": { Type: schema.TypeString, Required: true, - ForceNew: true, ValidateFunc: validation.StringLenBetween(1, 128), }, "override_action": { @@ -253,7 +252,7 @@ func resourceAwsWafv2WebACLUpdate(d *schema.ResourceData, meta interface{}) erro Scope: aws.String(d.Get("scope").(string)), LockToken: aws.String(d.Get("lock_token").(string)), DefaultAction: expandWafv2DefaultAction(d.Get("default_action").([]interface{})), - Rules: expandWafv2Rules(d.Get("rule").(*schema.Set).List()), + Rules: expandWafv2WebACLRules(d.Get("rule").(*schema.Set).List()), VisibilityConfig: expandWafv2VisibilityConfig(d.Get("visibility_config").([]interface{})), } diff --git a/aws/resource_aws_wafv2_web_acl_test.go b/aws/resource_aws_wafv2_web_acl_test.go index f7bacccda871..7c6af045b510 100644 --- a/aws/resource_aws_wafv2_web_acl_test.go +++ b/aws/resource_aws_wafv2_web_acl_test.go @@ -45,7 +45,220 @@ func TestAccAwsWafv2WebACL_basic(t *testing.T) { ), }, { - Config: testAccAwsWafv2WebACLConfig_BasicUpdate(webACLName), + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccAwsWafv2WebACLImportStateIdFunc(resourceName), + }, + }, + }) +} + +func TestAccAwsWafv2WebACL_updateRule(t *testing.T) { + var v wafv2.WebACL + webACLName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_web_acl.test" + ruleName1 := fmt.Sprintf("%s-1", webACLName) + ruleName2 := fmt.Sprintf("%s-2", webACLName) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2WebACLDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2WebACLConfig_BasicRule(webACLName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), + resource.TestCheckResourceAttr(resourceName, "name", webACLName), + resource.TestCheckResourceAttr(resourceName, "description", "Updated"), + resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeRegional), + resource.TestCheckResourceAttr(resourceName, "default_action.#", "1"), + resource.TestCheckResourceAttr(resourceName, "default_action.0.allow.#", "0"), + resource.TestCheckResourceAttr(resourceName, "default_action.0.block.#", "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"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "name": ruleName1, + "priority": "10", + "action.#": "1", + "action.0.allow.#": "0", + "action.0.block.#": "0", + "action.0.count.#": "1", + "visibility_config.#": "1", + "visibility_config.0.cloudwatch_metrics_enabled": "false", + "visibility_config.0.metric_name": fmt.Sprintf("%s-metric-name-1", webACLName), + "visibility_config.0.sampled_requests_enabled": "false", + "statement.#": "1", + "statement.0.size_constraint_statement.#": "1", + "statement.0.size_constraint_statement.0.comparison_operator": "LT", + "statement.0.size_constraint_statement.0.field_to_match.#": "1", + "statement.0.size_constraint_statement.0.field_to_match.0.query_string.#": "1", + "statement.0.size_constraint_statement.0.size": "50", + "statement.0.size_constraint_statement.0.text_transformation.#": "2", + }), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*.statement.0.size_constraint_statement.0.text_transformation.*", map[string]string{ + "priority": "2", + "type": "CMD_LINE", + }), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*.statement.0.size_constraint_statement.0.text_transformation.*", map[string]string{ + "priority": "5", + "type": "NONE", + }), + ), + }, + { + // Test step to verify additional rule block with first rule block unchanged + Config: testAccAwsWafv2WebACLConfig_UpdateRuleNamePriorityMetric(webACLName, ruleName1, ruleName2, 10, 5), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), + resource.TestCheckResourceAttr(resourceName, "name", webACLName), + resource.TestCheckResourceAttr(resourceName, "description", "Updated"), + resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeRegional), + resource.TestCheckResourceAttr(resourceName, "default_action.#", "1"), + resource.TestCheckResourceAttr(resourceName, "default_action.0.allow.#", "0"), + resource.TestCheckResourceAttr(resourceName, "default_action.0.block.#", "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"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.#", "2"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "name": ruleName1, + "priority": "10", + "action.#": "1", + "action.0.allow.#": "0", + "action.0.block.#": "0", + "action.0.count.#": "1", + "visibility_config.#": "1", + "visibility_config.0.cloudwatch_metrics_enabled": "false", + "visibility_config.0.metric_name": ruleName1, + "visibility_config.0.sampled_requests_enabled": "false", + "statement.#": "1", + "statement.0.size_constraint_statement.#": "1", + "statement.0.size_constraint_statement.0.comparison_operator": "LT", + "statement.0.size_constraint_statement.0.field_to_match.#": "1", + "statement.0.size_constraint_statement.0.field_to_match.0.query_string.#": "1", + "statement.0.size_constraint_statement.0.size": "50", + "statement.0.size_constraint_statement.0.text_transformation.#": "2", + }), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*.statement.0.size_constraint_statement.0.text_transformation.*", map[string]string{ + "priority": "2", + "type": "CMD_LINE", + }), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*.statement.0.size_constraint_statement.0.text_transformation.*", map[string]string{ + "priority": "5", + "type": "NONE", + }), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "name": ruleName2, + "priority": "5", + "action.#": "1", + "action.0.allow.#": "1", + "action.0.block.#": "0", + "action.0.count.#": "0", + "visibility_config.#": "1", + "visibility_config.0.cloudwatch_metrics_enabled": "false", + "visibility_config.0.metric_name": ruleName2, + "visibility_config.0.sampled_requests_enabled": "false", + "statement.#": "1", + "statement.0.geo_match_statement.#": "1", + "statement.0.geo_match_statement.0.country_codes.#": "2", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccAwsWafv2WebACLImportStateIdFunc(resourceName), + }, + }, + }) +} + +func TestAccAwsWafv2WebACL_UpdateRuleProperties(t *testing.T) { + var v wafv2.WebACL + webACLName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_web_acl.test" + ruleName1 := fmt.Sprintf("%s-1", webACLName) + ruleName2 := fmt.Sprintf("%s-2", webACLName) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2WebACLDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2WebACLConfig_UpdateRuleNamePriorityMetric(webACLName, ruleName1, ruleName2, 5, 10), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), + resource.TestCheckResourceAttr(resourceName, "name", webACLName), + resource.TestCheckResourceAttr(resourceName, "description", "Updated"), + resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeRegional), + resource.TestCheckResourceAttr(resourceName, "default_action.#", "1"), + resource.TestCheckResourceAttr(resourceName, "default_action.0.allow.#", "0"), + resource.TestCheckResourceAttr(resourceName, "default_action.0.block.#", "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"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.#", "2"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "name": ruleName1, + "priority": "5", + "action.#": "1", + "action.0.allow.#": "0", + "action.0.block.#": "0", + "action.0.count.#": "1", + "visibility_config.#": "1", + "visibility_config.0.cloudwatch_metrics_enabled": "false", + "visibility_config.0.metric_name": ruleName1, + "visibility_config.0.sampled_requests_enabled": "false", + "statement.#": "1", + "statement.0.size_constraint_statement.#": "1", + "statement.0.size_constraint_statement.0.comparison_operator": "LT", + "statement.0.size_constraint_statement.0.field_to_match.#": "1", + "statement.0.size_constraint_statement.0.field_to_match.0.query_string.#": "1", + "statement.0.size_constraint_statement.0.size": "50", + "statement.0.size_constraint_statement.0.text_transformation.#": "2", + }), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*.statement.0.size_constraint_statement.0.text_transformation.*", map[string]string{ + "priority": "2", + "type": "CMD_LINE", + }), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*.statement.0.size_constraint_statement.0.text_transformation.*", map[string]string{ + "priority": "5", + "type": "NONE", + }), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "name": ruleName2, + "priority": "10", + "action.#": "1", + "action.0.allow.#": "1", + "action.0.block.#": "0", + "action.0.count.#": "0", + "visibility_config.#": "1", + "visibility_config.0.cloudwatch_metrics_enabled": "false", + "visibility_config.0.metric_name": ruleName2, + "visibility_config.0.sampled_requests_enabled": "false", + "statement.#": "1", + "statement.0.geo_match_statement.#": "1", + "statement.0.geo_match_statement.0.country_codes.#": "2", + }), + ), + }, + { + Config: testAccAwsWafv2WebACLConfig_UpdateRuleNamePriorityMetric(webACLName, ruleName1, ruleName2, 10, 5), Check: resource.ComposeTestCheckFunc( testAccCheckAwsWafv2WebACLExists(resourceName, &v), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), @@ -62,13 +275,17 @@ func TestAccAwsWafv2WebACL_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), resource.TestCheckResourceAttr(resourceName, "rule.#", "2"), tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ - "name": "rule-2", - "priority": "10", - "action.#": "1", - "action.0.allow.#": "0", - "action.0.block.#": "0", - "action.0.count.#": "1", - "statement.#": "1", + "name": ruleName1, + "priority": "10", + "action.#": "1", + "action.0.allow.#": "0", + "action.0.block.#": "0", + "action.0.count.#": "1", + "visibility_config.#": "1", + "visibility_config.0.cloudwatch_metrics_enabled": "false", + "visibility_config.0.metric_name": ruleName1, + "visibility_config.0.sampled_requests_enabled": "false", + "statement.#": "1", "statement.0.size_constraint_statement.#": "1", "statement.0.size_constraint_statement.0.comparison_operator": "LT", "statement.0.size_constraint_statement.0.field_to_match.#": "1", @@ -85,12 +302,80 @@ func TestAccAwsWafv2WebACL_basic(t *testing.T) { "type": "NONE", }), tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ - "name": "rule-1", - "priority": "1", - "action.#": "1", - "action.0.allow.#": "1", - "action.0.block.#": "0", - "action.0.count.#": "0", + "name": ruleName2, + "priority": "5", + "action.#": "1", + "action.0.allow.#": "1", + "action.0.block.#": "0", + "action.0.count.#": "0", + "visibility_config.#": "1", + "visibility_config.0.cloudwatch_metrics_enabled": "false", + "visibility_config.0.metric_name": ruleName2, + "visibility_config.0.sampled_requests_enabled": "false", + "statement.#": "1", + "statement.0.geo_match_statement.#": "1", + "statement.0.geo_match_statement.0.country_codes.#": "2", + }), + ), + }, + { + Config: testAccAwsWafv2WebACLConfig_UpdateRuleNamePriorityMetric(webACLName, ruleName1, "updated", 10, 5), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), + resource.TestCheckResourceAttr(resourceName, "name", webACLName), + resource.TestCheckResourceAttr(resourceName, "description", "Updated"), + resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeRegional), + resource.TestCheckResourceAttr(resourceName, "default_action.#", "1"), + resource.TestCheckResourceAttr(resourceName, "default_action.0.allow.#", "0"), + resource.TestCheckResourceAttr(resourceName, "default_action.0.block.#", "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"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.#", "2"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "name": ruleName1, + "priority": "10", + "action.#": "1", + "action.0.allow.#": "0", + "action.0.block.#": "0", + "action.0.count.#": "1", + "visibility_config.#": "1", + "visibility_config.0.cloudwatch_metrics_enabled": "false", + "visibility_config.0.metric_name": ruleName1, + "visibility_config.0.sampled_requests_enabled": "false", + "statement.#": "1", + "statement.0.size_constraint_statement.#": "1", + "statement.0.size_constraint_statement.0.comparison_operator": "LT", + "statement.0.size_constraint_statement.0.field_to_match.#": "1", + "statement.0.size_constraint_statement.0.field_to_match.0.query_string.#": "1", + "statement.0.size_constraint_statement.0.size": "50", + "statement.0.size_constraint_statement.0.text_transformation.#": "2", + }), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*.statement.0.size_constraint_statement.0.text_transformation.*", map[string]string{ + "priority": "2", + "type": "CMD_LINE", + }), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*.statement.0.size_constraint_statement.0.text_transformation.*", map[string]string{ + "priority": "5", + "type": "NONE", + }), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "name": "updated", + "priority": "5", + "action.#": "1", + "action.0.allow.#": "1", + "action.0.block.#": "0", + "action.0.count.#": "0", + "visibility_config.#": "1", + "visibility_config.0.cloudwatch_metrics_enabled": "false", + "visibility_config.0.metric_name": "updated", + "visibility_config.0.sampled_requests_enabled": "false", + "statement.#": "1", + "statement.0.geo_match_statement.#": "1", + "statement.0.geo_match_statement.0.country_codes.#": "2", }), ), }, @@ -686,10 +971,10 @@ resource "aws_wafv2_web_acl" "test" { `, name) } -func testAccAwsWafv2WebACLConfig_BasicUpdate(name string) string { +func testAccAwsWafv2WebACLConfig_BasicRule(name string) string { return fmt.Sprintf(` resource "aws_wafv2_web_acl" "test" { - name = "%s" + name = "%[1]s" description = "Updated" scope = "REGIONAL" @@ -698,7 +983,7 @@ resource "aws_wafv2_web_acl" "test" { } rule { - name = "rule-2" + name = "%[1]s-1" priority = 10 action { @@ -728,14 +1013,70 @@ resource "aws_wafv2_web_acl" "test" { visibility_config { cloudwatch_metrics_enabled = false - metric_name = "friendly-rule-metric-name" + metric_name = "%[1]s-metric-name-1" sampled_requests_enabled = false } } + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-metric-name" + sampled_requests_enabled = false + } +} +`, name) +} + +func testAccAwsWafv2WebACLConfig_UpdateRuleNamePriorityMetric(name, ruleName1, ruleName2 string, priority1, priority2 int) string { + return fmt.Sprintf(` +resource "aws_wafv2_web_acl" "test" { + name = "%[1]s" + description = "Updated" + scope = "REGIONAL" + + default_action { + block {} + } + rule { - name = "rule-1" - priority = 1 + name = "%[2]s" + priority = %[3]d + + action { + count {} + } + + statement { + size_constraint_statement { + comparison_operator = "LT" + size = 50 + + field_to_match { + query_string {} + } + + text_transformation { + priority = 5 + type = "NONE" + } + + text_transformation { + priority = 2 + type = "CMD_LINE" + } + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "%[2]s" + sampled_requests_enabled = false + } + } + + rule { + name = "%[4]s" + priority = %[5]d action { allow {} @@ -749,7 +1090,7 @@ resource "aws_wafv2_web_acl" "test" { visibility_config { cloudwatch_metrics_enabled = false - metric_name = "friendly-rule-metric-name" + metric_name = "%[4]s" sampled_requests_enabled = false } } @@ -760,7 +1101,7 @@ resource "aws_wafv2_web_acl" "test" { sampled_requests_enabled = false } } -`, name) +`, name, ruleName1, priority1, ruleName2, priority2) } func testAccAwsWafv2WebACLConfig_ManagedRuleGroupStatement(name string) string { diff --git a/aws/wafv2_helper.go b/aws/wafv2_helper.go index 41186ff40e5d..4c85ed08cbbf 100644 --- a/aws/wafv2_helper.go +++ b/aws/wafv2_helper.go @@ -342,7 +342,6 @@ func wafv2VisibilityConfigSchema() *schema.Schema { "metric_name": { Type: schema.TypeString, Required: true, - ForceNew: true, ValidateFunc: validation.All( validation.StringLenBetween(1, 128), validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9-_]+$`), "must contain only alphanumeric hyphen and underscore characters"),