diff --git a/aws/config.go b/aws/config.go index d9858b2b596..d42325cedd7 100644 --- a/aws/config.go +++ b/aws/config.go @@ -720,7 +720,7 @@ func (c *Config) Client() (interface{}, error) { r.Retryable = aws.Bool(true) } - if r.Operation.Name == "CreateIPSet" || r.Operation.Name == "CreateRegexPatternSet" { + if r.Operation.Name == "CreateIPSet" || r.Operation.Name == "CreateRegexPatternSet" || r.Operation.Name == "CreateRuleGroup" { // WAFv2 supports tag on create which can result in the below error codes according to the documentation if isAWSErr(r.Error, wafv2.ErrCodeWAFTagOperationException, "Retry your request") { r.Retryable = aws.Bool(true) diff --git a/aws/provider.go b/aws/provider.go index 6d17d999c04..2978ccb37ca 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -887,6 +887,7 @@ func Provider() terraform.ResourceProvider { "aws_wafregional_web_acl_association": resourceAwsWafRegionalWebAclAssociation(), "aws_wafv2_ip_set": resourceAwsWafv2IPSet(), "aws_wafv2_regex_pattern_set": resourceAwsWafv2RegexPatternSet(), + "aws_wafv2_rule_group": resourceAwsWafv2RuleGroup(), "aws_worklink_fleet": resourceAwsWorkLinkFleet(), "aws_worklink_website_certificate_authority_association": resourceAwsWorkLinkWebsiteCertificateAuthorityAssociation(), "aws_workspaces_directory": resourceAwsWorkspacesDirectory(), diff --git a/aws/resource_aws_wafv2_rule_group.go b/aws/resource_aws_wafv2_rule_group.go new file mode 100644 index 00000000000..de94a5ea712 --- /dev/null +++ b/aws/resource_aws_wafv2_rule_group.go @@ -0,0 +1,1346 @@ +package aws + +import ( + "fmt" + "log" + "regexp" + "strings" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/wafv2" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" +) + +func resourceAwsWafv2RuleGroup() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsWafv2RuleGroupCreate, + Read: resourceAwsWafv2RuleGroupRead, + Update: resourceAwsWafv2RuleGroupUpdate, + Delete: resourceAwsWafv2RuleGroupDelete, + Importer: &schema.ResourceImporter{ + State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + idParts := strings.Split(d.Id(), "/") + if len(idParts) != 3 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" { + return nil, fmt.Errorf("Unexpected format of ID (%q), expected ID/NAME/SCOPE", d.Id()) + } + id := idParts[0] + name := idParts[1] + scope := idParts[2] + d.SetId(id) + d.Set("name", name) + d.Set("scope", scope) + return []*schema.ResourceData{d}, nil + }, + }, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "capacity": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + ValidateFunc: validation.IntAtLeast(1), + }, + "description": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 256), + }, + "lock_token": { + Type: schema.TypeString, + Computed: true, + }, + "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"), + ), + }, + "scope": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + wafv2.ScopeCloudfront, + wafv2.ScopeRegional, + }, false), + }, + "rule": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "action": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "allow": wafv2EmptySchema(), + "block": wafv2EmptySchema(), + "count": wafv2EmptySchema(), + }, + }, + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 128), + }, + "priority": { + Type: schema.TypeInt, + Required: true, + }, + "statement": wafv2RootStatementSchema(3), + "visibility_config": wafv2VisibilityConfigSchema(), + }, + }, + }, + "tags": tagsSchema(), + "visibility_config": wafv2VisibilityConfigSchema(), + }, + } +} + +func resourceAwsWafv2RuleGroupCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).wafv2conn + var resp *wafv2.CreateRuleGroupOutput + + params := &wafv2.CreateRuleGroupInput{ + Name: aws.String(d.Get("name").(string)), + Scope: aws.String(d.Get("scope").(string)), + Capacity: aws.Int64(int64(d.Get("capacity").(int))), + Rules: expandWafv2Rules(d.Get("rule").(*schema.Set).List()), + VisibilityConfig: expandWafv2VisibilityConfig(d.Get("visibility_config").([]interface{})), + } + + if v, ok := d.GetOk("description"); ok { + params.Description = aws.String(v.(string)) + } + + if v := d.Get("tags").(map[string]interface{}); len(v) > 0 { + params.Tags = keyvaluetags.New(v).IgnoreAws().Wafv2Tags() + } + + err := resource.Retry(5*time.Minute, func() *resource.RetryError { + var err error + resp, err = conn.CreateRuleGroup(params) + if err != nil { + if isAWSErr(err, wafv2.ErrCodeWAFUnavailableEntityException, "") { + return resource.RetryableError(err) + } + return resource.NonRetryableError(err) + } + return nil + }) + + if isResourceTimeoutError(err) { + _, err = conn.CreateRuleGroup(params) + } + + if err != nil { + return fmt.Errorf("Error creating WAFv2 RuleGroup: %s", err) + } + + if resp == nil || resp.Summary == nil { + return fmt.Errorf("Error creating WAFv2 RuleGroup") + } + + d.SetId(aws.StringValue(resp.Summary.Id)) + + return resourceAwsWafv2RuleGroupRead(d, meta) +} + +func resourceAwsWafv2RuleGroupRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).wafv2conn + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + + params := &wafv2.GetRuleGroupInput{ + Id: aws.String(d.Id()), + Name: aws.String(d.Get("name").(string)), + Scope: aws.String(d.Get("scope").(string)), + } + + resp, err := conn.GetRuleGroup(params) + if err != nil { + if isAWSErr(err, wafv2.ErrCodeWAFNonexistentItemException, "") { + log.Printf("[WARN] WAFv2 RuleGroup (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + return err + } + + if resp == nil || resp.RuleGroup == nil { + return fmt.Errorf("Error getting WAFv2 RuleGroup") + } + + d.Set("name", aws.StringValue(resp.RuleGroup.Name)) + d.Set("capacity", aws.Int64Value(resp.RuleGroup.Capacity)) + d.Set("description", aws.StringValue(resp.RuleGroup.Description)) + d.Set("arn", aws.StringValue(resp.RuleGroup.ARN)) + d.Set("lock_token", aws.StringValue(resp.LockToken)) + + if err := d.Set("rule", flattenWafv2Rules(resp.RuleGroup.Rules)); err != nil { + return fmt.Errorf("Error setting rule: %s", err) + } + + if err := d.Set("visibility_config", flattenWafv2VisibilityConfig(resp.RuleGroup.VisibilityConfig)); err != nil { + return fmt.Errorf("Error setting visibility_config: %s", err) + } + + arn := aws.StringValue(resp.RuleGroup.ARN) + tags, err := keyvaluetags.Wafv2ListTags(conn, arn) + if err != nil { + return fmt.Errorf("Error listing tags for WAFv2 RuleGroup (%s): %s", arn, err) + } + + if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return fmt.Errorf("Error setting tags: %s", err) + } + + return nil +} + +func resourceAwsWafv2RuleGroupUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).wafv2conn + + log.Printf("[INFO] Updating WAFv2 RuleGroup %s", d.Id()) + + 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: expandWafv2Rules(d.Get("rule").(*schema.Set).List()), + VisibilityConfig: expandWafv2VisibilityConfig(d.Get("visibility_config").([]interface{})), + } + + if v, ok := d.GetOk("description"); ok { + u.Description = aws.String(v.(string)) + } + + err := resource.Retry(5*time.Minute, func() *resource.RetryError { + _, err := conn.UpdateRuleGroup(u) + if err != nil { + if isAWSErr(err, wafv2.ErrCodeWAFUnavailableEntityException, "") { + return resource.RetryableError(err) + } + return resource.NonRetryableError(err) + } + return nil + }) + + if isResourceTimeoutError(err) { + _, err = conn.UpdateRuleGroup(u) + } + + if err != nil { + return fmt.Errorf("Error updating WAFv2 RuleGroup: %s", err) + } + + if d.HasChange("tags") { + o, n := d.GetChange("tags") + if err := keyvaluetags.Wafv2UpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return fmt.Errorf("Error updating tags: %s", err) + } + } + + return resourceAwsWafv2RuleGroupRead(d, meta) +} + +func resourceAwsWafv2RuleGroupDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).wafv2conn + + log.Printf("[INFO] Deleting WAFv2 RuleGroup %s", d.Id()) + + r := &wafv2.DeleteRuleGroupInput{ + 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)), + } + + err := resource.Retry(5*time.Minute, func() *resource.RetryError { + _, err := conn.DeleteRuleGroup(r) + if err != nil { + if isAWSErr(err, wafv2.ErrCodeWAFAssociatedItemException, "") { + return resource.RetryableError(err) + } + if isAWSErr(err, wafv2.ErrCodeWAFUnavailableEntityException, "") { + return resource.RetryableError(err) + } + return resource.NonRetryableError(err) + } + return nil + }) + + if isResourceTimeoutError(err) { + _, err = conn.DeleteRuleGroup(r) + } + + if err != nil { + return fmt.Errorf("Error deleting WAFv2 RuleGroup: %s", err) + } + + return nil +} + +func wafv2EmptySchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{}, + }, + } +} + +func wafv2RootStatementSchema(level int) *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "and_statement": wafv2StatementSchema(level - 1), + "byte_match_statement": wafv2ByteMatchStatementSchema(), + "geo_match_statement": wafv2GeoMatchStatementSchema(), + "ip_set_reference_statement": wafv2IpSetReferenceStatementSchema(), + "not_statement": wafv2StatementSchema(level - 1), + "or_statement": wafv2StatementSchema(level - 1), + "regex_pattern_set_reference_statement": wafv2RegexPatternSetReferenceStatementSchema(), + "size_constraint_statement": wafv2SizeConstraintSchema(), + "sqli_match_statement": wafv2SqliMatchStatementSchema(), + "xss_match_statement": wafv2XssMatchStatementSchema(), + }, + }, + } +} + +func wafv2StatementSchema(level int) *schema.Schema { + if level > 1 { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "statement": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "and_statement": wafv2StatementSchema(level - 1), + "byte_match_statement": wafv2ByteMatchStatementSchema(), + "geo_match_statement": wafv2GeoMatchStatementSchema(), + "ip_set_reference_statement": wafv2IpSetReferenceStatementSchema(), + "not_statement": wafv2StatementSchema(level - 1), + "or_statement": wafv2StatementSchema(level - 1), + "regex_pattern_set_reference_statement": wafv2RegexPatternSetReferenceStatementSchema(), + "size_constraint_statement": wafv2SizeConstraintSchema(), + "sqli_match_statement": wafv2SqliMatchStatementSchema(), + "xss_match_statement": wafv2XssMatchStatementSchema(), + }, + }, + }, + }, + }, + } + } + + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "statement": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "byte_match_statement": wafv2ByteMatchStatementSchema(), + "geo_match_statement": wafv2GeoMatchStatementSchema(), + "ip_set_reference_statement": wafv2IpSetReferenceStatementSchema(), + "regex_pattern_set_reference_statement": wafv2RegexPatternSetReferenceStatementSchema(), + "size_constraint_statement": wafv2SizeConstraintSchema(), + "sqli_match_statement": wafv2SqliMatchStatementSchema(), + "xss_match_statement": wafv2XssMatchStatementSchema(), + }, + }, + }, + }, + }, + } +} + +func wafv2ByteMatchStatementSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field_to_match": wafv2FieldToMatchSchema(), + "positional_constraint": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + wafv2.PositionalConstraintContains, + wafv2.PositionalConstraintContainsWord, + wafv2.PositionalConstraintEndsWith, + wafv2.PositionalConstraintExactly, + wafv2.PositionalConstraintStartsWith, + }, false), + }, + "search_string": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 200), + }, + "text_transformation": wafv2TextTransformationSchema(), + }, + }, + } +} + +func wafv2GeoMatchStatementSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "country_codes": { + Type: schema.TypeList, + Required: true, + MinItems: 1, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + } +} + +func wafv2IpSetReferenceStatementSchema() *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: validateArn, + }, + }, + }, + } +} + +func wafv2RegexPatternSetReferenceStatementSchema() *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: validateArn, + }, + "field_to_match": wafv2FieldToMatchSchema(), + "text_transformation": wafv2TextTransformationSchema(), + }, + }, + } +} + +func wafv2SizeConstraintSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + 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), + }, + "field_to_match": wafv2FieldToMatchSchema(), + "size": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(0, 21474836480), + }, + "text_transformation": wafv2TextTransformationSchema(), + }, + }, + } +} + +func wafv2SqliMatchStatementSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field_to_match": wafv2FieldToMatchSchema(), + "text_transformation": wafv2TextTransformationSchema(), + }, + }, + } +} + +func wafv2XssMatchStatementSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "field_to_match": wafv2FieldToMatchSchema(), + "text_transformation": wafv2TextTransformationSchema(), + }, + }, + } +} + +func wafv2FieldToMatchSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "all_query_arguments": wafv2EmptySchema(), + "body": wafv2EmptySchema(), + "method": wafv2EmptySchema(), + "query_string": wafv2EmptySchema(), + "single_header": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 40), + // The value is returned in lower case by the API. + // Trying to solve it with StateFunc and/or DiffSuppressFunc resulted in hash problem of the rule field or didn't work. + validation.StringMatch(regexp.MustCompile(`^[a-z0-9-_]+$`), "must contain only lowercase alphanumeric characters, underscores, and hyphens"), + ), + }, + }, + }, + }, + "single_query_argument": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 30), + // The value is returned in lower case by the API. + // Trying to solve it with StateFunc and/or DiffSuppressFunc resulted in hash problem of the rule field or didn't work. + validation.StringMatch(regexp.MustCompile(`^[a-z0-9-_]+$`), "must contain only lowercase alphanumeric characters, underscores, and hyphens"), + ), + }, + }, + }, + }, + "uri_path": wafv2EmptySchema(), + }, + }, + } +} + +func wafv2TextTransformationSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeSet, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "priority": { + Type: schema.TypeInt, + Required: true, + }, + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + wafv2.TextTransformationTypeCmdLine, + wafv2.TextTransformationTypeCompressWhiteSpace, + wafv2.TextTransformationTypeHtmlEntityDecode, + wafv2.TextTransformationTypeLowercase, + wafv2.TextTransformationTypeNone, + wafv2.TextTransformationTypeUrlDecode, + }, false), + }, + }, + }, + } +} + +func wafv2VisibilityConfigSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cloudwatch_metrics_enabled": { + Type: schema.TypeBool, + Required: true, + }, + "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"), + ), + }, + "sampled_requests_enabled": { + Type: schema.TypeBool, + Required: true, + }, + }, + }, + } +} + +func expandWafv2Rules(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, expandWafv2Rule(rule.(map[string]interface{}))) + } + + return rules +} + +func expandWafv2Rule(m map[string]interface{}) *wafv2.Rule { + if m == nil { + return nil + } + + return &wafv2.Rule{ + Name: aws.String(m["name"].(string)), + Priority: aws.Int64(int64(m["priority"].(int))), + Action: expandWafv2RuleAction(m["action"].([]interface{})), + Statement: expandWafv2RootStatement(m["statement"].([]interface{})), + VisibilityConfig: expandWafv2VisibilityConfig(m["visibility_config"].([]interface{})), + } +} + +func expandWafv2RuleAction(l []interface{}) *wafv2.RuleAction { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + action := &wafv2.RuleAction{} + + if v, ok := m["allow"]; ok && len(v.([]interface{})) > 0 { + action.Allow = &wafv2.AllowAction{} + } + + if v, ok := m["block"]; ok && len(v.([]interface{})) > 0 { + action.Block = &wafv2.BlockAction{} + } + + if v, ok := m["count"]; ok && len(v.([]interface{})) > 0 { + action.Count = &wafv2.CountAction{} + } + + return action +} + +func expandWafv2VisibilityConfig(l []interface{}) *wafv2.VisibilityConfig { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + configuration := &wafv2.VisibilityConfig{} + + if v, ok := m["cloudwatch_metrics_enabled"]; ok { + configuration.CloudWatchMetricsEnabled = aws.Bool(v.(bool)) + } + + if v, ok := m["metric_name"]; ok && len(v.(string)) > 0 { + configuration.MetricName = aws.String(v.(string)) + } + + if v, ok := m["sampled_requests_enabled"]; ok { + configuration.SampledRequestsEnabled = aws.Bool(v.(bool)) + } + + return configuration +} + +func expandWafv2RootStatement(l []interface{}) *wafv2.Statement { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + return expandWafv2Statement(m) +} + +func expandWafv2Statements(l []interface{}) []*wafv2.Statement { + if len(l) == 0 || l[0] == nil { + return nil + } + + statements := make([]*wafv2.Statement, 0) + + for _, statement := range l { + if statement == nil { + continue + } + statements = append(statements, expandWafv2Statement(statement.(map[string]interface{}))) + } + + return statements +} + +func expandWafv2Statement(m map[string]interface{}) *wafv2.Statement { + if m == nil { + return nil + } + + statement := &wafv2.Statement{} + + if v, ok := m["and_statement"]; ok { + statement.AndStatement = expandWafv2AndStatement(v.([]interface{})) + } + + if v, ok := m["byte_match_statement"]; ok { + statement.ByteMatchStatement = expandWafv2ByteMatchStatement(v.([]interface{})) + } + + if v, ok := m["ip_set_reference_statement"]; ok { + statement.IPSetReferenceStatement = expandWafv2IpSetReferenceStatement(v.([]interface{})) + } + + if v, ok := m["geo_match_statement"]; ok { + statement.GeoMatchStatement = expandWafv2GeoMatchStatement(v.([]interface{})) + } + + if v, ok := m["not_statement"]; ok { + statement.NotStatement = expandWafv2NotStatement(v.([]interface{})) + } + + if v, ok := m["or_statement"]; ok { + statement.OrStatement = expandWafv2OrStatement(v.([]interface{})) + } + + if v, ok := m["regex_pattern_set_reference_statement"]; ok { + statement.RegexPatternSetReferenceStatement = expandWafv2RegexPatternSetReferenceStatement(v.([]interface{})) + } + + if v, ok := m["size_constraint_statement"]; ok { + statement.SizeConstraintStatement = expandWafv2SizeConstraintStatement(v.([]interface{})) + } + + if v, ok := m["sqli_match_statement"]; ok { + statement.SqliMatchStatement = expandWafv2SqliMatchStatement(v.([]interface{})) + } + + if v, ok := m["xss_match_statement"]; ok { + statement.XssMatchStatement = expandWafv2XssMatchStatement(v.([]interface{})) + } + + return statement +} + +func expandWafv2AndStatement(l []interface{}) *wafv2.AndStatement { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + return &wafv2.AndStatement{ + Statements: expandWafv2Statements(m["statement"].([]interface{})), + } +} + +func expandWafv2ByteMatchStatement(l []interface{}) *wafv2.ByteMatchStatement { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + return &wafv2.ByteMatchStatement{ + FieldToMatch: expandWafv2FieldToMatch(m["field_to_match"].([]interface{})), + PositionalConstraint: aws.String(m["positional_constraint"].(string)), + SearchString: []byte(m["search_string"].(string)), + TextTransformations: expandWafv2TextTransformations(m["text_transformation"].(*schema.Set).List()), + } +} + +func expandWafv2FieldToMatch(l []interface{}) *wafv2.FieldToMatch { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + f := &wafv2.FieldToMatch{} + + if v, ok := m["all_query_arguments"]; ok && len(v.([]interface{})) > 0 { + f.AllQueryArguments = &wafv2.AllQueryArguments{} + } + + if v, ok := m["body"]; ok && len(v.([]interface{})) > 0 { + f.Body = &wafv2.Body{} + } + + if v, ok := m["method"]; ok && len(v.([]interface{})) > 0 { + f.Method = &wafv2.Method{} + } + + if v, ok := m["query_string"]; ok && len(v.([]interface{})) > 0 { + f.QueryString = &wafv2.QueryString{} + } + + if v, ok := m["single_header"]; ok && len(v.([]interface{})) > 0 { + f.SingleHeader = expandWafv2SingleHeader(m["single_header"].([]interface{})) + } + + if v, ok := m["single_query_argument"]; ok && len(v.([]interface{})) > 0 { + f.SingleQueryArgument = expandWafv2SingleQueryArgument(m["single_query_argument"].([]interface{})) + } + + if v, ok := m["uri_path"]; ok && len(v.([]interface{})) > 0 { + f.UriPath = &wafv2.UriPath{} + } + + return f +} + +func expandWafv2SingleHeader(l []interface{}) *wafv2.SingleHeader { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + return &wafv2.SingleHeader{ + Name: aws.String(m["name"].(string)), + } +} + +func expandWafv2SingleQueryArgument(l []interface{}) *wafv2.SingleQueryArgument { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + return &wafv2.SingleQueryArgument{ + Name: aws.String(m["name"].(string)), + } +} + +func expandWafv2TextTransformations(l []interface{}) []*wafv2.TextTransformation { + if len(l) == 0 || l[0] == nil { + return nil + } + + rules := make([]*wafv2.TextTransformation, 0) + + for _, rule := range l { + if rule == nil { + continue + } + rules = append(rules, expandWafv2TextTransformation(rule.(map[string]interface{}))) + } + + return rules +} + +func expandWafv2TextTransformation(m map[string]interface{}) *wafv2.TextTransformation { + if m == nil { + return nil + } + + return &wafv2.TextTransformation{ + Priority: aws.Int64(int64(m["priority"].(int))), + Type: aws.String(m["type"].(string)), + } +} + +func expandWafv2IpSetReferenceStatement(l []interface{}) *wafv2.IPSetReferenceStatement { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + return &wafv2.IPSetReferenceStatement{ + ARN: aws.String(m["arn"].(string)), + } +} + +func expandWafv2GeoMatchStatement(l []interface{}) *wafv2.GeoMatchStatement { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + return &wafv2.GeoMatchStatement{ + CountryCodes: expandStringList(m["country_codes"].([]interface{})), + } +} + +func expandWafv2NotStatement(l []interface{}) *wafv2.NotStatement { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + s := m["statement"].([]interface{}) + + if len(s) == 0 || s[0] == nil { + return nil + } + + m = s[0].(map[string]interface{}) + + return &wafv2.NotStatement{ + Statement: expandWafv2Statement(m), + } +} + +func expandWafv2OrStatement(l []interface{}) *wafv2.OrStatement { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + return &wafv2.OrStatement{ + Statements: expandWafv2Statements(m["statement"].([]interface{})), + } +} + +func expandWafv2RegexPatternSetReferenceStatement(l []interface{}) *wafv2.RegexPatternSetReferenceStatement { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + return &wafv2.RegexPatternSetReferenceStatement{ + ARN: aws.String(m["arn"].(string)), + FieldToMatch: expandWafv2FieldToMatch(m["field_to_match"].([]interface{})), + TextTransformations: expandWafv2TextTransformations(m["text_transformation"].(*schema.Set).List()), + } +} + +func expandWafv2SizeConstraintStatement(l []interface{}) *wafv2.SizeConstraintStatement { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + return &wafv2.SizeConstraintStatement{ + ComparisonOperator: aws.String(m["comparison_operator"].(string)), + FieldToMatch: expandWafv2FieldToMatch(m["field_to_match"].([]interface{})), + Size: aws.Int64(int64(m["size"].(int))), + TextTransformations: expandWafv2TextTransformations(m["text_transformation"].(*schema.Set).List()), + } +} + +func expandWafv2SqliMatchStatement(l []interface{}) *wafv2.SqliMatchStatement { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + return &wafv2.SqliMatchStatement{ + FieldToMatch: expandWafv2FieldToMatch(m["field_to_match"].([]interface{})), + TextTransformations: expandWafv2TextTransformations(m["text_transformation"].(*schema.Set).List()), + } +} + +func expandWafv2XssMatchStatement(l []interface{}) *wafv2.XssMatchStatement { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + return &wafv2.XssMatchStatement{ + FieldToMatch: expandWafv2FieldToMatch(m["field_to_match"].([]interface{})), + TextTransformations: expandWafv2TextTransformations(m["text_transformation"].(*schema.Set).List()), + } +} + +func flattenWafv2Rules(r []*wafv2.Rule) interface{} { + out := make([]map[string]interface{}, len(r)) + for i, rule := range r { + m := make(map[string]interface{}) + m["action"] = flattenWafv2RuleAction(rule.Action) + m["name"] = aws.StringValue(rule.Name) + m["priority"] = int(aws.Int64Value(rule.Priority)) + m["statement"] = flattenWafv2RootStatement(rule.Statement) + m["visibility_config"] = flattenWafv2VisibilityConfig(rule.VisibilityConfig) + out[i] = m + } + + return out +} + +func flattenWafv2RuleAction(a *wafv2.RuleAction) interface{} { + if a == nil { + return []interface{}{} + } + + m := map[string]interface{}{} + + if a.Allow != nil { + m["allow"] = make([]map[string]interface{}, 1) + } + + if a.Block != nil { + m["block"] = make([]map[string]interface{}, 1) + } + + if a.Count != nil { + m["count"] = make([]map[string]interface{}, 1) + } + + return []interface{}{m} +} + +func flattenWafv2RootStatement(s *wafv2.Statement) interface{} { + if s == nil { + return []interface{}{} + } + + return []interface{}{flattenWafv2Statement(s)} +} + +func flattenWafv2Statements(s []*wafv2.Statement) interface{} { + out := make([]interface{}, len(s)) + for i, statement := range s { + out[i] = flattenWafv2Statement(statement) + } + + return out +} + +func flattenWafv2Statement(s *wafv2.Statement) map[string]interface{} { + if s == nil { + return map[string]interface{}{} + } + + m := map[string]interface{}{} + + if s.AndStatement != nil { + m["and_statement"] = flattenWafv2AndStatement(s.AndStatement) + } + + if s.ByteMatchStatement != nil { + m["byte_match_statement"] = flattenWafv2ByteMatchStatement(s.ByteMatchStatement) + } + + if s.IPSetReferenceStatement != nil { + m["ip_set_reference_statement"] = flattenWafv2IpSetReferenceStatement(s.IPSetReferenceStatement) + } + + if s.GeoMatchStatement != nil { + m["geo_match_statement"] = flattenWafv2GeoMatchStatement(s.GeoMatchStatement) + } + + if s.NotStatement != nil { + m["not_statement"] = flattenWafv2NotStatement(s.NotStatement) + } + + if s.OrStatement != nil { + m["or_statement"] = flattenWafv2OrStatement(s.OrStatement) + } + + if s.RegexPatternSetReferenceStatement != nil { + m["regex_pattern_set_reference_statement"] = flattenWafv2RegexPatternSetReferenceStatement(s.RegexPatternSetReferenceStatement) + } + + if s.SizeConstraintStatement != nil { + m["size_constraint_statement"] = flattenWafv2SizeConstraintStatement(s.SizeConstraintStatement) + } + + if s.SqliMatchStatement != nil { + m["sqli_match_statement"] = flattenWafv2SqliMatchStatement(s.SqliMatchStatement) + } + + if s.XssMatchStatement != nil { + m["xss_match_statement"] = flattenWafv2XssMatchStatement(s.XssMatchStatement) + } + + return m +} + +func flattenWafv2AndStatement(a *wafv2.AndStatement) interface{} { + if a == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "statement": flattenWafv2Statements(a.Statements), + } + + return []interface{}{m} +} + +func flattenWafv2ByteMatchStatement(b *wafv2.ByteMatchStatement) interface{} { + if b == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "field_to_match": flattenWafv2FieldToMatch(b.FieldToMatch), + "positional_constraint": aws.StringValue(b.PositionalConstraint), + "search_string": string(b.SearchString), + "text_transformation": flattenWafv2TextTransformations(b.TextTransformations), + } + + return []interface{}{m} +} + +func flattenWafv2FieldToMatch(f *wafv2.FieldToMatch) interface{} { + if f == nil { + return []interface{}{} + } + + m := map[string]interface{}{} + + if f.AllQueryArguments != nil { + m["all_query_arguments"] = make([]map[string]interface{}, 1) + } + + if f.Body != nil { + m["body"] = make([]map[string]interface{}, 1) + } + + if f.Method != nil { + m["method"] = make([]map[string]interface{}, 1) + } + + if f.QueryString != nil { + m["query_string"] = make([]map[string]interface{}, 1) + } + + if f.SingleHeader != nil { + m["single_header"] = flattenWafv2SingleHeader(f.SingleHeader) + } + + if f.SingleQueryArgument != nil { + m["single_query_argument"] = flattenWafv2SingleQueryArgument(f.SingleQueryArgument) + } + + if f.UriPath != nil { + m["uri_path"] = make([]map[string]interface{}, 1) + } + + return []interface{}{m} +} + +func flattenWafv2SingleHeader(s *wafv2.SingleHeader) interface{} { + if s == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "name": aws.StringValue(s.Name), + } + + return []interface{}{m} +} + +func flattenWafv2SingleQueryArgument(s *wafv2.SingleQueryArgument) interface{} { + if s == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "name": aws.StringValue(s.Name), + } + + return []interface{}{m} +} + +func flattenWafv2TextTransformations(l []*wafv2.TextTransformation) []interface{} { + out := make([]interface{}, len(l)) + for i, t := range l { + m := make(map[string]interface{}) + m["priority"] = int(aws.Int64Value(t.Priority)) + m["type"] = aws.StringValue(t.Type) + out[i] = m + } + return out +} + +func flattenWafv2IpSetReferenceStatement(i *wafv2.IPSetReferenceStatement) interface{} { + if i == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "arn": aws.StringValue(i.ARN), + } + + return []interface{}{m} +} + +func flattenWafv2GeoMatchStatement(g *wafv2.GeoMatchStatement) interface{} { + if g == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "country_codes": flattenStringList(g.CountryCodes), + } + + return []interface{}{m} +} + +func flattenWafv2NotStatement(a *wafv2.NotStatement) interface{} { + if a == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "statement": []interface{}{flattenWafv2Statement(a.Statement)}, + } + + return []interface{}{m} +} + +func flattenWafv2OrStatement(a *wafv2.OrStatement) interface{} { + if a == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "statement": flattenWafv2Statements(a.Statements), + } + + return []interface{}{m} +} + +func flattenWafv2RegexPatternSetReferenceStatement(r *wafv2.RegexPatternSetReferenceStatement) interface{} { + if r == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "arn": aws.StringValue(r.ARN), + "field_to_match": flattenWafv2FieldToMatch(r.FieldToMatch), + "text_transformation": flattenWafv2TextTransformations(r.TextTransformations), + } + + return []interface{}{m} +} + +func flattenWafv2SizeConstraintStatement(s *wafv2.SizeConstraintStatement) interface{} { + if s == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "comparison_operator": aws.StringValue(s.ComparisonOperator), + "field_to_match": flattenWafv2FieldToMatch(s.FieldToMatch), + "size": int(aws.Int64Value(s.Size)), + "text_transformation": flattenWafv2TextTransformations(s.TextTransformations), + } + + return []interface{}{m} +} + +func flattenWafv2SqliMatchStatement(s *wafv2.SqliMatchStatement) interface{} { + if s == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "field_to_match": flattenWafv2FieldToMatch(s.FieldToMatch), + "text_transformation": flattenWafv2TextTransformations(s.TextTransformations), + } + + return []interface{}{m} +} + +func flattenWafv2XssMatchStatement(s *wafv2.XssMatchStatement) interface{} { + if s == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "field_to_match": flattenWafv2FieldToMatch(s.FieldToMatch), + "text_transformation": flattenWafv2TextTransformations(s.TextTransformations), + } + + return []interface{}{m} +} + +func flattenWafv2VisibilityConfig(config *wafv2.VisibilityConfig) interface{} { + if config == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "cloudwatch_metrics_enabled": aws.BoolValue(config.CloudWatchMetricsEnabled), + "metric_name": aws.StringValue(config.MetricName), + "sampled_requests_enabled": aws.BoolValue(config.SampledRequestsEnabled), + } + + return []interface{}{m} +} diff --git a/aws/resource_aws_wafv2_rule_group_test.go b/aws/resource_aws_wafv2_rule_group_test.go new file mode 100644 index 00000000000..55ca8757e20 --- /dev/null +++ b/aws/resource_aws_wafv2_rule_group_test.go @@ -0,0 +1,2488 @@ +package aws + +import ( + "fmt" + "regexp" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/wafv2" + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/terraform" +) + +func TestAccAwsWafv2RuleGroup_Basic(t *testing.T) { + var v wafv2.RuleGroup + ruleGroupName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_rule_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2RuleGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2RuleGroupConfig_Basic(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "capacity", "2"), + resource.TestCheckResourceAttr(resourceName, "name", ruleGroupName), + resource.TestCheckResourceAttr(resourceName, "description", ruleGroupName), + resource.TestCheckResourceAttr(resourceName, "rule.#", "0"), + resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeRegional), + 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"), + ), + }, + { + Config: testAccAwsWafv2RuleGroupConfig_BasicUpdate(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "capacity", "50"), + resource.TestCheckResourceAttr(resourceName, "name", ruleGroupName), + resource.TestCheckResourceAttr(resourceName, "description", "Updated"), + resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeRegional), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + 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, "rule.#", "2"), + resource.TestCheckResourceAttr(resourceName, "rule.1162901930.name", "rule-2"), + resource.TestCheckResourceAttr(resourceName, "rule.1162901930.priority", "10"), + resource.TestCheckResourceAttr(resourceName, "rule.1162901930.action.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1162901930.action.0.allow.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.1162901930.action.0.block.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1162901930.action.0.count.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.1162901930.statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1162901930.statement.0.size_constraint_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1162901930.statement.0.size_constraint_statement.0.comparison_operator", "LT"), + resource.TestCheckResourceAttr(resourceName, "rule.1162901930.statement.0.size_constraint_statement.0.field_to_match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1162901930.statement.0.size_constraint_statement.0.field_to_match.0.query_string.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1162901930.statement.0.size_constraint_statement.0.size", "50"), + resource.TestCheckResourceAttr(resourceName, "rule.1162901930.statement.0.size_constraint_statement.0.text_transformation.#", "2"), + resource.TestCheckResourceAttr(resourceName, "rule.1162901930.statement.0.size_constraint_statement.0.text_transformation.2212084700.priority", "2"), + resource.TestCheckResourceAttr(resourceName, "rule.1162901930.statement.0.size_constraint_statement.0.text_transformation.2212084700.type", "CMD_LINE"), + resource.TestCheckResourceAttr(resourceName, "rule.1162901930.statement.0.size_constraint_statement.0.text_transformation.4292479961.priority", "5"), + resource.TestCheckResourceAttr(resourceName, "rule.1162901930.statement.0.size_constraint_statement.0.text_transformation.4292479961.type", "NONE"), + resource.TestCheckResourceAttr(resourceName, "rule.3400404505.name", "rule-1"), + resource.TestCheckResourceAttr(resourceName, "rule.3400404505.priority", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.3400404505.action.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.3400404505.action.0.allow.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.3400404505.action.0.block.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.3400404505.action.0.count.#", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccAwsWafv2RuleGroupImportStateIdFunc(resourceName), + }, + }, + }) +} + +func TestAccAwsWafv2RuleGroup_ByteMatchStatement(t *testing.T) { + var v wafv2.RuleGroup + ruleGroupName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_rule_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2RuleGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2RuleGroupConfig_ByteMatchStatement(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.3451531458.statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.3451531458.statement.0.byte_match_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.3451531458.statement.0.byte_match_statement.0.positional_constraint", "CONTAINS"), + resource.TestCheckResourceAttr(resourceName, "rule.3451531458.statement.0.byte_match_statement.0.search_string", "word"), + resource.TestCheckResourceAttr(resourceName, "rule.3451531458.statement.0.byte_match_statement.0.text_transformation.#", "2"), + resource.TestCheckResourceAttr(resourceName, "rule.3451531458.statement.0.byte_match_statement.0.text_transformation.4292479961.priority", "5"), + resource.TestCheckResourceAttr(resourceName, "rule.3451531458.statement.0.byte_match_statement.0.text_transformation.4292479961.type", "NONE"), + resource.TestCheckResourceAttr(resourceName, "rule.3451531458.statement.0.byte_match_statement.0.text_transformation.2156930824.priority", "2"), + resource.TestCheckResourceAttr(resourceName, "rule.3451531458.statement.0.byte_match_statement.0.text_transformation.2156930824.type", "LOWERCASE"), + ), + }, + { + Config: testAccAwsWafv2RuleGroupConfig_ByteMatchStatement_Update(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.3446749900.statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.3446749900.statement.0.byte_match_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.3446749900.statement.0.byte_match_statement.0.positional_constraint", "EXACTLY"), + resource.TestCheckResourceAttr(resourceName, "rule.3446749900.statement.0.byte_match_statement.0.search_string", "sentence"), + resource.TestCheckResourceAttr(resourceName, "rule.3446749900.statement.0.byte_match_statement.0.text_transformation.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.3446749900.statement.0.byte_match_statement.0.text_transformation.766585421.priority", "3"), + resource.TestCheckResourceAttr(resourceName, "rule.3446749900.statement.0.byte_match_statement.0.text_transformation.766585421.type", "CMD_LINE"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccAwsWafv2RuleGroupImportStateIdFunc(resourceName), + }, + }, + }) +} + +func TestAccAwsWafv2RuleGroup_ByteMatchStatement_FieldToMatch(t *testing.T) { + var v wafv2.RuleGroup + ruleGroupName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_rule_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2RuleGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2RuleGroupConfig_ByteMatchStatement_FieldToMatchAllQueryArguments(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2971982489.statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2971982489.statement.0.byte_match_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2971982489.statement.0.byte_match_statement.0.field_to_match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2971982489.statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2971982489.statement.0.byte_match_statement.0.field_to_match.0.body.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.2971982489.statement.0.byte_match_statement.0.field_to_match.0.method.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.2971982489.statement.0.byte_match_statement.0.field_to_match.0.query_string.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.2971982489.statement.0.byte_match_statement.0.field_to_match.0.single_header.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.2971982489.statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.2971982489.statement.0.byte_match_statement.0.field_to_match.0.uri_path.#", "0"), + ), + }, + { + Config: testAccAwsWafv2RuleGroupConfig_ByteMatchStatement_FieldToMatchBody(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.4243272354.statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.4243272354.statement.0.byte_match_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.4243272354.statement.0.byte_match_statement.0.field_to_match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.4243272354.statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.4243272354.statement.0.byte_match_statement.0.field_to_match.0.body.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.4243272354.statement.0.byte_match_statement.0.field_to_match.0.method.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.4243272354.statement.0.byte_match_statement.0.field_to_match.0.query_string.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.4243272354.statement.0.byte_match_statement.0.field_to_match.0.single_header.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.4243272354.statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.4243272354.statement.0.byte_match_statement.0.field_to_match.0.uri_path.#", "0"), + ), + }, + { + Config: testAccAwsWafv2RuleGroupConfig_ByteMatchStatement_FieldToMatchMethod(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1903944126.statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1903944126.statement.0.byte_match_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1903944126.statement.0.byte_match_statement.0.field_to_match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1903944126.statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.1903944126.statement.0.byte_match_statement.0.field_to_match.0.body.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.1903944126.statement.0.byte_match_statement.0.field_to_match.0.method.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1903944126.statement.0.byte_match_statement.0.field_to_match.0.query_string.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.1903944126.statement.0.byte_match_statement.0.field_to_match.0.single_header.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.1903944126.statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.1903944126.statement.0.byte_match_statement.0.field_to_match.0.uri_path.#", "0"), + ), + }, + { + Config: testAccAwsWafv2RuleGroupConfig_ByteMatchStatement_FieldToMatchQueryString(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.95492546.statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.95492546.statement.0.byte_match_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.95492546.statement.0.byte_match_statement.0.field_to_match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.95492546.statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.95492546.statement.0.byte_match_statement.0.field_to_match.0.body.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.95492546.statement.0.byte_match_statement.0.field_to_match.0.method.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.95492546.statement.0.byte_match_statement.0.field_to_match.0.query_string.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.95492546.statement.0.byte_match_statement.0.field_to_match.0.single_header.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.95492546.statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.95492546.statement.0.byte_match_statement.0.field_to_match.0.uri_path.#", "0"), + ), + }, + { + Config: testAccAwsWafv2RuleGroupConfig_ByteMatchStatement_FieldToMatchSingleHeader(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2127391528.statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2127391528.statement.0.byte_match_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2127391528.statement.0.byte_match_statement.0.field_to_match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2127391528.statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.2127391528.statement.0.byte_match_statement.0.field_to_match.0.body.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.2127391528.statement.0.byte_match_statement.0.field_to_match.0.method.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.2127391528.statement.0.byte_match_statement.0.field_to_match.0.query_string.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.2127391528.statement.0.byte_match_statement.0.field_to_match.0.single_header.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2127391528.statement.0.byte_match_statement.0.field_to_match.0.single_header.0.name", "a-forty-character-long-header-name-40-40"), + resource.TestCheckResourceAttr(resourceName, "rule.2127391528.statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.2127391528.statement.0.byte_match_statement.0.field_to_match.0.uri_path.#", "0"), + ), + }, + { + Config: testAccAwsWafv2RuleGroupConfig_ByteMatchStatement_FieldToMatchSingleQueryArgument(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.492520910.statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.492520910.statement.0.byte_match_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.492520910.statement.0.byte_match_statement.0.field_to_match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.492520910.statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.492520910.statement.0.byte_match_statement.0.field_to_match.0.body.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.492520910.statement.0.byte_match_statement.0.field_to_match.0.method.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.492520910.statement.0.byte_match_statement.0.field_to_match.0.query_string.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.492520910.statement.0.byte_match_statement.0.field_to_match.0.single_header.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.492520910.statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.492520910.statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.0.name", "a-max-30-characters-long-name-"), + resource.TestCheckResourceAttr(resourceName, "rule.492520910.statement.0.byte_match_statement.0.field_to_match.0.uri_path.#", "0"), + ), + }, + { + Config: testAccAwsWafv2RuleGroupConfig_ByteMatchStatement_FieldToMatchUriPath(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.984121555.statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.984121555.statement.0.byte_match_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.984121555.statement.0.byte_match_statement.0.field_to_match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.984121555.statement.0.byte_match_statement.0.field_to_match.0.all_query_arguments.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.984121555.statement.0.byte_match_statement.0.field_to_match.0.body.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.984121555.statement.0.byte_match_statement.0.field_to_match.0.method.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.984121555.statement.0.byte_match_statement.0.field_to_match.0.query_string.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.984121555.statement.0.byte_match_statement.0.field_to_match.0.single_header.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.984121555.statement.0.byte_match_statement.0.field_to_match.0.single_query_argument.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.984121555.statement.0.byte_match_statement.0.field_to_match.0.uri_path.#", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccAwsWafv2RuleGroupImportStateIdFunc(resourceName), + }, + }, + }) +} + +func TestAccAwsWafv2RuleGroup_ChangeNameForceNew(t *testing.T) { + var before, after wafv2.RuleGroup + ruleGroupName := acctest.RandomWithPrefix("tf-acc-test") + ruleGroupNewName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_rule_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2RuleGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2RuleGroupConfig_Basic(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &before), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "capacity", "2"), + resource.TestCheckResourceAttr(resourceName, "name", ruleGroupName), + resource.TestCheckResourceAttr(resourceName, "description", ruleGroupName), + resource.TestCheckResourceAttr(resourceName, "rule.#", "0"), + resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeRegional), + 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"), + ), + }, + { + Config: testAccAwsWafv2RuleGroupConfig_Basic(ruleGroupNewName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &after), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "capacity", "2"), + resource.TestCheckResourceAttr(resourceName, "name", ruleGroupNewName), + resource.TestCheckResourceAttr(resourceName, "description", ruleGroupNewName), + resource.TestCheckResourceAttr(resourceName, "rule.#", "0"), + resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeRegional), + 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"), + ), + }, + }, + }) +} + +func TestAccAwsWafv2RuleGroup_ChangeCapacityForceNew(t *testing.T) { + var before, after wafv2.RuleGroup + ruleGroupName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_rule_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2RuleGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2RuleGroupConfig_Basic(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &before), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "capacity", "2"), + resource.TestCheckResourceAttr(resourceName, "name", ruleGroupName), + resource.TestCheckResourceAttr(resourceName, "description", ruleGroupName), + resource.TestCheckResourceAttr(resourceName, "rule.#", "0"), + resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeRegional), + 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"), + ), + }, + { + Config: testAccAwsWafv2RuleGroupConfig_UpdateCapacity(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &after), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "capacity", "3"), + resource.TestCheckResourceAttr(resourceName, "name", ruleGroupName), + resource.TestCheckResourceAttr(resourceName, "description", ruleGroupName), + resource.TestCheckResourceAttr(resourceName, "rule.#", "0"), + resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeRegional), + 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"), + ), + }, + }, + }) +} + +func TestAccAwsWafv2RuleGroup_ChangeMetricNameForceNew(t *testing.T) { + var before, after wafv2.RuleGroup + ruleGroupName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_rule_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2RuleGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2RuleGroupConfig_Basic(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &before), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "capacity", "2"), + resource.TestCheckResourceAttr(resourceName, "name", ruleGroupName), + resource.TestCheckResourceAttr(resourceName, "description", ruleGroupName), + resource.TestCheckResourceAttr(resourceName, "rule.#", "0"), + resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeRegional), + 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"), + ), + }, + { + Config: testAccAwsWafv2RuleGroupConfig_UpdateMetricName(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &after), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "capacity", "2"), + resource.TestCheckResourceAttr(resourceName, "name", ruleGroupName), + resource.TestCheckResourceAttr(resourceName, "description", ruleGroupName), + resource.TestCheckResourceAttr(resourceName, "rule.#", "0"), + resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeRegional), + resource.TestCheckResourceAttr(resourceName, "visibility_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "visibility_config.0.cloudwatch_metrics_enabled", "false"), + resource.TestCheckResourceAttr(resourceName, "visibility_config.0.metric_name", "updated-friendly-metric-name"), + resource.TestCheckResourceAttr(resourceName, "visibility_config.0.sampled_requests_enabled", "false"), + ), + }, + }, + }) +} + +func TestAccAwsWafv2RuleGroup_Disappears(t *testing.T) { + var v wafv2.RuleGroup + ruleGroupName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_rule_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2RuleGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2RuleGroupConfig_Minimal(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccCheckResourceDisappears(testAccProvider, resourceAwsWafv2RuleGroup(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAwsWafv2RuleGroup_GeoMatchStatement(t *testing.T) { + var v wafv2.RuleGroup + ruleGroupName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_rule_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2RuleGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2RuleGroupConfig_GeoMatchStatement(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2064993648.statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2064993648.statement.0.geo_match_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2064993648.statement.0.geo_match_statement.0.country_codes.#", "2"), + resource.TestCheckResourceAttr(resourceName, "rule.2064993648.statement.0.geo_match_statement.0.country_codes.0", "US"), + resource.TestCheckResourceAttr(resourceName, "rule.2064993648.statement.0.geo_match_statement.0.country_codes.1", "NL"), + ), + }, + { + Config: testAccAwsWafv2RuleGroupConfig_GeoMatchStatement_Update(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.4215509823.statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.4215509823.statement.0.geo_match_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.4215509823.statement.0.geo_match_statement.0.country_codes.#", "3"), + resource.TestCheckResourceAttr(resourceName, "rule.4215509823.statement.0.geo_match_statement.0.country_codes.0", "ZM"), + resource.TestCheckResourceAttr(resourceName, "rule.4215509823.statement.0.geo_match_statement.0.country_codes.1", "EE"), + resource.TestCheckResourceAttr(resourceName, "rule.4215509823.statement.0.geo_match_statement.0.country_codes.2", "MM"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccAwsWafv2RuleGroupImportStateIdFunc(resourceName), + }, + }, + }) +} + +func TestAccAwsWafv2RuleGroup_IpSetReferenceStatement(t *testing.T) { + var v wafv2.RuleGroup + var idx int + ruleGroupName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_rule_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2RuleGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2RuleGroupConfig_IpSetReferenceStatement(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + computeWafv2IpSetRefStatementIndex(&v, &idx), + testCheckResourceAttrWithIndexesAddr(resourceName, "rule.%d.statement.#", &idx, "1"), + testCheckResourceAttrWithIndexesAddr(resourceName, "rule.%d.statement.0.ip_set_reference_statement.#", &idx, "1"), + testAccMatchResourceAttrArnWithIndexesAddr(resourceName, "rule.%d.statement.0.ip_set_reference_statement.0.arn", &idx, "wafv2", regexp.MustCompile(`regional/ipset/.+$`)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccAwsWafv2RuleGroupImportStateIdFunc(resourceName), + }, + }, + }) +} + +func TestAccAwsWafv2RuleGroup_LogicalRuleStatements(t *testing.T) { + var v wafv2.RuleGroup + ruleGroupName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_rule_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2RuleGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2RuleGroupConfig_LogicalRuleStatement_And(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.867936606.statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.867936606.statement.0.and_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.867936606.statement.0.and_statement.0.statement.#", "2"), + resource.TestCheckResourceAttr(resourceName, "rule.867936606.statement.0.and_statement.0.statement.0.geo_match_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.867936606.statement.0.and_statement.0.statement.1.geo_match_statement.#", "1"), + ), + }, + { + Config: testAccAwsWafv2RuleGroupConfig_LogicalRuleStatement_NotAnd(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2966210839.statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2966210839.statement.0.not_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2966210839.statement.0.not_statement.0.statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2966210839.statement.0.not_statement.0.statement.0.and_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2966210839.statement.0.not_statement.0.statement.0.and_statement.0.statement.#", "2"), + resource.TestCheckResourceAttr(resourceName, "rule.2966210839.statement.0.not_statement.0.statement.0.and_statement.0.statement.0.geo_match_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2966210839.statement.0.not_statement.0.statement.0.and_statement.0.statement.1.geo_match_statement.#", "1"), + ), + }, + { + Config: testAccAwsWafv2RuleGroupConfig_LogicalRuleStatement_OrNotAnd(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.464720012.statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.464720012.statement.0.or_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.464720012.statement.0.or_statement.0.statement.#", "2"), + resource.TestCheckResourceAttr(resourceName, "rule.464720012.statement.0.or_statement.0.statement.0.not_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.464720012.statement.0.or_statement.0.statement.0.not_statement.0.statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.464720012.statement.0.or_statement.0.statement.0.not_statement.0.statement.0.geo_match_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.464720012.statement.0.or_statement.0.statement.1.and_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.464720012.statement.0.or_statement.0.statement.1.and_statement.0.statement.#", "2"), + resource.TestCheckResourceAttr(resourceName, "rule.464720012.statement.0.or_statement.0.statement.1.and_statement.0.statement.0.geo_match_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.464720012.statement.0.or_statement.0.statement.1.and_statement.0.statement.1.geo_match_statement.#", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccAwsWafv2RuleGroupImportStateIdFunc(resourceName), + }, + }, + }) +} + +func TestAccAwsWafv2RuleGroup_Minimal(t *testing.T) { + var v wafv2.RuleGroup + ruleGroupName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_rule_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2RuleGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2RuleGroupConfig_Minimal(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "capacity", "2"), + resource.TestCheckResourceAttr(resourceName, "name", ruleGroupName), + resource.TestCheckResourceAttr(resourceName, "description", ""), + resource.TestCheckResourceAttr(resourceName, "rule.#", "0"), + resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeRegional), + 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"), + ), + }, + }, + }) +} + +func TestAccAwsWafv2RuleGroup_RegexPatternSetReferenceStatement(t *testing.T) { + var v wafv2.RuleGroup + var idx int + ruleGroupName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_rule_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2RuleGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2RuleGroupConfig_RegexPatternSetReferenceStatement(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + computeWafv2RegexSetRefStatementIndex(&v, &idx), + testCheckResourceAttrWithIndexesAddr(resourceName, "rule.%d.statement.#", &idx, "1"), + testCheckResourceAttrWithIndexesAddr(resourceName, "rule.%d.statement.0.regex_pattern_set_reference_statement.#", &idx, "1"), + testCheckResourceAttrWithIndexesAddr(resourceName, "rule.%d.statement.0.regex_pattern_set_reference_statement.0.field_to_match.#", &idx, "1"), + testCheckResourceAttrWithIndexesAddr(resourceName, "rule.%d.statement.0.regex_pattern_set_reference_statement.0.text_transformation.#", &idx, "1"), + testAccMatchResourceAttrArnWithIndexesAddr(resourceName, "rule.%d.statement.0.regex_pattern_set_reference_statement.0.arn", &idx, "wafv2", regexp.MustCompile(`regional/regexpatternset/.+$`)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccAwsWafv2RuleGroupImportStateIdFunc(resourceName), + }, + }, + }) +} + +func TestAccAwsWafv2RuleGroup_RuleAction(t *testing.T) { + var v wafv2.RuleGroup + ruleGroupName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_rule_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2RuleGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2RuleGroupConfig_RuleActionAllow(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "capacity", "2"), + resource.TestCheckResourceAttr(resourceName, "name", ruleGroupName), + resource.TestCheckResourceAttr(resourceName, "description", ""), + resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeRegional), + resource.TestCheckResourceAttr(resourceName, "visibility_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2064993648.action.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2064993648.action.0.allow.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2064993648.action.0.block.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.2064993648.action.0.count.#", "0"), + ), + }, + { + Config: testAccAwsWafv2RuleGroupConfig_RuleActionBlock(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "capacity", "2"), + resource.TestCheckResourceAttr(resourceName, "name", ruleGroupName), + resource.TestCheckResourceAttr(resourceName, "description", ""), + resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeRegional), + resource.TestCheckResourceAttr(resourceName, "visibility_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2490412960.action.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2490412960.action.0.allow.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.2490412960.action.0.block.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2490412960.action.0.count.#", "0"), + ), + }, + { + Config: testAccAwsWafv2RuleGroupConfig_RuleActionCount(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "capacity", "2"), + resource.TestCheckResourceAttr(resourceName, "name", ruleGroupName), + resource.TestCheckResourceAttr(resourceName, "description", ""), + resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeRegional), + resource.TestCheckResourceAttr(resourceName, "visibility_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.3400404505.action.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.3400404505.action.0.allow.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.3400404505.action.0.block.#", "0"), + resource.TestCheckResourceAttr(resourceName, "rule.3400404505.action.0.count.#", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccAwsWafv2RuleGroupImportStateIdFunc(resourceName), + }, + }, + }) +} + +func TestAccAwsWafv2RuleGroup_SizeConstraintStatement(t *testing.T) { + var v wafv2.RuleGroup + ruleGroupName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_rule_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2RuleGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2RuleGroupConfig_SizeConstraintStatement(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.204038473.statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.204038473.statement.0.size_constraint_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.204038473.statement.0.size_constraint_statement.0.comparison_operator", "GT"), + resource.TestCheckResourceAttr(resourceName, "rule.204038473.statement.0.size_constraint_statement.0.size", "100"), + resource.TestCheckResourceAttr(resourceName, "rule.204038473.statement.0.size_constraint_statement.0.field_to_match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.204038473.statement.0.size_constraint_statement.0.field_to_match.0.method.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.204038473.statement.0.size_constraint_statement.0.text_transformation.#", "1"), + ), + }, + { + Config: testAccAwsWafv2RuleGroupConfig_SizeConstraintStatement_Update(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.3233602303.statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.3233602303.statement.0.size_constraint_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.3233602303.statement.0.size_constraint_statement.0.comparison_operator", "LT"), + resource.TestCheckResourceAttr(resourceName, "rule.3233602303.statement.0.size_constraint_statement.0.size", "50"), + resource.TestCheckResourceAttr(resourceName, "rule.3233602303.statement.0.size_constraint_statement.0.field_to_match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.3233602303.statement.0.size_constraint_statement.0.field_to_match.0.query_string.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.3233602303.statement.0.size_constraint_statement.0.text_transformation.#", "2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccAwsWafv2RuleGroupImportStateIdFunc(resourceName), + }, + }, + }) +} + +func TestAccAwsWafv2RuleGroup_SqliMatchStatement(t *testing.T) { + var v wafv2.RuleGroup + ruleGroupName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_rule_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2RuleGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2RuleGroupConfig_SqliMatchStatement(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1169024249.statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1169024249.statement.0.sqli_match_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1169024249.statement.0.sqli_match_statement.0.field_to_match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1169024249.statement.0.sqli_match_statement.0.field_to_match.0.all_query_arguments.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1169024249.statement.0.sqli_match_statement.0.text_transformation.#", "2"), + resource.TestCheckResourceAttr(resourceName, "rule.1169024249.statement.0.sqli_match_statement.0.text_transformation.1247795808.priority", "5"), + resource.TestCheckResourceAttr(resourceName, "rule.1169024249.statement.0.sqli_match_statement.0.text_transformation.1247795808.type", "URL_DECODE"), + resource.TestCheckResourceAttr(resourceName, "rule.1169024249.statement.0.sqli_match_statement.0.text_transformation.2156930824.priority", "2"), + resource.TestCheckResourceAttr(resourceName, "rule.1169024249.statement.0.sqli_match_statement.0.text_transformation.2156930824.type", "LOWERCASE"), + ), + }, + { + Config: testAccAwsWafv2RuleGroupConfig_SqliMatchStatement_Update(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2934928563.statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2934928563.statement.0.sqli_match_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2934928563.statement.0.sqli_match_statement.0.field_to_match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2934928563.statement.0.sqli_match_statement.0.field_to_match.0.body.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.2934928563.statement.0.sqli_match_statement.0.text_transformation.#", "3"), + resource.TestCheckResourceAttr(resourceName, "rule.2934928563.statement.0.sqli_match_statement.0.text_transformation.1247795808.priority", "5"), + resource.TestCheckResourceAttr(resourceName, "rule.2934928563.statement.0.sqli_match_statement.0.text_transformation.1247795808.type", "URL_DECODE"), + resource.TestCheckResourceAttr(resourceName, "rule.2934928563.statement.0.sqli_match_statement.0.text_transformation.4120379776.priority", "4"), + resource.TestCheckResourceAttr(resourceName, "rule.2934928563.statement.0.sqli_match_statement.0.text_transformation.4120379776.type", "HTML_ENTITY_DECODE"), + resource.TestCheckResourceAttr(resourceName, "rule.2934928563.statement.0.sqli_match_statement.0.text_transformation.1521630275.priority", "3"), + resource.TestCheckResourceAttr(resourceName, "rule.2934928563.statement.0.sqli_match_statement.0.text_transformation.1521630275.type", "COMPRESS_WHITE_SPACE"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccAwsWafv2RuleGroupImportStateIdFunc(resourceName), + }, + }, + }) +} + +func TestAccAwsWafv2RuleGroup_Tags(t *testing.T) { + var v wafv2.RuleGroup + ruleGroupName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_rule_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2RuleGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2RuleGroupConfig_OneTag(ruleGroupName, "Tag1", "Value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.Tag1", "Value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccAwsWafv2RuleGroupImportStateIdFunc(resourceName), + }, + { + Config: testAccAwsWafv2RuleGroupConfig_TwoTags(ruleGroupName, "Tag1", "Value1Updated", "Tag2", "Value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.Tag1", "Value1Updated"), + resource.TestCheckResourceAttr(resourceName, "tags.Tag2", "Value2"), + ), + }, + { + Config: testAccAwsWafv2RuleGroupConfig_OneTag(ruleGroupName, "Tag2", "Value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.Tag2", "Value2"), + ), + }, + }, + }) +} + +func TestAccAwsWafv2RuleGroup_XssMatchStatement(t *testing.T) { + var v wafv2.RuleGroup + ruleGroupName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_rule_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2RuleGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2RuleGroupConfig_XssMatchStatement(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.377378487.statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.377378487.statement.0.xss_match_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.377378487.statement.0.xss_match_statement.0.field_to_match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.377378487.statement.0.xss_match_statement.0.field_to_match.0.body.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.377378487.statement.0.xss_match_statement.0.text_transformation.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.377378487.statement.0.xss_match_statement.0.text_transformation.2336416342.priority", "2"), + resource.TestCheckResourceAttr(resourceName, "rule.377378487.statement.0.xss_match_statement.0.text_transformation.2336416342.type", "NONE"), + ), + }, + { + Config: testAccAwsWafv2RuleGroupConfig_XssMatchStatement_Update(ruleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.3452423088.statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.3452423088.statement.0.xss_match_statement.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.3452423088.statement.0.xss_match_statement.0.field_to_match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.3452423088.statement.0.xss_match_statement.0.field_to_match.0.body.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.3452423088.statement.0.xss_match_statement.0.text_transformation.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.3452423088.statement.0.xss_match_statement.0.text_transformation.2876362756.priority", "2"), + resource.TestCheckResourceAttr(resourceName, "rule.3452423088.statement.0.xss_match_statement.0.text_transformation.2876362756.type", "URL_DECODE"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccAwsWafv2RuleGroupImportStateIdFunc(resourceName), + }, + }, + }) +} + +func testAccMatchResourceAttrArnWithIndexesAddr(name, format string, idx *int, arnService string, arnResourceRegexp *regexp.Regexp) resource.TestCheckFunc { + return func(s *terraform.State) error { + return testAccMatchResourceAttrRegionalARN(name, fmt.Sprintf(format, *idx), "wafv2", arnResourceRegexp)(s) + } +} + +// Calculates the index which isn't static because ARN is generated as part of the test +func computeWafv2IpSetRefStatementIndex(r *wafv2.RuleGroup, idx *int) resource.TestCheckFunc { + return func(s *terraform.State) error { + ruleResource := resourceAwsWafv2RuleGroup().Schema["rule"].Elem.(*schema.Resource) + + rule := map[string]interface{}{ + "name": "rule-1", + "priority": 1, + "action": []interface{}{ + map[string]interface{}{ + "allow": make([]interface{}, 1), + "block": []interface{}{}, + "count": []interface{}{}, + }, + }, + "statement": []interface{}{ + map[string]interface{}{ + "and_statement": []interface{}{}, + "byte_match_statement": []interface{}{}, + "geo_match_statement": []interface{}{}, + "ip_set_reference_statement": []interface{}{ + map[string]interface{}{ + "arn": aws.StringValue(r.Rules[0].Statement.IPSetReferenceStatement.ARN), + }, + }, + "not_statement": []interface{}{}, + "or_statement": []interface{}{}, + "regex_pattern_set_reference_statement": []interface{}{}, + "size_constraint_statement": []interface{}{}, + "sqli_match_statement": []interface{}{}, + "xss_match_statement": []interface{}{}, + }, + }, + "visibility_config": []interface{}{ + map[string]interface{}{ + "cloudwatch_metrics_enabled": false, + "metric_name": "friendly-rule-metric-name", + "sampled_requests_enabled": false, + }, + }, + } + + f := schema.HashResource(ruleResource) + *idx = f(rule) + + return nil + } +} + +// Calculates the index which isn't static because ARN is generated as part of the test +func computeWafv2RegexSetRefStatementIndex(r *wafv2.RuleGroup, idx *int) resource.TestCheckFunc { + return func(s *terraform.State) error { + ruleResource := resourceAwsWafv2RuleGroup().Schema["rule"].Elem.(*schema.Resource) + textTransformationResource := ruleResource.Schema["statement"].Elem.(*schema.Resource).Schema["regex_pattern_set_reference_statement"].Elem.(*schema.Resource).Schema["text_transformation"].Elem.(*schema.Resource) + rule := map[string]interface{}{ + "name": "rule-1", + "priority": 1, + "action": []interface{}{ + map[string]interface{}{ + "allow": make([]interface{}, 1), + "block": []interface{}{}, + "count": []interface{}{}, + }, + }, + "statement": []interface{}{ + map[string]interface{}{ + "and_statement": []interface{}{}, + "byte_match_statement": []interface{}{}, + "geo_match_statement": []interface{}{}, + "ip_set_reference_statement": []interface{}{}, + "not_statement": []interface{}{}, + "or_statement": []interface{}{}, + "regex_pattern_set_reference_statement": []interface{}{ + map[string]interface{}{ + "arn": aws.StringValue(r.Rules[0].Statement.RegexPatternSetReferenceStatement.ARN), + "field_to_match": []interface{}{ + map[string]interface{}{ + "all_query_arguments": []interface{}{}, + "body": make([]interface{}, 1), + "method": []interface{}{}, + "query_string": []interface{}{}, + "single_header": []interface{}{}, + "single_query_argument": []interface{}{}, + "uri_path": []interface{}{}, + }, + }, + "text_transformation": schema.NewSet(schema.HashResource(textTransformationResource), []interface{}{ + map[string]interface{}{ + "priority": 2, + "type": "NONE", + }, + }), + }, + }, + "size_constraint_statement": []interface{}{}, + "sqli_match_statement": []interface{}{}, + "xss_match_statement": []interface{}{}, + }, + }, + "visibility_config": []interface{}{ + map[string]interface{}{ + "cloudwatch_metrics_enabled": false, + "metric_name": "friendly-rule-metric-name", + "sampled_requests_enabled": false, + }, + }, + } + + f := schema.HashResource(ruleResource) + *idx = f(rule) + + return nil + } +} + +func testAccCheckAwsWafv2RuleGroupDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_wafv2_rule_group" { + continue + } + + conn := testAccProvider.Meta().(*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 + } + + // Return nil if the RuleGroup is already destroyed + if isAWSErr(err, wafv2.ErrCodeWAFNonexistentItemException, "") { + return nil + } + + return err + } + + return nil +} + +func testAccCheckAwsWafv2RuleGroupExists(n string, v *wafv2.RuleGroup) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No WAFv2 RuleGroup ID is set") + } + + conn := testAccProvider.Meta().(*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 { + 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 + } + + return fmt.Errorf("WAFv2 RuleGroup (%s) not found", rs.Primary.ID) + } +} + +func testAccAwsWafv2RuleGroupConfig_Basic(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 2 + name = "%s" + description = "%s" + scope = "REGIONAL" + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-metric-name" + sampled_requests_enabled = false + } +} +`, name, name) +} + +func testAccAwsWafv2RuleGroupConfig_BasicUpdate(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 50 + name = "%s" + description = "Updated" + scope = "REGIONAL" + + rule { + name = "rule-2" + priority = 10 + + action { + block {} + } + + 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 = "friendly-rule-metric-name" + sampled_requests_enabled = false + } + } + + rule { + name = "rule-1" + priority = 1 + + action { + count {} + } + + statement { + geo_match_statement { + country_codes = ["US", "NL"] + } + } + + 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 testAccAwsWafv2RuleGroupConfig_UpdateCapacity(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 3 + name = "%s" + description = "%s" + scope = "REGIONAL" + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-metric-name" + sampled_requests_enabled = false + } +} +`, name, name) +} + +func testAccAwsWafv2RuleGroupConfig_UpdateMetricName(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 2 + name = "%s" + description = "%s" + scope = "REGIONAL" + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "updated-friendly-metric-name" + sampled_requests_enabled = false + } +} +`, name, name) +} + +func testAccAwsWafv2RuleGroupConfig_Minimal(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 2 + name = "%s" + scope = "REGIONAL" + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-metric-name" + sampled_requests_enabled = false + } +} +`, name) +} + +func testAccAwsWafv2RuleGroupConfig_RuleActionAllow(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 2 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + geo_match_statement { + country_codes = ["US", "NL"] + } + } + + 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 testAccAwsWafv2RuleGroupConfig_RuleActionBlock(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 2 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + block {} + } + + statement { + geo_match_statement { + country_codes = ["US", "NL"] + } + } + + 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 testAccAwsWafv2RuleGroupConfig_RuleActionCount(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 2 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + count {} + } + + statement { + geo_match_statement { + country_codes = ["US", "NL"] + } + } + + 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 testAccAwsWafv2RuleGroupConfig_ByteMatchStatement(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 300 + 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 { + all_query_arguments {} + } + + text_transformation { + priority = 5 + type = "NONE" + } + + text_transformation { + priority = 2 + type = "LOWERCASE" + } + } + } + + 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 testAccAwsWafv2RuleGroupConfig_ByteMatchStatement_Update(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 30 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + byte_match_statement { + positional_constraint = "EXACTLY" + search_string = "sentence" + + field_to_match { + all_query_arguments {} + } + + text_transformation { + priority = 3 + type = "CMD_LINE" + } + } + } + + 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 testAccAwsWafv2RuleGroupConfig_ByteMatchStatement_FieldToMatchAllQueryArguments(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 30 + 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 { + all_query_arguments {} + } + + text_transformation { + priority = 5 + 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 testAccAwsWafv2RuleGroupConfig_ByteMatchStatement_FieldToMatchBody(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 testAccAwsWafv2RuleGroupConfig_ByteMatchStatement_FieldToMatchMethod(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 { + method {} + } + + 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 testAccAwsWafv2RuleGroupConfig_ByteMatchStatement_FieldToMatchQueryString(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 { + query_string {} + } + + 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 testAccAwsWafv2RuleGroupConfig_ByteMatchStatement_FieldToMatchSingleHeader(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 { + single_header { + name = "a-forty-character-long-header-name-40-40" + } + } + + 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 testAccAwsWafv2RuleGroupConfig_ByteMatchStatement_FieldToMatchSingleQueryArgument(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 30 + 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 { + single_query_argument { + name = "a-max-30-characters-long-name-" + } + } + + 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 testAccAwsWafv2RuleGroupConfig_ByteMatchStatement_FieldToMatchUriPath(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 { + uri_path {} + } + + 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 testAccAwsWafv2RuleGroupConfig_IpSetReferenceStatement(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_ip_set" "test" { + name = "ip-set-%s" + scope = "REGIONAL" + ip_address_version = "IPV4" + addresses = ["1.1.1.1/32", "2.2.2.2/32"] +} + +resource "aws_wafv2_rule_group" "test" { + capacity = 2 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + ip_set_reference_statement { + arn = aws_wafv2_ip_set.test.arn + } + } + + 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, name) +} + +func testAccAwsWafv2RuleGroupConfig_GeoMatchStatement(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 2 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + geo_match_statement { + country_codes = ["US", "NL"] + } + } + + 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 testAccAwsWafv2RuleGroupConfig_GeoMatchStatement_Update(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 2 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + geo_match_statement { + country_codes = ["ZM", "EE", "MM"] + } + } + + 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 testAccAwsWafv2RuleGroupConfig_LogicalRuleStatement_And(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 2 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + and_statement { + statement { + geo_match_statement { + country_codes = ["US"] + } + } + statement { + geo_match_statement { + country_codes = ["NL"] + } + } + } + } + + 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 testAccAwsWafv2RuleGroupConfig_LogicalRuleStatement_NotAnd(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 2 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + not_statement { + statement { + and_statement { + statement { + geo_match_statement { + country_codes = ["US"] + } + } + statement { + geo_match_statement { + country_codes = ["NL"] + } + } + } + } + } + } + + 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 testAccAwsWafv2RuleGroupConfig_LogicalRuleStatement_OrNotAnd(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 3 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + or_statement { + statement { + not_statement { + statement { + geo_match_statement { + country_codes = ["DE"] + } + } + } + } + statement { + and_statement { + statement { + geo_match_statement { + country_codes = ["US"] + } + } + statement { + geo_match_statement { + country_codes = ["NL"] + } + } + } + } + } + } + + 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 testAccAwsWafv2RuleGroupConfig_RegexPatternSetReferenceStatement(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_regex_pattern_set" "test" { + name = "regex-pattern-set-%s" + scope = "REGIONAL" + + regular_expression { + regex_string = "one" + } +} + +resource "aws_wafv2_rule_group" "test" { + capacity = 50 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + regex_pattern_set_reference_statement { + arn = aws_wafv2_regex_pattern_set.test.arn + + field_to_match { + body {} + } + + text_transformation { + priority = 2 + 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, name) +} + +func testAccAwsWafv2RuleGroupConfig_SizeConstraintStatement(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 30 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + size_constraint_statement { + comparison_operator = "GT" + size = 100 + + field_to_match { + method {} + } + + text_transformation { + priority = 5 + 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 testAccAwsWafv2RuleGroupConfig_SizeConstraintStatement_Update(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 30 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + 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 = "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 testAccAwsWafv2RuleGroupConfig_SqliMatchStatement(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 300 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + sqli_match_statement { + field_to_match { + all_query_arguments {} + } + + text_transformation { + priority = 5 + type = "URL_DECODE" + } + + text_transformation { + priority = 2 + type = "LOWERCASE" + } + } + } + + 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 testAccAwsWafv2RuleGroupConfig_SqliMatchStatement_Update(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 300 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + sqli_match_statement { + field_to_match { + body {} + } + + text_transformation { + priority = 5 + type = "URL_DECODE" + } + + text_transformation { + priority = 4 + type = "HTML_ENTITY_DECODE" + } + + text_transformation { + priority = 3 + type = "COMPRESS_WHITE_SPACE" + } + } + } + + 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 testAccAwsWafv2RuleGroupConfig_XssMatchStatement(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 300 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + block {} + } + + statement { + xss_match_statement { + + field_to_match { + body {} + } + + text_transformation { + priority = 2 + 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 testAccAwsWafv2RuleGroupConfig_XssMatchStatement_Update(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 300 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + xss_match_statement { + field_to_match { + body {} + } + + text_transformation { + priority = 2 + type = "URL_DECODE" + } + } + } + + 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 testAccAwsWafv2RuleGroupConfig_OneTag(name, tagKey, tagValue string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 2 + name = "%s" + description = "%s" + scope = "REGIONAL" + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-metric-name" + sampled_requests_enabled = false + } + + tags = { + "%s" = "%s" + } +} +`, name, name, tagKey, tagValue) +} + +func testAccAwsWafv2RuleGroupConfig_TwoTags(name, tag1Key, tag1Value, tag2Key, tag2Value string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 2 + name = "%s" + description = "%s" + scope = "REGIONAL" + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-metric-name" + sampled_requests_enabled = false + } + + tags = { + "%s" = "%s" + "%s" = "%s" + } +} +`, name, name, tag1Key, tag1Value, tag2Key, tag2Value) +} + +func testAccAwsWafv2RuleGroupImportStateIdFunc(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/%s", rs.Primary.ID, rs.Primary.Attributes["name"], rs.Primary.Attributes["scope"]), nil + } +} diff --git a/website/aws.erb b/website/aws.erb index 883dddb4d6a..d7191bed5b3 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -3574,6 +3574,9 @@
  • aws_wafv2_regex_pattern_set
  • +
  • + aws_wafv2_rule_group +
  • diff --git a/website/docs/r/wafv2_rule_group.html.markdown b/website/docs/r/wafv2_rule_group.html.markdown new file mode 100644 index 00000000000..dbdfbff25d9 --- /dev/null +++ b/website/docs/r/wafv2_rule_group.html.markdown @@ -0,0 +1,466 @@ +--- +subcategory: "WAFv2" +layout: "aws" +page_title: "AWS: aws_wafv2_rule_group" +description: |- + Creates a WAFv2 rule group resource. +--- + +# Resource: aws_wafv2_rule_group + +Creates a WAFv2 Rule Group resource. + +## Example Usage + +### Simple + +```hcl +resource "aws_wafv2_rule_group" "example" { + name = "example-rule" + scope = "REGIONAL" + capacity = 2 + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + + geo_match_statement { + country_codes = ["US", "NL"] + } + } + + 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 + } +} +``` + +### Complex + +```hcl +resource "aws_wafv2_ip_set" "test" { + name = "test" + scope = "REGIONAL" + ip_address_version = "IPV4" + addresses = ["1.1.1.1/32", "2.2.2.2/32"] +} + +resource "aws_wafv2_regex_pattern_set" "test" { + name = "test" + scope = "REGIONAL" + + regular_expression_list { + regex_string = "one" + } +} + +resource "aws_wafv2_rule_group" "example" { + name = "complex-example" + description = "An rule group containing all statements" + scope = "REGIONAL" + capacity = 500 + + rule { + name = "rule-1" + priority = 1 + + action { + block {} + } + + statement { + + not_statement { + statement { + + and_statement { + statement { + + geo_match_statement { + country_codes = ["US"] + } + } + + statement { + + byte_match_statement { + positional_constraint = "CONTAINS" + search_string = "word" + + field_to_match { + all_query_arguments {} + } + + text_transformation { + priority = 5 + type = "CMD_LINE" + } + + text_transformation { + priority = 2 + type = "LOWERCASE" + } + } + } + } + } + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "rule-1" + sampled_requests_enabled = false + } + } + + rule { + name = "rule-2" + priority = 2 + + action { + count {} + } + + statement { + + or_statement { + statement { + + sqli_match_statement { + + field_to_match { + body {} + } + + text_transformation { + priority = 5 + type = "URL_DECODE" + } + + text_transformation { + priority = 4 + type = "HTML_ENTITY_DECODE" + } + + text_transformation { + priority = 3 + type = "COMPRESS_WHITE_SPACE" + } + } + } + + statement { + + xss_match_statement { + + field_to_match { + method {} + } + + text_transformation { + priority = 2 + type = "NONE" + } + } + } + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "rule-2" + sampled_requests_enabled = false + } + } + + rule { + name = "rule-3" + priority = 3 + + action { + block {} + } + + statement { + + size_constraint_statement { + comparison_operator = "GT" + size = 100 + + field_to_match { + single_query_argument { + name = "username" + } + } + + text_transformation { + priority = 5 + type = "NONE" + } + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "rule-3" + sampled_requests_enabled = false + } + } + + rule { + name = "rule-4" + priority = 4 + + action { + block {} + } + + statement { + + or_statement { + statement { + + ip_set_reference_statement { + arn = aws_wafv2_ip_set.test.arn + } + } + + statement { + + regex_pattern_set_reference_statement { + arn = aws_wafv2_regex_pattern_set.test.arn + + field_to_match { + single_header { + name = "referer" + } + } + + text_transformation { + priority = 2 + type = "NONE" + } + } + } + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "rule-4" + sampled_requests_enabled = false + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-metric-name" + sampled_requests_enabled = false + } + + tags = { + "Name" = "example-and-statement" + "Code" = "123456" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `capacity` - (Required) The web ACL capacity units (WCUs) required for this rule group. See [here](https://docs.aws.amazon.com/waf/latest/APIReference/API_CreateRuleGroup.html#API_CreateRuleGroup_RequestSyntax) for general information and [here](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statements-list.html) for capacity specific information. +* `description` - (Optional) A friendly description of the rule group. +* `name` - (Required) A friendly name of the rule group. +* `rule` - (Optional) The rule blocks used to identify the web requests that you want to `allow`, `block`, or `count`. See [Rules](#rules) below for details. +* `scope` - (Required) Specifies whether this is for an AWS CloudFront distribution or for a regional application. Valid values are `CLOUDFRONT` or `REGIONAL`. To work with CloudFront, you must also specify the region `us-east-1` (N. Virginia) on the AWS provider. +* `tags` - (Optional) An array of key:value pairs to associate with the resource. +* `visibility_config` - (Required) Defines and enables Amazon CloudWatch metrics and web request sample collection. See [Visibility Configuration](#visibility-configuration) below for details. + +### Rules + +Each `rule` supports the following arguments: + +* `action` - (Optional) The action that AWS WAF should take on a web request when it matches the rule's statement. Settings at the `aws_wafv2_web_acl` level can override the rule action setting. See [Action](#action) below for details. +* `name` - (Required) A friendly name of the rule. +* `priority` - (Required) If you define more than one Rule in a WebACL, AWS WAF evaluates each request against the `rules` in order based on the value of `priority`. AWS WAF processes rules with lower priority first. +* `statement` - (Required) The AWS WAF processing statement for the rule, for example `byte_match_statement` or `geo_match_statement`. See [Statement](#statement) below for details. +* `visibility_config` - (Required) Defines and enables Amazon CloudWatch metrics and web request sample collection. See [Visibility Configuration](#visibility-configuration) below for details. + +### Action + +The `action` block supports the following arguments: + +* `allow` - (Optional) Instructs AWS WAF to allow the web request. +* `block` - (Optional) Instructs AWS WAF to block the web request. +* `count` - (Optional) Instructs AWS WAF to count the web request and allow it. + +### Statement + +The processing guidance for a Rule, used by AWS WAF to determine whether a web request matches the rule. See the [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statements-list.html) for more information. + +-> **NOTE:** Although the `statement` block is recursive, currently only 3 levels are supported. + +The `statement` block supports the following arguments: + +* `and_statement` - (Optional) A logical rule statement used to combine other rule statements with AND logic. See [AND Statement](#and-statement) below for details. +* `byte_match_statement` - (Optional) A 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) A 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) A 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. +* `not_statement` - (Optional) A logical rule statement used to negate the results of another rule statement. See [NOT Statement](#not-statement) below for details. +* `or_statement` - (Optional) A logical rule statement used to combine other rule statements with OR logic. See [OR Statement](#or-statement) below for details. +* `regex_pattern_set_reference_statement` - (Optional) A rule statement used to search web request components for matches with regular expressions. See [Regex Pattern Set Reference Statement](#regex-pattern-set-reference-statement) below for details. +* `sqli_match_statement` - (Optional) An SQL injection match condition identifies the part of web requests, such as the URI or the query string, that you want AWS WAF to inspect. See [SQL Injection Match Statement](#sql-injection-match-statement) below for details. +* `xss_match_statement` - (Optional) A rule statement that defines a cross-site scripting (XSS) match search for AWS WAF to apply to web requests. See [XSS Match Statement](#xss-match-statement) below for details. + +### AND Statement + +A logical rule statement used to combine other rule statements with `AND` logic. You provide more than one `statement` within the `and_statement`. + +The `and_statement` block supports the following arguments: + +* `statement` - (Required) The statements to combine with `AND` logic. You can use any statements that can be nested. See [Statement](#statement) above for details. + +### Byte Match Statement + +The byte match statement provides the bytes to search for, the location in requests that you want AWS WAF to search, and other settings. The bytes to search for are typically a string that corresponds with ASCII characters. + +The `byte_match_statement` block supports the following arguments: + +* `field_to_match` - (Required) The part of a web request that you want AWS WAF to inspect. See [Field to Match](#field-to-match) below for details. +* `positional_constraint` - (Required) The area within the portion of a web request that you want AWS WAF to search for `search_string`. Valid values include the following: `EXACTLY`, `STARTS_WITH`, `ENDS_WITH`, `CONTAINS`, `CONTAINS_WORD`. See the AWS [documentation](https://docs.aws.amazon.com/waf/latest/APIReference/API_ByteMatchStatement.html) for more information. +* `search_string` - (Required) A string value that you want AWS WAF to search for. AWS WAF searches only in the part of web requests that you designate for inspection in `field_to_match`. The maximum length of the value is 50 bytes. +* `text_transformation` - (Required) Text transformations eliminate some of the unusual formatting that attackers use in web requests in an effort to bypass detection. See [Text Transformation](#text-transformation) below for details. + + +### GEO Match Statement + +The `geo_match_statement` block supports the following arguments: + +* `country_codes` - (Required) An array of two-character country codes, for example, [ "US", "CN" ], from the alpha-2 country ISO codes of the `ISO 3166` international standard. See the [documentation](https://docs.aws.amazon.com/waf/latest/APIReference/API_GeoMatchStatement.html) for valid values. + +### IP Set Reference Statement + +A rule statement used to detect web requests coming from particular IP addresses or address ranges. To use this, create an `aws_wafv2_ip_set` that specifies the addresses you want to detect, then use the `ARN` of that set in this statement. + +The `ip_set_reference_statement` block supports the following arguments: + +* `arn` - (Required) The Amazon Resource Name (ARN) of the IP Set that this statement references. + +### NOT Statement + +A logical rule statement used to negate the results of another rule statement. You provide one `statement` within the `not_statement`. + +The `not_statement` block supports the following arguments: + +* `statement` - (Required) The statement to negate. You can use any statement that can be nested. See [Statement](#statement) above for details. + +### OR Statement + +A logical rule statement used to combine other rule statements with `OR` logic. You provide more than one `statement` within the `or_statement`. + +The `or_statement` block supports the following arguments: + +* `statement` - (Required) The statements to combine with `OR` logic. You can use any statements that can be nested. See [Statement](#statement) above for details. + +### Regex Pattern Set Reference Statement` + +A rule statement used to search web request components for matches with regular expressions. To use this, create a `aws_wafv2_regex_pattern_set` that specifies the expressions that you want to detect, then use the `ARN` of that set in this statement. A web request matches the pattern set rule statement if the request component matches any of the patterns in the set. + +The `regex_pattern_set_reference_statement` block supports the following arguments: + +* `arn` - (Required) The Amazon Resource Name (ARN) of the Regex Pattern Set that this statement references. + +### SQL Injection Match Statement + +An SQL injection match condition identifies the part of web requests, such as the URI or the query string, that you want AWS WAF to inspect. Later in the process, when you create a web ACL, you specify whether to allow or block requests that appear to contain malicious SQL code. + +The `sqli_match_statement` block supports the following arguments: + +* `field_to_match` - (Required) The part of a web request that you want AWS WAF to inspect. See [Field to Match](#field-to-match) below for details. +* `text_transformation` - (Required) Text transformations eliminate some of the unusual formatting that attackers use in web requests in an effort to bypass detection. See [Text Transformation](#text-transformation) below for details. + +### XSS Match Statement + +The XSS match statement provides the location in requests that you want AWS WAF to search and text transformations to use on the search area before AWS WAF searches for character sequences that are likely to be malicious strings. + +The `xss_match_statement` block supports the following arguments: + +* `field_to_match` - (Required) The part of a web request that you want AWS WAF to inspect. See [Field to Match](#field-to-match) below for details. +* `text_transformation` - (Required) Text transformations eliminate some of the unusual formatting that attackers use in web requests in an effort to bypass detection. See [Text Transformation](#text-transformation) below for details. + +### Field to Match + +The part of a web request that you want AWS WAF to inspect. Include the single `field_to_match` type that you want to inspect, with additional specifications as needed, according to the type. You specify a single request component in `field_to_match` for each rule statement that requires it. To inspect more than one component of a web request, create a separate rule statement for each component. See the [documentation](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-fields.html#waf-rule-statement-request-component) for more details. + +The `field_to_match` block supports the following arguments: + +* `all_query_arguments` - (Optional) Inspect all query arguments. +* `body` - (Optional) Inspect the request body, which immediately follows the request headers. +* `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. +* `single_query_argument` - (Optional) Inspect a single query argument. See [Single Query Argument](#single-query-argument) below for details. +* `uri_path` - (Optional) Inspect the request URI path. This is the part of a web request that identifies a resource, for example, `/images/daily-ad.jpg`. + +### Single Header + +Inspect a single header. Provide the name of the header to inspect, for example, `User-Agent` or `Referer` (provided as lowercase strings). + +The `single_header` block supports the following arguments: + +* `name` - (Optional) The name of the query header to inspect. This setting must be provided as lower case characters. + +### Single Query Argument + +Inspect a single query argument. Provide the name of the query argument to inspect, such as `UserName` or `SalesRegion` (provided as lowercase strings). + +The `single_query_argument` block supports the following arguments: + +* `name` - (Optional) The name of the query header to inspect. This setting must be provided as lower case characters. + +### Text Transformation + +The `text_transformation` block supports the following arguments: + +* `priority` - (Required) The relative processing order for multiple transformations that are defined for a rule statement. AWS WAF processes all transformations, from lowest priority to highest, before inspecting the transformed content. +* `type` - (Required) The transformation to apply, you can specify the following types: `NONE`, `COMPRESS_WHITE_SPACE`, `HTML_ENTITY_DECODE`, `LOWERCASE`, `CMD_LINE`, `URL_DECODE`. See the [documentation](https://docs.aws.amazon.com/waf/latest/APIReference/API_TextTransformation.html) for more details. + +### Visibility Configuration + +The `visibility_config` block supports the following arguments: + +* `cloudwatch_metrics_enabled` - (Required) A boolean indicating whether the associated resource sends metrics to CloudWatch. For the list of available metrics, see [AWS WAF Metrics](https://docs.aws.amazon.com/waf/latest/developerguide/monitoring-cloudwatch.html#waf-metrics). +* `metric_name` - (Required) A friendly name of the CloudWatch metric. The name can contain only alphanumeric characters (A-Z, a-z, 0-9) hyphen(-) and underscore (_), with length from one to 128 characters. It can't contain whitespace or metric names reserved for AWS WAF, for example `All` and `Default_Action`. +* `sampled_requests_enabled` - (Required) A boolean indicating whether AWS WAF should store a sampling of the web requests that match the rules. You can view the sampled requests through the AWS WAF console. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The ID of the WAF rule group. +* `arn` - The ARN of the WAF rule group. + +## Import + +WAFv2 Rule Group can be imported using `ID/name/scope` e.g. + +``` +$ terraform import aws_wafv2_rule_group.example a1b2c3d4-d5f6-7777-8888-9999aaaabbbbcccc/example/REGIONAL +```