From 41385d6abe93748101400b57033cb288851f59c7 Mon Sep 17 00:00:00 2001 From: TsvetanMilanov Date: Tue, 30 May 2023 15:21:40 +0300 Subject: [PATCH 01/11] r/aws_wafv2_web_acl add support for association_config --- .changelog/31668.txt | 3 + internal/service/wafv2/flex.go | 68 +++++++++++++++ internal/service/wafv2/schemas.go | 37 ++++++++ internal/service/wafv2/web_acl.go | 36 ++++---- internal/service/wafv2/web_acl_test.go | 98 ++++++++++++++++++++++ website/docs/r/wafv2_web_acl.html.markdown | 19 +++++ 6 files changed, 246 insertions(+), 15 deletions(-) create mode 100644 .changelog/31668.txt diff --git a/.changelog/31668.txt b/.changelog/31668.txt new file mode 100644 index 00000000000..75a3c63729f --- /dev/null +++ b/.changelog/31668.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_wafv2_web_acl: Add association_config argument +``` diff --git a/internal/service/wafv2/flex.go b/internal/service/wafv2/flex.go index d9458fe3eb6..11be306cc1a 100644 --- a/internal/service/wafv2/flex.go +++ b/internal/service/wafv2/flex.go @@ -71,6 +71,51 @@ func expandCaptchaConfig(l []interface{}) *wafv2.CaptchaConfig { return configuration } +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 len(m) > 0 { + configuration.RequestBody = make(map[string]*wafv2.RequestBodyAssociatedResourceTypeConfig) + } + + if v, ok := m["cloudfront"]; ok { + inner = v.([]interface{}) + configuration.RequestBody[wafv2.AssociatedResourceTypeCloudfront] = expandRequestBodyConfigItem(inner) + } + } + + return configuration +} + +func expandRequestBodyConfigItem(l []interface{}) *wafv2.RequestBodyAssociatedResourceTypeConfig { + configuration := &wafv2.RequestBodyAssociatedResourceTypeConfig{} + + if len(l) == 0 || l[0] == nil { + return configuration + } + + m := l[0].(map[string]interface{}) + if v, ok := m["default_size_inspection_limit"]; ok { + if v != "" { + configuration.DefaultSizeInspectionLimit = aws.String(v.(string)) + } + } + + return configuration +} + func expandRuleLabels(l []interface{}) []*wafv2.Label { if len(l) == 0 || l[0] == nil { return nil @@ -1423,6 +1468,29 @@ func flattenCaptchaConfig(config *wafv2.CaptchaConfig) interface{} { return []interface{}{m} } +func flattenAssociationConfig(config *wafv2.AssociationConfig) interface{} { + associationConfig := []interface{}{} + if config == nil { + return associationConfig + } + if config.RequestBody == nil { + return associationConfig + } + + cloudfrontRequestBodyConfig := config.RequestBody[wafv2.AssociatedResourceTypeCloudfront] + if cloudfrontRequestBodyConfig != nil { + associationConfig = append(associationConfig, map[string]interface{}{ + "request_body": []map[string]interface{}{{ + "cloudfront": []map[string]interface{}{{ + "default_size_inspection_limit": aws.StringValue(cloudfrontRequestBodyConfig.DefaultSizeInspectionLimit), + }}, + }}, + }) + } + + return associationConfig +} + func flattenChallenge(a *wafv2.ChallengeAction) []interface{} { if a == nil { return []interface{}{} diff --git a/internal/service/wafv2/schemas.go b/internal/service/wafv2/schemas.go index bc1e68ece81..2785cb4ee9a 100644 --- a/internal/service/wafv2/schemas.go +++ b/internal/service/wafv2/schemas.go @@ -513,6 +513,43 @@ func visibilityConfigSchema() *schema.Schema { } } +func associationConfigSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "request_body": requestBodySchema(), + }, + }, + } +} + +func requestBodySchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cloudfront": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "default_size_inspection_limit": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(wafv2.SizeInspectionLimit_Values(), false), + }, + }, + }, + }, + }, + }, + } +} + func allowConfigSchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeList, diff --git a/internal/service/wafv2/web_acl.go b/internal/service/wafv2/web_acl.go index b3106d347a4..9acbfe0d8a1 100644 --- a/internal/service/wafv2/web_acl.go +++ b/internal/service/wafv2/web_acl.go @@ -60,6 +60,7 @@ func ResourceWebACL() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "association_config": associationConfigSchema(), "capacity": { Type: schema.TypeInt, Computed: true, @@ -173,13 +174,14 @@ func resourceWebACLCreate(ctx context.Context, d *schema.ResourceData, meta inte name := d.Get("name").(string) input := &wafv2.CreateWebACLInput{ - CaptchaConfig: expandCaptchaConfig(d.Get("captcha_config").([]interface{})), - DefaultAction: expandDefaultAction(d.Get("default_action").([]interface{})), - Name: aws.String(name), - Rules: expandWebACLRules(d.Get("rule").(*schema.Set).List()), - Scope: aws.String(d.Get("scope").(string)), - Tags: getTagsIn(ctx), - VisibilityConfig: expandVisibilityConfig(d.Get("visibility_config").([]interface{})), + AssociationConfig: expandAssociationConfig(d.Get("association_config").([]interface{})), + CaptchaConfig: expandCaptchaConfig(d.Get("captcha_config").([]interface{})), + DefaultAction: expandDefaultAction(d.Get("default_action").([]interface{})), + Name: aws.String(name), + Rules: expandWebACLRules(d.Get("rule").(*schema.Set).List()), + Scope: aws.String(d.Get("scope").(string)), + Tags: getTagsIn(ctx), + VisibilityConfig: expandVisibilityConfig(d.Get("visibility_config").([]interface{})), } if v, ok := d.GetOk("custom_response_body"); ok && v.(*schema.Set).Len() > 0 { @@ -228,6 +230,9 @@ func resourceWebACLRead(ctx context.Context, d *schema.ResourceData, meta interf arn := aws.StringValue(webACL.ARN) d.Set("arn", arn) d.Set("capacity", webACL.Capacity) + if err := d.Set("association_config", flattenAssociationConfig(webACL.AssociationConfig)); err != nil { + return diag.Errorf("setting association_config: %s", err) + } if err := d.Set("captcha_config", flattenCaptchaConfig(webACL.CaptchaConfig)); err != nil { return diag.Errorf("setting captcha_config: %s", err) } @@ -257,14 +262,15 @@ func resourceWebACLUpdate(ctx context.Context, d *schema.ResourceData, meta inte if d.HasChangesExcept("tags", "tags_all") { input := &wafv2.UpdateWebACLInput{ - CaptchaConfig: expandCaptchaConfig(d.Get("captcha_config").([]interface{})), - DefaultAction: expandDefaultAction(d.Get("default_action").([]interface{})), - Id: aws.String(d.Id()), - LockToken: aws.String(d.Get("lock_token").(string)), - Name: aws.String(d.Get("name").(string)), - Rules: expandWebACLRules(d.Get("rule").(*schema.Set).List()), - Scope: aws.String(d.Get("scope").(string)), - VisibilityConfig: expandVisibilityConfig(d.Get("visibility_config").([]interface{})), + AssociationConfig: expandAssociationConfig(d.Get("association_config").([]interface{})), + CaptchaConfig: expandCaptchaConfig(d.Get("captcha_config").([]interface{})), + DefaultAction: expandDefaultAction(d.Get("default_action").([]interface{})), + Id: aws.String(d.Id()), + LockToken: aws.String(d.Get("lock_token").(string)), + Name: aws.String(d.Get("name").(string)), + Rules: expandWebACLRules(d.Get("rule").(*schema.Set).List()), + Scope: aws.String(d.Get("scope").(string)), + VisibilityConfig: expandVisibilityConfig(d.Get("visibility_config").([]interface{})), } if v, ok := d.GetOk("custom_response_body"); ok && v.(*schema.Set).Len() > 0 { diff --git a/internal/service/wafv2/web_acl_test.go b/internal/service/wafv2/web_acl_test.go index dca56866877..81f025a04a6 100644 --- a/internal/service/wafv2/web_acl_test.go +++ b/internal/service/wafv2/web_acl_test.go @@ -3,17 +3,20 @@ package wafv2_test import ( "context" "fmt" + "os" "regexp" "testing" "time" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/endpoints" "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" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/envvar" tfwafv2 "github.com/hashicorp/terraform-provider-aws/internal/service/wafv2" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) @@ -45,6 +48,7 @@ func TestAccWAFV2WebACL_basic(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( testAccCheckWebACLExists(ctx, resourceName, &v), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), + resource.TestCheckResourceAttr(resourceName, "association_config.#", "0"), resource.TestCheckResourceAttr(resourceName, "captcha_config.#", "0"), resource.TestCheckResourceAttr(resourceName, "default_action.#", "1"), resource.TestCheckResourceAttr(resourceName, "default_action.0.allow.#", "1"), @@ -2385,6 +2389,54 @@ func TestAccWAFV2WebACL_tokenDomains(t *testing.T) { }) } +func TestAccWAFV2WebACL_associationConfig(t *testing.T) { + // All wafv2 requests with scope CLOUDFRONT should be sent to the us-east-1 api. + originalDefaultRegionEnvVarValue := os.Getenv(envvar.DefaultRegion) + defer func() { os.Setenv(envvar.DefaultRegion, originalDefaultRegionEnvVarValue) }() + os.Setenv(envvar.DefaultRegion, endpoints.UsEast1RegionID) //nolint:tenv // Can't use t.Setenv() because we are running the tests in parallel. + + 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); testAccPreCheckScopeCloudFront(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, wafv2.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckWebACLDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccWebACLConfig_associationConfig(webACLName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckWebACLExists(ctx, resourceName, &v), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`global/webacl/.+$`)), + resource.TestCheckResourceAttr(resourceName, "association_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "association_config.0.request_body.#", "1"), + resource.TestCheckResourceAttr(resourceName, "association_config.0.request_body.0.cloudfront.#", "1"), + resource.TestCheckResourceAttr(resourceName, "association_config.0.request_body.0.cloudfront.0.default_size_inspection_limit", "KB_64"), + 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, "description", webACLName), + resource.TestCheckResourceAttr(resourceName, "name", webACLName), + resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeCloudfront), + 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"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccWebACLImportStateIdFunc(resourceName), + }, + }, + }) +} + func testAccCheckWebACLDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { for _, rs := range s.RootModule().Resources { @@ -4650,3 +4702,49 @@ resource "aws_wafv2_web_acl" "test" { } `, name, domain1, domain2) } + +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 {} + } + + association_config { + request_body { + cloudfront { + default_size_inspection_limit = "KB_64" + } + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-metric-name" + sampled_requests_enabled = false + } +} +`, name) +} + +func testAccPreCheckScopeCloudFront(ctx context.Context, t *testing.T) { + conn := acctest.Provider.Meta().(*conns.AWSClient).WAFV2Conn(ctx) + + input := &wafv2.ListWebACLsInput{ + Scope: aws.String(wafv2.ScopeCloudfront), + } + + _, err := conn.ListWebACLsWithContext(ctx, input) + + if acctest.PreCheckSkipError(err) { + t.Skipf("skipping acceptance testing: %s", err) + } + + if err != nil { + t.Fatalf("unexpected PreCheck error: %s", err) + } +} diff --git a/website/docs/r/wafv2_web_acl.html.markdown b/website/docs/r/wafv2_web_acl.html.markdown index 0e6c4892d2c..96fe5e2c9bb 100644 --- a/website/docs/r/wafv2_web_acl.html.markdown +++ b/website/docs/r/wafv2_web_acl.html.markdown @@ -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. @@ -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` + +The `association_config` block supports the following arguments: + +* `request_body` - (Optional) Customizes the request body that your protected resource 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: @@ -853,6 +860,18 @@ The `immunity_time_property` block supports the following arguments: * `immunity_time` - (Optional) The amount of time, in seconds, that a CAPTCHA or challenge timestamp is considered valid by AWS WAF. The default setting is 300. +### `request_body` + +The `request_body` block supports the following arguments: + +* `cloudfront` - (Optional) Customizes the request body that your protected CloudFront distributions forward to AWS WAF for inspection. See [`cloudfront`](#cloudfront) below for details. + +### `cloudfront` + +The `cloudfront` block supports the following arguments: + +* `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. This applies to statements in the web ACL that inspect the body or JSON body. Valid values are `KB_16`, `KB_32`, `KB_48` and `KB_64`. + ## Attributes Reference In addition to all arguments above, the following attributes are exported: From 4162652f26c6a92d743575302b431a97e3cec793 Mon Sep 17 00:00:00 2001 From: Sergio Charpinel Date: Tue, 4 Jul 2023 13:24:19 -0300 Subject: [PATCH 02/11] add association_config attribute to aws_wafv2_web_acl resource --- internal/service/wafv2/flex.go | 50 +++++++++++++++ internal/service/wafv2/rule_group_test.go | 18 ++++++ internal/service/wafv2/schemas.go | 34 ++++++++++ internal/service/wafv2/web_acl.go | 12 ++++ internal/service/wafv2/web_acl_test.go | 74 ++++++++++++++++++++++ website/docs/r/wafv2_web_acl.html.markdown | 14 ++++ 6 files changed, 202 insertions(+) diff --git a/internal/service/wafv2/flex.go b/internal/service/wafv2/flex.go index c27fbd4d704..d374ccf6fe6 100644 --- a/internal/service/wafv2/flex.go +++ b/internal/service/wafv2/flex.go @@ -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{} @@ -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{}{} diff --git a/internal/service/wafv2/rule_group_test.go b/internal/service/wafv2/rule_group_test.go index 20a081553e0..bc6dc0d4bfb 100644 --- a/internal/service/wafv2/rule_group_test.go +++ b/internal/service/wafv2/rule_group_test.go @@ -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 { diff --git a/internal/service/wafv2/schemas.go b/internal/service/wafv2/schemas.go index 4b6771d2f4b..5b18ed6640b 100644 --- a/internal/service/wafv2/schemas.go +++ b/internal/service/wafv2/schemas.go @@ -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), + }, + }, + }, + }, + }, + }, + } +} diff --git a/internal/service/wafv2/web_acl.go b/internal/service/wafv2/web_acl.go index 2cd463fcfb3..1e48dceca8b 100644 --- a/internal/service/wafv2/web_acl.go +++ b/internal/service/wafv2/web_acl.go @@ -63,6 +63,7 @@ func ResourceWebACL() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "association_config": associationConfigSchema(), "capacity": { Type: schema.TypeInt, Computed: true, @@ -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()) } @@ -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) @@ -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()) } diff --git a/internal/service/wafv2/web_acl_test.go b/internal/service/wafv2/web_acl_test.go index 8afd137c4d8..8f581632759 100644 --- a/internal/service/wafv2/web_acl_test.go +++ b/internal/service/wafv2/web_acl_test.go @@ -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" @@ -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 { diff --git a/website/docs/r/wafv2_web_acl.html.markdown b/website/docs/r/wafv2_web_acl.html.markdown index 0e6c4892d2c..e2228e93e5a 100644 --- a/website/docs/r/wafv2_web_acl.html.markdown +++ b/website/docs/r/wafv2_web_acl.html.markdown @@ -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. @@ -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: @@ -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. From 9eab6578de220d0a345ccbc869083577e0c0e2d3 Mon Sep 17 00:00:00 2001 From: Sergio Charpinel Date: Tue, 4 Jul 2023 13:51:40 -0300 Subject: [PATCH 03/11] add changelog file --- .changelog/32367.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/32367.txt diff --git a/.changelog/32367.txt b/.changelog/32367.txt new file mode 100644 index 00000000000..54f854a2330 --- /dev/null +++ b/.changelog/32367.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_wafv2_web_acl: Add `association_config` configuration block +``` \ No newline at end of file From 6065283efb59807f3fb5d120b7a48c8c51232e1e Mon Sep 17 00:00:00 2001 From: Sergio Charpinel Date: Tue, 4 Jul 2023 14:18:15 -0300 Subject: [PATCH 04/11] add AssociationConfig update test --- internal/service/wafv2/web_acl.go | 4 +- internal/service/wafv2/web_acl_test.go | 121 ++++++++++++++++++++----- 2 files changed, 99 insertions(+), 26 deletions(-) diff --git a/internal/service/wafv2/web_acl.go b/internal/service/wafv2/web_acl.go index 1e48dceca8b..a3f1050394e 100644 --- a/internal/service/wafv2/web_acl.go +++ b/internal/service/wafv2/web_acl.go @@ -278,8 +278,8 @@ 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("association_config"); ok { + input.AssociationConfig = expandAssociationConfig(v.([]interface{})) } if v, ok := d.GetOk("custom_response_body"); ok && v.(*schema.Set).Len() > 0 { diff --git a/internal/service/wafv2/web_acl_test.go b/internal/service/wafv2/web_acl_test.go index 8f581632759..08ac4fc391c 100644 --- a/internal/service/wafv2/web_acl_test.go +++ b/internal/service/wafv2/web_acl_test.go @@ -2435,31 +2435,57 @@ func TestAccWAFV2WebACL_AssociationConfig(t *testing.T) { }) } -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 - } +func TestAccWAFV2WebACL_AssociationConfig_Update(t *testing.T) { + ctx := acctest.Context(t) + var v wafv2.WebACL + webACLName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_wafv2_web_acl.test" - association_config { - request_body { - key = "CLOUDFRONT" - default_size_inspection_limit = "KB_32" - } - } -} -`, name) + 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_basicCloudFront(webACLName), + Check: resource.ComposeAggregateTestCheckFunc( + 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, "association_config.#", "0"), + ), + }, + { + 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, "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 testAccCheckWebACLDestroy(ctx context.Context) resource.TestCheckFunc { @@ -2513,6 +2539,33 @@ func testAccCheckWebACLExists(ctx context.Context, n string, v *wafv2.WebACL) re } } +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 testAccWebACLConfig_basic(name string) string { return fmt.Sprintf(` resource "aws_wafv2_web_acl" "test" { @@ -2533,6 +2586,26 @@ resource "aws_wafv2_web_acl" "test" { `, name) } +func testAccWebACLConfig_basicCloudFront(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 + } +} +`, name) +} + func testAccWebACLConfig_basicRule(name string) string { return fmt.Sprintf(` resource "aws_wafv2_web_acl" "test" { From 26ac97a76963ce218bb044a16c6e80ff3194b2b5 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 20 Jul 2023 12:24:12 -0400 Subject: [PATCH 05/11] Revert "add AssociationConfig update test" This reverts commit 6065283efb59807f3fb5d120b7a48c8c51232e1e. --- internal/service/wafv2/web_acl.go | 4 +- internal/service/wafv2/web_acl_test.go | 121 +++++-------------------- 2 files changed, 26 insertions(+), 99 deletions(-) diff --git a/internal/service/wafv2/web_acl.go b/internal/service/wafv2/web_acl.go index a3f1050394e..1e48dceca8b 100644 --- a/internal/service/wafv2/web_acl.go +++ b/internal/service/wafv2/web_acl.go @@ -278,8 +278,8 @@ 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 { - input.AssociationConfig = expandAssociationConfig(v.([]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 { diff --git a/internal/service/wafv2/web_acl_test.go b/internal/service/wafv2/web_acl_test.go index 08ac4fc391c..8f581632759 100644 --- a/internal/service/wafv2/web_acl_test.go +++ b/internal/service/wafv2/web_acl_test.go @@ -2435,57 +2435,31 @@ func TestAccWAFV2WebACL_AssociationConfig(t *testing.T) { }) } -func TestAccWAFV2WebACL_AssociationConfig_Update(t *testing.T) { - ctx := acctest.Context(t) - var v wafv2.WebACL - webACLName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_wafv2_web_acl.test" +func testAccWebACLConfig_associationConfig(name string) string { + return fmt.Sprintf(` +resource "aws_wafv2_web_acl" "test" { + name = %[1]q + description = %[1]q + scope = "CLOUDFRONT" - 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_basicCloudFront(webACLName), - Check: resource.ComposeAggregateTestCheckFunc( - 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, "association_config.#", "0"), - ), - }, - { - 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, "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), - }, - }, - }) + 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 { @@ -2539,33 +2513,6 @@ func testAccCheckWebACLExists(ctx context.Context, n string, v *wafv2.WebACL) re } } -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 testAccWebACLConfig_basic(name string) string { return fmt.Sprintf(` resource "aws_wafv2_web_acl" "test" { @@ -2586,26 +2533,6 @@ resource "aws_wafv2_web_acl" "test" { `, name) } -func testAccWebACLConfig_basicCloudFront(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 - } -} -`, name) -} - func testAccWebACLConfig_basicRule(name string) string { return fmt.Sprintf(` resource "aws_wafv2_web_acl" "test" { From deabecb855f24d405e5fb106dac5fd2e33116bb9 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 20 Jul 2023 12:24:24 -0400 Subject: [PATCH 06/11] Revert "add changelog file" This reverts commit 9eab6578de220d0a345ccbc869083577e0c0e2d3. --- .changelog/32367.txt | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 .changelog/32367.txt diff --git a/.changelog/32367.txt b/.changelog/32367.txt deleted file mode 100644 index 54f854a2330..00000000000 --- a/.changelog/32367.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:enhancement -resource/aws_wafv2_web_acl: Add `association_config` configuration block -``` \ No newline at end of file From 0293c4398cc27c591600e129634d0bb18f157d0f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 20 Jul 2023 12:24:30 -0400 Subject: [PATCH 07/11] Revert "add association_config attribute to aws_wafv2_web_acl resource" This reverts commit 4162652f26c6a92d743575302b431a97e3cec793. --- internal/service/wafv2/flex.go | 50 --------------- internal/service/wafv2/rule_group_test.go | 18 ------ internal/service/wafv2/schemas.go | 34 ---------- internal/service/wafv2/web_acl.go | 12 ---- internal/service/wafv2/web_acl_test.go | 74 ---------------------- website/docs/r/wafv2_web_acl.html.markdown | 14 ---- 6 files changed, 202 deletions(-) diff --git a/internal/service/wafv2/flex.go b/internal/service/wafv2/flex.go index d374ccf6fe6..c27fbd4d704 100644 --- a/internal/service/wafv2/flex.go +++ b/internal/service/wafv2/flex.go @@ -144,36 +144,6 @@ 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{} @@ -1411,26 +1381,6 @@ 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{}{} diff --git a/internal/service/wafv2/rule_group_test.go b/internal/service/wafv2/rule_group_test.go index bc6dc0d4bfb..20a081553e0 100644 --- a/internal/service/wafv2/rule_group_test.go +++ b/internal/service/wafv2/rule_group_test.go @@ -2126,24 +2126,6 @@ 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 { diff --git a/internal/service/wafv2/schemas.go b/internal/service/wafv2/schemas.go index 5b18ed6640b..4b6771d2f4b 100644 --- a/internal/service/wafv2/schemas.go +++ b/internal/service/wafv2/schemas.go @@ -1235,37 +1235,3 @@ 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), - }, - }, - }, - }, - }, - }, - } -} diff --git a/internal/service/wafv2/web_acl.go b/internal/service/wafv2/web_acl.go index 1e48dceca8b..2cd463fcfb3 100644 --- a/internal/service/wafv2/web_acl.go +++ b/internal/service/wafv2/web_acl.go @@ -63,7 +63,6 @@ func ResourceWebACL() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "association_config": associationConfigSchema(), "capacity": { Type: schema.TypeInt, Computed: true, @@ -186,10 +185,6 @@ 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()) } @@ -235,9 +230,6 @@ 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) @@ -278,10 +270,6 @@ 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()) } diff --git a/internal/service/wafv2/web_acl_test.go b/internal/service/wafv2/web_acl_test.go index 8f581632759..8afd137c4d8 100644 --- a/internal/service/wafv2/web_acl_test.go +++ b/internal/service/wafv2/web_acl_test.go @@ -11,7 +11,6 @@ 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" @@ -2389,79 +2388,6 @@ 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 { diff --git a/website/docs/r/wafv2_web_acl.html.markdown b/website/docs/r/wafv2_web_acl.html.markdown index e2228e93e5a..0e6c4892d2c 100644 --- a/website/docs/r/wafv2_web_acl.html.markdown +++ b/website/docs/r/wafv2_web_acl.html.markdown @@ -349,7 +349,6 @@ 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. @@ -360,12 +359,6 @@ 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: @@ -595,13 +588,6 @@ 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. From a5791a045b113986a3490efeee97cbad3addb322 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 20 Jul 2023 13:54:29 -0400 Subject: [PATCH 08/11] Remove 'testAccCheckWebACLDestroyWithProvider' and 'testAccCheckWebACLExistsWithProvider'. --- internal/service/wafv2/web_acl_test.go | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/internal/service/wafv2/web_acl_test.go b/internal/service/wafv2/web_acl_test.go index 744b66d9a89..c2ddd7979fe 100644 --- a/internal/service/wafv2/web_acl_test.go +++ b/internal/service/wafv2/web_acl_test.go @@ -13,7 +13,6 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/endpoints" "github.com/aws/aws-sdk-go/service/wafv2" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -2525,13 +2524,7 @@ func testAccPreCheckScopeCloudFront(ctx context.Context, t *testing.T) { func testAccCheckWebACLDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - return testAccCheckWebACLDestroyWithProvider(ctx)(s, acctest.Provider) - } -} - -func testAccCheckWebACLDestroyWithProvider(ctx context.Context) acctest.TestCheckWithProviderFunc { - return func(s *terraform.State, provider *schema.Provider) error { - conn := provider.Meta().(*conns.AWSClient).WAFV2Conn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).WAFV2Conn(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_wafv2_web_acl" { @@ -2556,10 +2549,6 @@ func testAccCheckWebACLDestroyWithProvider(ctx context.Context) acctest.TestChec } func testAccCheckWebACLExists(ctx context.Context, n string, v *wafv2.WebACL) resource.TestCheckFunc { - return testAccCheckWebACLExistsWithProvider(ctx, n, v, func() *schema.Provider { return acctest.Provider }) -} - -func testAccCheckWebACLExistsWithProvider(ctx context.Context, n string, v *wafv2.WebACL, providerF func() *schema.Provider) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -2570,7 +2559,7 @@ func testAccCheckWebACLExistsWithProvider(ctx context.Context, n string, v *wafv return fmt.Errorf("No WAFv2 WebACL ID is set") } - conn := providerF().Meta().(*conns.AWSClient).WAFV2Conn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).WAFV2Conn(ctx) output, err := tfwafv2.FindWebACLByThreePartKey(ctx, conn, rs.Primary.ID, rs.Primary.Attributes["name"], rs.Primary.Attributes["scope"]) From 57e6a5ef0173bbe624435200bed15432780dc949 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 20 Jul 2023 13:55:18 -0400 Subject: [PATCH 09/11] Tweak CHANGELOG entry. --- .changelog/31668.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changelog/31668.txt b/.changelog/31668.txt index 75a3c63729f..43798625187 100644 --- a/.changelog/31668.txt +++ b/.changelog/31668.txt @@ -1,3 +1,3 @@ ```release-note:enhancement -resource/aws_wafv2_web_acl: Add association_config argument +resource/aws_wafv2_web_acl: Add `association_config` argument ``` From 72adcacbda27d253ed1d5631bef8f754183bb345 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 20 Jul 2023 14:06:02 -0400 Subject: [PATCH 10/11] Prevent 'WAFInvalidOperationException: Your request contains fields that belong to a feature you are not allowed to use' errors with nil 'association_config'. --- internal/service/wafv2/flex.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/service/wafv2/flex.go b/internal/service/wafv2/flex.go index 7e15dee8725..ea4f130909d 100644 --- a/internal/service/wafv2/flex.go +++ b/internal/service/wafv2/flex.go @@ -75,12 +75,12 @@ func expandCaptchaConfig(l []interface{}) *wafv2.CaptchaConfig { } func expandAssociationConfig(l []interface{}) *wafv2.AssociationConfig { - configuration := &wafv2.AssociationConfig{} - if len(l) == 0 || l[0] == nil { - return configuration + return nil } + configuration := &wafv2.AssociationConfig{} + m := l[0].(map[string]interface{}) if v, ok := m["request_body"]; ok { inner := v.([]interface{}) From f1645e8867d898542cdf1f7a548709e94585130b Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 20 Jul 2023 14:09:35 -0400 Subject: [PATCH 11/11] Fix anchor links. --- website/docs/r/wafv2_web_acl.html.markdown | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/docs/r/wafv2_web_acl.html.markdown b/website/docs/r/wafv2_web_acl.html.markdown index 0cf877d6e72..877880eacfa 100644 --- a/website/docs/r/wafv2_web_acl.html.markdown +++ b/website/docs/r/wafv2_web_acl.html.markdown @@ -349,7 +349,7 @@ resource "aws_wafv2_web_acl" "test" { This resource supports the following arguments: -* `association_config` - (Optional) Specifies custom configurations for the associations between the web ACL and protected resources. See [`association_config`](#association_config) below for details. +* `association_config` - (Optional) Specifies custom configurations for the associations between the web ACL and protected resources. See [`association_config`](#association_config-block) 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-block) below for details. * `default_action` - (Required) Action to perform if none of the `rules` contained in the WebACL match. See [`default_action`](#default_action-block) below for details. * `description` - (Optional) Friendly description of the WebACL. @@ -364,7 +364,7 @@ This resource supports the following arguments: The `association_config` block supports the following arguments: -* `request_body` - (Optional) Customizes the request body that your protected resource forward to AWS WAF for inspection. See [`request_body`](#request_body) below for details. +* `request_body` - (Optional) Customizes the request body that your protected resource forward to AWS WAF for inspection. See [`request_body`](#request_body-block) below for details. ### `custom_response_body` Block @@ -848,7 +848,7 @@ The `immunity_time_property` block supports the following arguments: The `request_body` block supports the following arguments: -* `cloudfront` - (Optional) Customizes the request body that your protected CloudFront distributions forward to AWS WAF for inspection. See [`cloudfront`](#cloudfront) below for details. +* `cloudfront` - (Optional) Customizes the request body that your protected CloudFront distributions forward to AWS WAF for inspection. See [`cloudfront`](#cloudfront-block) below for details. ### `cloudfront` Block