Skip to content

Commit

Permalink
add association_config attribute to aws_wafv2_web_acl resource
Browse files Browse the repository at this point in the history
  • Loading branch information
sergiocharpineljr committed Jul 4, 2023
1 parent a12d178 commit 4162652
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 0 deletions.
50 changes: 50 additions & 0 deletions internal/service/wafv2/flex.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,36 @@ func expandAllowAction(l []interface{}) *wafv2.AllowAction {
return action
}

func expandAssociationConfig(l []interface{}) *wafv2.AssociationConfig {
configuration := &wafv2.AssociationConfig{}

if len(l) == 0 || l[0] == nil {
return configuration
}

m := l[0].(map[string]interface{})
if v, ok := m["request_body"]; ok {
inner := v.([]interface{})
if len(inner) == 0 || inner[0] == nil {
return configuration
}

m = inner[0].(map[string]interface{})

if t, ok := m["key"]; ok {
if v, ok := m["default_size_inspection_limit"]; ok {
configuration.RequestBody = map[string]*wafv2.RequestBodyAssociatedResourceTypeConfig{
t.(string): {
DefaultSizeInspectionLimit: aws.String(v.(string)),
},
}
}
}
}

return configuration
}

func expandBlockAction(l []interface{}) *wafv2.BlockAction {
action := &wafv2.BlockAction{}

Expand Down Expand Up @@ -1381,6 +1411,26 @@ func flattenAllow(a *wafv2.AllowAction) []interface{} {
return []interface{}{m}
}

func flattenAssociationConfig(config *wafv2.AssociationConfig) []interface{} {
if config == nil {
return []interface{}{}
}
if config.RequestBody == nil {
return []interface{}{}
}

m := map[string]interface{}{}
for k, v := range config.RequestBody {
body := map[string]interface{}{
"key": aws.StringValue(&k),
"default_size_inspection_limit": aws.StringValue(v.DefaultSizeInspectionLimit),
}
m["request_body"] = []interface{}{body}
}

return []interface{}{m}
}

func flattenBlock(a *wafv2.BlockAction) []interface{} {
if a == nil {
return []interface{}{}
Expand Down
18 changes: 18 additions & 0 deletions internal/service/wafv2/rule_group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2126,6 +2126,24 @@ func testAccPreCheckScopeRegional(ctx context.Context, t *testing.T) {
}
}

func testAccPreCheckScopeCloudfront(ctx context.Context, t *testing.T) {
conn := acctest.Provider.Meta().(*conns.AWSClient).WAFV2Conn(ctx)

input := &wafv2.ListRuleGroupsInput{
Scope: aws.String(wafv2.ScopeCloudfront),
}

_, err := conn.ListRuleGroupsWithContext(ctx, input)

if acctest.PreCheckSkipError(err) {
t.Skipf("skipping acceptance testing: %s", err)
}

if err != nil {
t.Fatalf("unexpected PreCheck error: %s", err)
}
}

func testAccCheckRuleGroupDestroy(ctx context.Context) resource.TestCheckFunc {
return func(s *terraform.State) error {
for _, rs := range s.RootModule().Resources {
Expand Down
34 changes: 34 additions & 0 deletions internal/service/wafv2/schemas.go
Original file line number Diff line number Diff line change
Expand Up @@ -1235,3 +1235,37 @@ func managedRuleGroupConfigATPResponseInspectionSchema() *schema.Schema {
},
}
}

func associationConfigSchema() *schema.Schema {
return &schema.Schema{
Type: schema.TypeList,
MaxItems: 1,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"request_body": {
Type: schema.TypeList,
MaxItems: 1,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"key": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.All(
validation.StringLenBetween(1, 128),
validation.StringMatch(regexp.MustCompile(`^[\w\-]+$`), "must contain only alphanumeric, hyphen, and underscore characters"),
),
},
"default_size_inspection_limit": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice(wafv2.SizeInspectionLimit_Values(), false),
},
},
},
},
},
},
}
}
12 changes: 12 additions & 0 deletions internal/service/wafv2/web_acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ func ResourceWebACL() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"association_config": associationConfigSchema(),
"capacity": {
Type: schema.TypeInt,
Computed: true,
Expand Down Expand Up @@ -185,6 +186,10 @@ func resourceWebACLCreate(ctx context.Context, d *schema.ResourceData, meta inte
VisibilityConfig: expandVisibilityConfig(d.Get("visibility_config").([]interface{})),
}

if v, ok := d.GetOk("association_config"); ok {
input.AssociationConfig = expandAssociationConfig(v.([]interface{}))
}

if v, ok := d.GetOk("custom_response_body"); ok && v.(*schema.Set).Len() > 0 {
input.CustomResponseBodies = expandCustomResponseBodies(v.(*schema.Set).List())
}
Expand Down Expand Up @@ -230,6 +235,9 @@ func resourceWebACLRead(ctx context.Context, d *schema.ResourceData, meta interf
webACL := output.WebACL
arn := aws.StringValue(webACL.ARN)
d.Set("arn", arn)
if err := d.Set("association_config", flattenAssociationConfig(webACL.AssociationConfig)); err != nil {
return diag.Errorf("setting association_config: %s", err)
}
d.Set("capacity", webACL.Capacity)
if err := d.Set("captcha_config", flattenCaptchaConfig(webACL.CaptchaConfig)); err != nil {
return diag.Errorf("setting captcha_config: %s", err)
Expand Down Expand Up @@ -270,6 +278,10 @@ func resourceWebACLUpdate(ctx context.Context, d *schema.ResourceData, meta inte
VisibilityConfig: expandVisibilityConfig(d.Get("visibility_config").([]interface{})),
}

if v, ok := d.GetOk("association_config"); ok && v.(*schema.Set).Len() > 0 {
input.AssociationConfig = expandAssociationConfig(v.(*schema.Set).List())
}

if v, ok := d.GetOk("custom_response_body"); ok && v.(*schema.Set).Len() > 0 {
input.CustomResponseBodies = expandCustomResponseBodies(v.(*schema.Set).List())
}
Expand Down
74 changes: 74 additions & 0 deletions internal/service/wafv2/web_acl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudfront"
"github.com/aws/aws-sdk-go/service/wafv2"
sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
Expand Down Expand Up @@ -2388,6 +2389,79 @@ func TestAccWAFV2WebACL_tokenDomains(t *testing.T) {
})
}

func TestAccWAFV2WebACL_AssociationConfig(t *testing.T) {
ctx := acctest.Context(t)
var v wafv2.WebACL
webACLName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_wafv2_web_acl.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() {
acctest.PreCheck(ctx, t)
acctest.PreCheckRegion(t, "us-east-1")
acctest.PreCheckPartitionHasService(t, wafv2.EndpointsID)
acctest.PreCheckPartitionHasService(t, cloudfront.EndpointsID)
testAccPreCheckScopeCloudfront(ctx, t)
},
ErrorCheck: acctest.ErrorCheck(t, wafv2.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckWebACLDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccWebACLConfig_associationConfig(webACLName),
Check: resource.ComposeTestCheckFunc(
testAccCheckWebACLExists(ctx, resourceName, &v),
acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`global/webacl/.+$`)),
resource.TestCheckResourceAttr(resourceName, "name", webACLName),
resource.TestCheckResourceAttr(resourceName, "scope", "CLOUDFRONT"),
resource.TestCheckResourceAttr(resourceName, "description", webACLName),
resource.TestCheckResourceAttr(resourceName, "rule.#", "0"),
resource.TestCheckResourceAttr(resourceName, "default_action.#", "1"),
resource.TestCheckResourceAttr(resourceName, "default_action.0.allow.#", "1"),
resource.TestCheckResourceAttr(resourceName, "default_action.0.block.#", "0"),
resource.TestCheckResourceAttr(resourceName, "association_config.#", "1"),
resource.TestCheckResourceAttr(resourceName, "association_config.0.request_body.#", "1"),
resource.TestCheckResourceAttr(resourceName, "association_config.0.request_body.0.key", "CLOUDFRONT"),
resource.TestCheckResourceAttr(resourceName, "association_config.0.request_body.0.default_size_inspection_limit", "KB_32"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateIdFunc: testAccWebACLImportStateIdFunc(resourceName),
},
},
})
}

