Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add token_domains to WAFv2 Web ACL #30340

Merged
merged 6 commits into from
Apr 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/30340.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_wafv2_web_acl: Add `token_domains` argument
```
25 changes: 23 additions & 2 deletions internal/service/wafv2/web_acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
"github.com/hashicorp/terraform-provider-aws/internal/flex"
tftags "github.com/hashicorp/terraform-provider-aws/internal/tags"
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
"github.com/hashicorp/terraform-provider-aws/internal/verify"
Expand Down Expand Up @@ -145,8 +146,19 @@ func ResourceWebACL() *schema.Resource {
ForceNew: true,
ValidateFunc: validation.StringInSlice(wafv2.Scope_Values(), false),
},
names.AttrTags: tftags.TagsSchema(),
names.AttrTagsAll: tftags.TagsSchemaComputed(),
names.AttrTags: tftags.TagsSchema(),
names.AttrTagsAll: tftags.TagsSchemaComputed(),
"token_domains": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.All(
validation.StringLenBetween(1, 253),
validation.StringMatch(regexp.MustCompile(`^[\w\.\-/]+$`), "must contain only alphanumeric, hyphen, dot, underscore and forward-slash characters"),
),
},
},
"visibility_config": visibilityConfigSchema(),
},

Expand Down Expand Up @@ -176,6 +188,10 @@ func resourceWebACLCreate(ctx context.Context, d *schema.ResourceData, meta inte
input.Description = aws.String(v.(string))
}

if v, ok := d.GetOk("token_domains"); ok && v.(*schema.Set).Len() > 0 {
input.TokenDomains = flex.ExpandStringSet(v.(*schema.Set))
}

outputRaw, err := tfresource.RetryWhenAWSErrCodeEquals(ctx, webACLCreateTimeout, func() (interface{}, error) {
return conn.CreateWebACLWithContext(ctx, input)
}, wafv2.ErrCodeWAFUnavailableEntityException)
Expand Down Expand Up @@ -226,6 +242,7 @@ func resourceWebACLRead(ctx context.Context, d *schema.ResourceData, meta interf
if err := d.Set("rule", flattenWebACLRules(rules)); err != nil {
return diag.Errorf("setting rule: %s", err)
}
d.Set("token_domains", aws.StringValueSlice(webACL.TokenDomains))
if err := d.Set("visibility_config", flattenVisibilityConfig(webACL.VisibilityConfig)); err != nil {
return diag.Errorf("setting visibility_config: %s", err)
}
Expand Down Expand Up @@ -256,6 +273,10 @@ func resourceWebACLUpdate(ctx context.Context, d *schema.ResourceData, meta inte
input.Description = aws.String(v.(string))
}

if v, ok := d.GetOk("token_domains"); ok {
input.TokenDomains = flex.ExpandStringSet(v.(*schema.Set))
}

_, err := tfresource.RetryWhenAWSErrCodeEquals(ctx, webACLUpdateTimeout, func() (interface{}, error) {
return conn.UpdateWebACLWithContext(ctx, input)
}, wafv2.ErrCodeWAFUnavailableEntityException)
Expand Down
80 changes: 74 additions & 6 deletions internal/service/wafv2/web_acl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,23 @@ func TestAccWAFV2WebACL_basic(t *testing.T) {
Steps: []resource.TestStep{
{
Config: testAccWebACLConfig_basic(webACLName),
Check: resource.ComposeTestCheckFunc(
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckWebACLExists(ctx, resourceName, &v),
acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)),
resource.TestCheckResourceAttr(resourceName, "name", webACLName),
resource.TestCheckResourceAttr(resourceName, "description", webACLName),
resource.TestCheckResourceAttr(resourceName, "rule.#", "0"),
resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeRegional),
resource.TestCheckResourceAttr(resourceName, "captcha_config.#", "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, "description", webACLName),
resource.TestCheckResourceAttr(resourceName, "name", webACLName),
resource.TestCheckResourceAttr(resourceName, "rule.#", "0"),
resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeRegional),
resource.TestCheckResourceAttr(resourceName, "tags.%", "0"),
resource.TestCheckResourceAttr(resourceName, "token_domains.#", "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, "tags.%", "0"),
),
},
{
Expand Down Expand Up @@ -2339,6 +2340,52 @@ func TestAccWAFV2WebACL_Operators_maxNested(t *testing.T) {
})
}

func TestAccWAFV2WebACL_tokenDomains(t *testing.T) {
ctx := acctest.Context(t)
var v wafv2.WebACL
webACLName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
domain1 := "mywebsite.com"
domain2 := "myotherwebsite.com"
resourceName := "aws_wafv2_web_acl.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t); testAccPreCheckScopeRegional(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, wafv2.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckWebACLDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccWebACLConfig_tokenDomains(webACLName, domain1, domain2),
Check: resource.ComposeTestCheckFunc(
testAccCheckWebACLExists(ctx, resourceName, &v),
acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)),
resource.TestCheckResourceAttr(resourceName, "name", webACLName),
resource.TestCheckResourceAttr(resourceName, "description", webACLName),
resource.TestCheckResourceAttr(resourceName, "rule.#", "0"),
resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeRegional),
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, "token_domains.#", "2"),
resource.TestCheckTypeSetElemAttr(resourceName, "token_domains.*", domain1),
resource.TestCheckTypeSetElemAttr(resourceName, "token_domains.*", domain2),
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"),
),
},
{
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 {
Expand Down Expand Up @@ -4567,3 +4614,24 @@ resource "aws_wafv2_web_acl" "test" {
}
`, name)
}

func testAccWebACLConfig_tokenDomains(name, domain1, domain2 string) string {
return fmt.Sprintf(`
resource "aws_wafv2_web_acl" "test" {
name = %[1]q
description = %[1]q
scope = "REGIONAL"

default_action {
allow {}
}

token_domains = [%[2]q, %[3]q]
visibility_config {
cloudwatch_metrics_enabled = false
metric_name = "friendly-metric-name"
sampled_requests_enabled = false
}
}
`, name, domain1, domain2)
}
3 changes: 3 additions & 0 deletions website/docs/r/wafv2_web_acl.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ resource "aws_wafv2_web_acl" "example" {
}
}

token_domains = ["mywebsite.com", "myotherwebsite.com"]

visibility_config {
cloudwatch_metrics_enabled = false
metric_name = "friendly-rule-metric-name"
Expand Down Expand Up @@ -345,6 +347,7 @@ The following arguments are supported:
* `rule` - (Optional) Rule blocks used to identify the web requests that you want to `allow`, `block`, or `count`. See [`rule`](#rule) 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) Map of key-value pairs to associate with the resource. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level.
* `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.

### `custom_response_body`
Expand Down