func testAccWebACLConfig_associationConfig(name string) string {
return fmt.Sprintf(`
resource "aws_wafv2_web_acl" "test" {
name = %[1]q
description = %[1]q
scope = "CLOUDFRONT"
default_action {
allow {}
}
visibility_config {
cloudwatch_metrics_enabled = false
metric_name = "friendly-metric-name"
sampled_requests_enabled = false
}
association_config {
request_body {
key = "CLOUDFRONT"
default_size_inspection_limit = "KB_32"
}
}
}
`, name)
}

func testAccCheckWebACLDestroy(ctx context.Context) resource.TestCheckFunc {
return func(s *terraform.State) error {
for _, rs := range s.RootModule().Resources {
Expand Down
14 changes: 14 additions & 0 deletions website/docs/r/wafv2_web_acl.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ resource "aws_wafv2_web_acl" "test" {

The following arguments are supported:

* `association_config` - (Optional) Specifies custom configurations for the associations between the web ACL and protected resources. See [`association_config`](#association_config) below for details.
* `custom_response_body` - (Optional) Defines custom response bodies that can be referenced by `custom_response` actions. See [`custom_response_body`](#custom_response_body) below for details.
* `default_action` - (Required) Action to perform if none of the `rules` contained in the WebACL match. See [`default_ action`](#default_action) below for details.
* `description` - (Optional) Friendly description of the WebACL.
Expand All @@ -359,6 +360,12 @@ The following arguments are supported:
* `token_domains` - (Optional) Specifies the domains that AWS WAF should accept in a web request token. This enables the use of tokens across multiple protected websites. When AWS WAF provides a token, it uses the domain of the AWS resource that the web ACL is protecting. If you don't specify a list of token domains, AWS WAF accepts tokens only for the domain of the protected resource. With a token domain list, AWS WAF accepts the resource's host domain plus all domains in the token domain list, including their prefixed subdomains.
* `visibility_config` - (Required) Defines and enables Amazon CloudWatch metrics and web request sample collection. See [`visibility_config`](#visibility_config) below for details.

### `association_config`

Each `association_config` block supports the following arguments:

* `request_body` - (Optional) Customizes the maximum size of the request body that your protected CloudFront distributions forward to AWS WAF for inspection. See [`request_body`](#request_body) below for details.

### `custom_response_body`

Each `custom_response_body` block supports the following arguments:
Expand Down Expand Up @@ -588,6 +595,13 @@ The `rate_based_statement` block supports the following arguments:
* `limit` - (Required) Limit on requests per 5-minute period for a single originating IP address.
* `scope_down_statement` - (Optional) Optional nested statement that narrows the scope of the rate-based statement to matching web requests. This can be any nestable statement, and you can nest statements at any level below this scope-down statement. See [`statement`](#statement) above for details.

#### `request_body`

The `request_body` block supports the following arguments:

* `key` - (Required) Unique key identifying the associated resource type.
* `default_size_inspection_limit` - (Required) Specifies the maximum size of the web request body component that an associated CloudFront distribution should send to AWS WAF for inspection.

#### `regex_match_statement`

A rule statement used to search web request components for a match against a single regular expression.
Expand Down

0 comments on commit 4162652

Please sign in to comment.