diff --git a/CHANGELOG.md b/CHANGELOG.md index 0613308f4a41..d40cfb6ecb9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ FEATURES: * **New Data Source:** `aws_wafregional_rate_based_rule` [GH-10125] * **New Resource:** `aws_quicksight_user` [GH-10401] +ENHANCEMENTS: + +* resource/aws_waf_rate_based_rule: Support resource import [GH-10475] +* resource/aws_waf_rule: Add `tags` argument [GH-10408] +* resource/aws_waf_rule_group: Add `tags` argument [GH-10408] +* resource/aws_waf_web_acl: Add `tags` argument [GH-10408] + BUG FIXES: * resource/aws_gamelift_fleet: Increase default deletion timeout to 20 minutes to match service timing [GH-10443] diff --git a/aws/resource_aws_waf_rate_based_rule.go b/aws/resource_aws_waf_rate_based_rule.go index 4f7c7271002e..527878ec5871 100644 --- a/aws/resource_aws_waf_rate_based_rule.go +++ b/aws/resource_aws_waf_rate_based_rule.go @@ -17,6 +17,9 @@ func resourceAwsWafRateBasedRule() *schema.Resource { Read: resourceAwsWafRateBasedRuleRead, Update: resourceAwsWafRateBasedRuleUpdate, Delete: resourceAwsWafRateBasedRuleDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, Schema: map[string]*schema.Schema{ "name": { diff --git a/aws/resource_aws_waf_rate_based_rule_test.go b/aws/resource_aws_waf_rate_based_rule_test.go index a3de398590a0..c48151a790e8 100644 --- a/aws/resource_aws_waf_rate_based_rule_test.go +++ b/aws/resource_aws_waf_rate_based_rule_test.go @@ -17,6 +17,8 @@ import ( func TestAccAWSWafRateBasedRule_basic(t *testing.T) { var v waf.RateBasedRule wafRuleName := fmt.Sprintf("wafrule%s", acctest.RandString(5)) + resourceName := "aws_waf_rate_based_rule.wafrule" + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSWaf(t) }, Providers: testAccProviders, @@ -25,15 +27,17 @@ func TestAccAWSWafRateBasedRule_basic(t *testing.T) { { Config: testAccAWSWafRateBasedRuleConfig(wafRuleName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSWafRateBasedRuleExists("aws_waf_rate_based_rule.wafrule", &v), - resource.TestCheckResourceAttr( - "aws_waf_rate_based_rule.wafrule", "name", wafRuleName), - resource.TestCheckResourceAttr( - "aws_waf_rate_based_rule.wafrule", "predicates.#", "1"), - resource.TestCheckResourceAttr( - "aws_waf_rate_based_rule.wafrule", "metric_name", wafRuleName), + testAccCheckAWSWafRateBasedRuleExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "name", wafRuleName), + resource.TestCheckResourceAttr(resourceName, "predicates.#", "1"), + resource.TestCheckResourceAttr(resourceName, "metric_name", wafRuleName), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } @@ -42,6 +46,7 @@ func TestAccAWSWafRateBasedRule_changeNameForceNew(t *testing.T) { var before, after waf.RateBasedRule wafRuleName := fmt.Sprintf("wafrule%s", acctest.RandString(5)) wafRuleNewName := fmt.Sprintf("wafrulenew%s", acctest.RandString(5)) + resourceName := "aws_waf_rate_based_rule.wafrule" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSWaf(t) }, @@ -51,27 +56,26 @@ func TestAccAWSWafRateBasedRule_changeNameForceNew(t *testing.T) { { Config: testAccAWSWafRateBasedRuleConfig(wafRuleName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSWafRateBasedRuleExists("aws_waf_rate_based_rule.wafrule", &before), - resource.TestCheckResourceAttr( - "aws_waf_rate_based_rule.wafrule", "name", wafRuleName), - resource.TestCheckResourceAttr( - "aws_waf_rate_based_rule.wafrule", "predicates.#", "1"), - resource.TestCheckResourceAttr( - "aws_waf_rate_based_rule.wafrule", "metric_name", wafRuleName), + testAccCheckAWSWafRateBasedRuleExists(resourceName, &before), + resource.TestCheckResourceAttr(resourceName, "name", wafRuleName), + resource.TestCheckResourceAttr(resourceName, "predicates.#", "1"), + resource.TestCheckResourceAttr(resourceName, "metric_name", wafRuleName), ), }, { Config: testAccAWSWafRateBasedRuleConfigChangeName(wafRuleNewName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSWafRateBasedRuleExists("aws_waf_rate_based_rule.wafrule", &after), - resource.TestCheckResourceAttr( - "aws_waf_rate_based_rule.wafrule", "name", wafRuleNewName), - resource.TestCheckResourceAttr( - "aws_waf_rate_based_rule.wafrule", "predicates.#", "1"), - resource.TestCheckResourceAttr( - "aws_waf_rate_based_rule.wafrule", "metric_name", wafRuleNewName), + testAccCheckAWSWafRateBasedRuleExists(resourceName, &after), + resource.TestCheckResourceAttr(resourceName, "name", wafRuleNewName), + resource.TestCheckResourceAttr(resourceName, "predicates.#", "1"), + resource.TestCheckResourceAttr(resourceName, "metric_name", wafRuleNewName), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } @@ -79,6 +83,8 @@ func TestAccAWSWafRateBasedRule_changeNameForceNew(t *testing.T) { func TestAccAWSWafRateBasedRule_disappears(t *testing.T) { var v waf.RateBasedRule wafRuleName := fmt.Sprintf("wafrule%s", acctest.RandString(5)) + resourceName := "aws_waf_rate_based_rule.wafrule" + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSWaf(t) }, Providers: testAccProviders, @@ -87,7 +93,7 @@ func TestAccAWSWafRateBasedRule_disappears(t *testing.T) { { Config: testAccAWSWafRateBasedRuleConfig(wafRuleName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSWafRateBasedRuleExists("aws_waf_rate_based_rule.wafrule", &v), + testAccCheckAWSWafRateBasedRuleExists(resourceName, &v), testAccCheckAWSWafRateBasedRuleDisappears(&v), ), ExpectNonEmptyPlan: true, @@ -103,6 +109,7 @@ func TestAccAWSWafRateBasedRule_changePredicates(t *testing.T) { var before, after waf.RateBasedRule var idx int ruleName := fmt.Sprintf("wafrule%s", acctest.RandString(5)) + resourceName := "aws_waf_rate_based_rule.wafrule" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSWaf(t) }, @@ -113,26 +120,31 @@ func TestAccAWSWafRateBasedRule_changePredicates(t *testing.T) { Config: testAccAWSWafRateBasedRuleConfig(ruleName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSWafIPSetExists("aws_waf_ipset.ipset", &ipset), - testAccCheckAWSWafRateBasedRuleExists("aws_waf_rate_based_rule.wafrule", &before), - resource.TestCheckResourceAttr("aws_waf_rate_based_rule.wafrule", "name", ruleName), - resource.TestCheckResourceAttr("aws_waf_rate_based_rule.wafrule", "predicates.#", "1"), + testAccCheckAWSWafRateBasedRuleExists(resourceName, &before), + resource.TestCheckResourceAttr(resourceName, "name", ruleName), + resource.TestCheckResourceAttr(resourceName, "predicates.#", "1"), computeWafRateBasedRulePredicateWithIpSet(&ipset, false, "IPMatch", &idx), - testCheckResourceAttrWithIndexesAddr("aws_waf_rate_based_rule.wafrule", "predicates.%d.negated", &idx, "false"), - testCheckResourceAttrWithIndexesAddr("aws_waf_rate_based_rule.wafrule", "predicates.%d.type", &idx, "IPMatch"), + testCheckResourceAttrWithIndexesAddr(resourceName, "predicates.%d.negated", &idx, "false"), + testCheckResourceAttrWithIndexesAddr(resourceName, "predicates.%d.type", &idx, "IPMatch"), ), }, { Config: testAccAWSWafRateBasedRuleConfig_changePredicates(ruleName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSWafByteMatchSetExists("aws_waf_byte_match_set.set", &byteMatchSet), - testAccCheckAWSWafRateBasedRuleExists("aws_waf_rate_based_rule.wafrule", &after), - resource.TestCheckResourceAttr("aws_waf_rate_based_rule.wafrule", "name", ruleName), - resource.TestCheckResourceAttr("aws_waf_rate_based_rule.wafrule", "predicates.#", "1"), + testAccCheckAWSWafRateBasedRuleExists(resourceName, &after), + resource.TestCheckResourceAttr(resourceName, "name", ruleName), + resource.TestCheckResourceAttr(resourceName, "predicates.#", "1"), computeWafRateBasedRulePredicateWithByteMatchSet(&byteMatchSet, true, "ByteMatch", &idx), - testCheckResourceAttrWithIndexesAddr("aws_waf_rate_based_rule.wafrule", "predicates.%d.negated", &idx, "true"), - testCheckResourceAttrWithIndexesAddr("aws_waf_rate_based_rule.wafrule", "predicates.%d.type", &idx, "ByteMatch"), + testCheckResourceAttrWithIndexesAddr(resourceName, "predicates.%d.negated", &idx, "true"), + testCheckResourceAttrWithIndexesAddr(resourceName, "predicates.%d.type", &idx, "ByteMatch"), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } @@ -178,6 +190,7 @@ func computeWafRateBasedRulePredicateWithByteMatchSet(set *waf.ByteMatchSet, neg func TestAccAWSWafRateBasedRule_noPredicates(t *testing.T) { var rule waf.RateBasedRule ruleName := fmt.Sprintf("wafrule%s", acctest.RandString(5)) + resourceName := "aws_waf_rate_based_rule.wafrule" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSWaf(t) }, @@ -187,13 +200,16 @@ func TestAccAWSWafRateBasedRule_noPredicates(t *testing.T) { { Config: testAccAWSWafRateBasedRuleConfig_noPredicates(ruleName), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSWafRateBasedRuleExists("aws_waf_rate_based_rule.wafrule", &rule), - resource.TestCheckResourceAttr( - "aws_waf_rate_based_rule.wafrule", "name", ruleName), - resource.TestCheckResourceAttr( - "aws_waf_rate_based_rule.wafrule", "predicates.#", "0"), + testAccCheckAWSWafRateBasedRuleExists(resourceName, &rule), + resource.TestCheckResourceAttr(resourceName, "name", ruleName), + resource.TestCheckResourceAttr(resourceName, "predicates.#", "0"), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } diff --git a/aws/resource_aws_waf_rule.go b/aws/resource_aws_waf_rule.go index 69a8ef626372..a8fe22ef61cd 100644 --- a/aws/resource_aws_waf_rule.go +++ b/aws/resource_aws_waf_rule.go @@ -5,10 +5,12 @@ import ( "log" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/waf" "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 resourceAwsWafRule() *schema.Resource { @@ -55,12 +57,14 @@ func resourceAwsWafRule() *schema.Resource { }, }, }, + "tags": tagsSchema(), }, } } func resourceAwsWafRuleCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).wafconn + tags := keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().WafTags() wr := newWafRetryer(conn) out, err := wr.RetryWithToken(func(token *string) (interface{}, error) { @@ -70,6 +74,10 @@ func resourceAwsWafRuleCreate(d *schema.ResourceData, meta interface{}) error { Name: aws.String(d.Get("name").(string)), } + if len(tags) > 0 { + params.Tags = tags + } + return conn.CreateRule(params) }) if err != nil { @@ -109,6 +117,23 @@ func resourceAwsWafRuleRead(d *schema.ResourceData, meta interface{}) error { predicates = append(predicates, predicate) } + arn := arn.ARN{ + Partition: meta.(*AWSClient).partition, + Service: "waf", + AccountID: meta.(*AWSClient).accountid, + Resource: fmt.Sprintf("rule/%s", d.Id()), + }.String() + + tagList, err := conn.ListTagsForResource(&waf.ListTagsForResourceInput{ + ResourceARN: aws.String(arn), + }) + if err != nil { + return fmt.Errorf("Failed to get WAF Rule parameter tags for %s: %s", d.Get("name"), err) + } + if err := d.Set("tags", keyvaluetags.WafKeyValueTags(tagList.TagInfoForResource.TagList).IgnoreAws().Map()); err != nil { + return fmt.Errorf("error setting tags: %s", err) + } + d.Set("predicates", predicates) d.Set("name", resp.Rule.Name) d.Set("metric_name", resp.Rule.MetricName) @@ -129,6 +154,21 @@ func resourceAwsWafRuleUpdate(d *schema.ResourceData, meta interface{}) error { } } + if d.HasChange("tags") { + o, n := d.GetChange("tags") + + arn := arn.ARN{ + Partition: meta.(*AWSClient).partition, + Service: "waf", + AccountID: meta.(*AWSClient).accountid, + Resource: fmt.Sprintf("rule/%s", d.Id()), + }.String() + + if err := keyvaluetags.WafUpdateTags(conn, arn, o, n); err != nil { + return fmt.Errorf("error updating tags: %s", err) + } + } + return resourceAwsWafRuleRead(d, meta) } diff --git a/aws/resource_aws_waf_rule_group.go b/aws/resource_aws_waf_rule_group.go index 8fdd0406a346..9f15081d86ef 100644 --- a/aws/resource_aws_waf_rule_group.go +++ b/aws/resource_aws_waf_rule_group.go @@ -5,8 +5,10 @@ import ( "log" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/waf" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" ) func resourceAwsWafRuleGroup() *schema.Resource { @@ -65,12 +67,14 @@ func resourceAwsWafRuleGroup() *schema.Resource { }, }, }, + "tags": tagsSchema(), }, } } func resourceAwsWafRuleGroupCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).wafconn + tags := keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().WafTags() wr := newWafRetryer(conn) out, err := wr.RetryWithToken(func(token *string) (interface{}, error) { @@ -80,6 +84,10 @@ func resourceAwsWafRuleGroupCreate(d *schema.ResourceData, meta interface{}) err Name: aws.String(d.Get("name").(string)), } + if len(tags) > 0 { + params.Tags = tags + } + return conn.CreateRuleGroup(params) }) if err != nil { @@ -115,6 +123,23 @@ func resourceAwsWafRuleGroupRead(d *schema.ResourceData, meta interface{}) error return fmt.Errorf("error listing activated rules in WAF Rule Group (%s): %s", d.Id(), err) } + arn := arn.ARN{ + Partition: meta.(*AWSClient).partition, + Service: "waf", + AccountID: meta.(*AWSClient).accountid, + Resource: fmt.Sprintf("rulegroup/%s", d.Id()), + }.String() + + tagList, err := conn.ListTagsForResource(&waf.ListTagsForResourceInput{ + ResourceARN: aws.String(arn), + }) + if err != nil { + return fmt.Errorf("Failed to get WAF Rule Group parameter tags for %s: %s", d.Get("name"), err) + } + if err := d.Set("tags", keyvaluetags.WafKeyValueTags(tagList.TagInfoForResource.TagList).IgnoreAws().Map()); err != nil { + return fmt.Errorf("error setting tags: %s", err) + } + d.Set("activated_rule", flattenWafActivatedRules(rResp.ActivatedRules)) d.Set("name", resp.RuleGroup.Name) d.Set("metric_name", resp.RuleGroup.MetricName) @@ -135,6 +160,21 @@ func resourceAwsWafRuleGroupUpdate(d *schema.ResourceData, meta interface{}) err } } + if d.HasChange("tags") { + o, n := d.GetChange("tags") + + arn := arn.ARN{ + Partition: meta.(*AWSClient).partition, + Service: "waf", + AccountID: meta.(*AWSClient).accountid, + Resource: fmt.Sprintf("rulegroup/%s", d.Id()), + }.String() + + if err := keyvaluetags.WafUpdateTags(conn, arn, o, n); err != nil { + return fmt.Errorf("error updating tags: %s", err) + } + } + return resourceAwsWafRuleGroupRead(d, meta) } diff --git a/aws/resource_aws_waf_rule_group_test.go b/aws/resource_aws_waf_rule_group_test.go index 8b6d00242605..096d6c58e6bd 100644 --- a/aws/resource_aws_waf_rule_group_test.go +++ b/aws/resource_aws_waf_rule_group_test.go @@ -250,6 +250,56 @@ func computeWafActivatedRuleWithRuleId(rule *waf.Rule, actionType string, priori } } +func TestAccAWSWafRuleGroup_Tags(t *testing.T) { + var group waf.RuleGroup + groupName := fmt.Sprintf("test%s", acctest.RandString(5)) + resourceName := "aws_waf_rule_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSWaf(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSWafWebAclDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSWafRuleGroupConfigTags1(groupName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafRuleGroupExists(resourceName, &group), + resource.TestCheckResourceAttr(resourceName, "name", groupName), + resource.TestCheckResourceAttr(resourceName, "activated_rule.#", "0"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + Config: testAccAWSWafRuleGroupConfigTags2(groupName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafRuleGroupExists(resourceName, &group), + resource.TestCheckResourceAttr(resourceName, "name", groupName), + resource.TestCheckResourceAttr(resourceName, "activated_rule.#", "0"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccAWSWafRuleGroupConfigTags1(groupName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafRuleGroupExists(resourceName, &group), + resource.TestCheckResourceAttr(resourceName, "name", groupName), + resource.TestCheckResourceAttr(resourceName, "activated_rule.#", "0"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAWSWafRuleGroup_noActivatedRules(t *testing.T) { var group waf.RuleGroup groupName := fmt.Sprintf("test%s", acctest.RandString(5)) @@ -264,10 +314,8 @@ func TestAccAWSWafRuleGroup_noActivatedRules(t *testing.T) { Config: testAccAWSWafRuleGroupConfig_noActivatedRules(groupName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSWafRuleGroupExists(resourceName, &group), - resource.TestCheckResourceAttr( - resourceName, "name", groupName), - resource.TestCheckResourceAttr( - resourceName, "activated_rule.#", "0"), + resource.TestCheckResourceAttr(resourceName, "name", groupName), + resource.TestCheckResourceAttr(resourceName, "activated_rule.#", "0"), ), }, }, @@ -458,3 +506,30 @@ resource "aws_waf_rule_group" "test" { } `, groupName) } + +func testAccAWSWafRuleGroupConfigTags1(gName, tag1Key, tag1Value string) string { + return fmt.Sprintf(` +resource "aws_waf_rule_group" "test" { + name = "%[1]s" + metric_name = "%[1]s" + + tags = { + %q = %q + } +} +`, gName, tag1Key, tag1Value) +} + +func testAccAWSWafRuleGroupConfigTags2(gName, tag1Key, tag1Value, tag2Key, tag2Value string) string { + return fmt.Sprintf(` +resource "aws_waf_rule_group" "test" { + name = "%[1]s" + metric_name = "%[1]s" + + tags = { + %q = %q + %q = %q + } +} +`, gName, tag1Key, tag1Value, tag2Key, tag2Value) +} diff --git a/aws/resource_aws_waf_rule_test.go b/aws/resource_aws_waf_rule_test.go index f541571bd14c..ea7e6054cbc0 100644 --- a/aws/resource_aws_waf_rule_test.go +++ b/aws/resource_aws_waf_rule_test.go @@ -17,6 +17,7 @@ import ( func TestAccAWSWafRule_basic(t *testing.T) { var v waf.Rule wafRuleName := fmt.Sprintf("wafrule%s", acctest.RandString(5)) + resourceName := "aws_waf_rule.wafrule" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSWaf(t) }, Providers: testAccProviders, @@ -25,17 +26,14 @@ func TestAccAWSWafRule_basic(t *testing.T) { { Config: testAccAWSWafRuleConfig(wafRuleName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSWafRuleExists("aws_waf_rule.wafrule", &v), - resource.TestCheckResourceAttr( - "aws_waf_rule.wafrule", "name", wafRuleName), - resource.TestCheckResourceAttr( - "aws_waf_rule.wafrule", "predicates.#", "1"), - resource.TestCheckResourceAttr( - "aws_waf_rule.wafrule", "metric_name", wafRuleName), + testAccCheckAWSWafRuleExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "name", wafRuleName), + resource.TestCheckResourceAttr(resourceName, "predicates.#", "1"), + resource.TestCheckResourceAttr(resourceName, "metric_name", wafRuleName), ), }, { - ResourceName: "aws_waf_rule.wafrule", + ResourceName: resourceName, ImportState: true, ImportStateVerify: true, }, @@ -47,6 +45,7 @@ func TestAccAWSWafRule_changeNameForceNew(t *testing.T) { var before, after waf.Rule wafRuleName := fmt.Sprintf("wafrule%s", acctest.RandString(5)) wafRuleNewName := fmt.Sprintf("wafrulenew%s", acctest.RandString(5)) + resourceName := "aws_waf_rule.wafrule" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSWaf(t) }, @@ -56,25 +55,19 @@ func TestAccAWSWafRule_changeNameForceNew(t *testing.T) { { Config: testAccAWSWafRuleConfig(wafRuleName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSWafRuleExists("aws_waf_rule.wafrule", &before), - resource.TestCheckResourceAttr( - "aws_waf_rule.wafrule", "name", wafRuleName), - resource.TestCheckResourceAttr( - "aws_waf_rule.wafrule", "predicates.#", "1"), - resource.TestCheckResourceAttr( - "aws_waf_rule.wafrule", "metric_name", wafRuleName), + testAccCheckAWSWafRuleExists(resourceName, &before), + resource.TestCheckResourceAttr(resourceName, "name", wafRuleName), + resource.TestCheckResourceAttr(resourceName, "predicates.#", "1"), + resource.TestCheckResourceAttr(resourceName, "metric_name", wafRuleName), ), }, { Config: testAccAWSWafRuleConfigChangeName(wafRuleNewName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSWafRuleExists("aws_waf_rule.wafrule", &after), - resource.TestCheckResourceAttr( - "aws_waf_rule.wafrule", "name", wafRuleNewName), - resource.TestCheckResourceAttr( - "aws_waf_rule.wafrule", "predicates.#", "1"), - resource.TestCheckResourceAttr( - "aws_waf_rule.wafrule", "metric_name", wafRuleNewName), + testAccCheckAWSWafRuleExists(resourceName, &after), + resource.TestCheckResourceAttr(resourceName, "name", wafRuleNewName), + resource.TestCheckResourceAttr(resourceName, "predicates.#", "1"), + resource.TestCheckResourceAttr(resourceName, "metric_name", wafRuleNewName), ), }, }, @@ -84,6 +77,8 @@ func TestAccAWSWafRule_changeNameForceNew(t *testing.T) { func TestAccAWSWafRule_disappears(t *testing.T) { var v waf.Rule wafRuleName := fmt.Sprintf("wafrule%s", acctest.RandString(5)) + resourceName := "aws_waf_rule.wafrule" + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSWaf(t) }, Providers: testAccProviders, @@ -92,7 +87,7 @@ func TestAccAWSWafRule_disappears(t *testing.T) { { Config: testAccAWSWafRuleConfig(wafRuleName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSWafRuleExists("aws_waf_rule.wafrule", &v), + testAccCheckAWSWafRuleExists(resourceName, &v), testAccCheckAWSWafRuleDisappears(&v), ), ExpectNonEmptyPlan: true, @@ -108,6 +103,7 @@ func TestAccAWSWafRule_changePredicates(t *testing.T) { var before, after waf.Rule var idx int ruleName := fmt.Sprintf("wafrule%s", acctest.RandString(5)) + resourceName := "aws_waf_rule.wafrule" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSWaf(t) }, @@ -118,24 +114,24 @@ func TestAccAWSWafRule_changePredicates(t *testing.T) { Config: testAccAWSWafRuleConfig(ruleName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSWafIPSetExists("aws_waf_ipset.ipset", &ipset), - testAccCheckAWSWafRuleExists("aws_waf_rule.wafrule", &before), - resource.TestCheckResourceAttr("aws_waf_rule.wafrule", "name", ruleName), - resource.TestCheckResourceAttr("aws_waf_rule.wafrule", "predicates.#", "1"), + testAccCheckAWSWafRuleExists(resourceName, &before), + resource.TestCheckResourceAttr(resourceName, "name", ruleName), + resource.TestCheckResourceAttr(resourceName, "predicates.#", "1"), computeWafRulePredicateWithIpSet(&ipset, false, "IPMatch", &idx), - testCheckResourceAttrWithIndexesAddr("aws_waf_rule.wafrule", "predicates.%d.negated", &idx, "false"), - testCheckResourceAttrWithIndexesAddr("aws_waf_rule.wafrule", "predicates.%d.type", &idx, "IPMatch"), + testCheckResourceAttrWithIndexesAddr(resourceName, "predicates.%d.negated", &idx, "false"), + testCheckResourceAttrWithIndexesAddr(resourceName, "predicates.%d.type", &idx, "IPMatch"), ), }, { Config: testAccAWSWafRuleConfig_changePredicates(ruleName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSWafByteMatchSetExists("aws_waf_byte_match_set.set", &byteMatchSet), - testAccCheckAWSWafRuleExists("aws_waf_rule.wafrule", &after), - resource.TestCheckResourceAttr("aws_waf_rule.wafrule", "name", ruleName), - resource.TestCheckResourceAttr("aws_waf_rule.wafrule", "predicates.#", "1"), + testAccCheckAWSWafRuleExists(resourceName, &after), + resource.TestCheckResourceAttr(resourceName, "name", ruleName), + resource.TestCheckResourceAttr(resourceName, "predicates.#", "1"), computeWafRulePredicateWithByteMatchSet(&byteMatchSet, true, "ByteMatch", &idx), - testCheckResourceAttrWithIndexesAddr("aws_waf_rule.wafrule", "predicates.%d.negated", &idx, "true"), - testCheckResourceAttrWithIndexesAddr("aws_waf_rule.wafrule", "predicates.%d.type", &idx, "ByteMatch"), + testCheckResourceAttrWithIndexesAddr(resourceName, "predicates.%d.negated", &idx, "true"), + testCheckResourceAttrWithIndexesAddr(resourceName, "predicates.%d.type", &idx, "ByteMatch"), ), }, }, @@ -148,6 +144,7 @@ func TestAccAWSWafRule_geoMatchSetPredicate(t *testing.T) { var v waf.Rule var idx int ruleName := fmt.Sprintf("wafrule%s", acctest.RandString(5)) + resourceName := "aws_waf_rule.wafrule" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSWaf(t) }, @@ -158,12 +155,12 @@ func TestAccAWSWafRule_geoMatchSetPredicate(t *testing.T) { Config: testAccAWSWafRuleConfig_geoMatchSetPredicate(ruleName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAWSWafGeoMatchSetExists("aws_waf_geo_match_set.geo_match_set", &geoMatchSet), - testAccCheckAWSWafRuleExists("aws_waf_rule.wafrule", &v), - resource.TestCheckResourceAttr("aws_waf_rule.wafrule", "name", ruleName), - resource.TestCheckResourceAttr("aws_waf_rule.wafrule", "predicates.#", "1"), + testAccCheckAWSWafRuleExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "name", ruleName), + resource.TestCheckResourceAttr(resourceName, "predicates.#", "1"), computeWafRulePredicateWithGeoMatchSet(&geoMatchSet, true, "GeoMatch", &idx), - testCheckResourceAttrWithIndexesAddr("aws_waf_rule.wafrule", "predicates.%d.negated", &idx, "true"), - testCheckResourceAttrWithIndexesAddr("aws_waf_rule.wafrule", "predicates.%d.type", &idx, "GeoMatch"), + testCheckResourceAttrWithIndexesAddr(resourceName, "predicates.%d.negated", &idx, "true"), + testCheckResourceAttrWithIndexesAddr(resourceName, "predicates.%d.type", &idx, "GeoMatch"), ), }, }, @@ -236,6 +233,7 @@ func testCheckResourceAttrWithIndexesAddr(name, format string, idx *int, value s func TestAccAWSWafRule_noPredicates(t *testing.T) { var rule waf.Rule ruleName := fmt.Sprintf("wafrule%s", acctest.RandString(5)) + resourceName := "aws_waf_rule.wafrule" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSWaf(t) }, @@ -245,17 +243,68 @@ func TestAccAWSWafRule_noPredicates(t *testing.T) { { Config: testAccAWSWafRuleConfig_noPredicates(ruleName), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSWafRuleExists("aws_waf_rule.wafrule", &rule), - resource.TestCheckResourceAttr( - "aws_waf_rule.wafrule", "name", ruleName), - resource.TestCheckResourceAttr( - "aws_waf_rule.wafrule", "predicates.#", "0"), + testAccCheckAWSWafRuleExists(resourceName, &rule), + resource.TestCheckResourceAttr(resourceName, "name", ruleName), + resource.TestCheckResourceAttr(resourceName, "predicates.#", "0"), ), }, }, }) } +func TestAccAWSWafRule_Tags(t *testing.T) { + var rule waf.Rule + ruleName := fmt.Sprintf("wafrule%s", acctest.RandString(5)) + resourceName := "aws_waf_rule.wafrule" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSWaf(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSWafWebAclDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSWafRuleConfigTags1(ruleName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafRuleExists(resourceName, &rule), + resource.TestCheckResourceAttr(resourceName, "predicates.#", "1"), + resource.TestCheckResourceAttr(resourceName, "metric_name", ruleName), + resource.TestCheckResourceAttr(resourceName, "name", ruleName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + Config: testAccAWSWafRuleConfigTags2(ruleName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafRuleExists(resourceName, &rule), + resource.TestCheckResourceAttr(resourceName, "predicates.#", "1"), + resource.TestCheckResourceAttr(resourceName, "metric_name", ruleName), + resource.TestCheckResourceAttr(resourceName, "name", ruleName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccAWSWafRuleConfigTags1(ruleName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafRuleExists(resourceName, &rule), + resource.TestCheckResourceAttr(resourceName, "predicates.#", "1"), + resource.TestCheckResourceAttr(resourceName, "metric_name", ruleName), + resource.TestCheckResourceAttr(resourceName, "name", ruleName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testAccCheckAWSWafRuleDisappears(v *waf.Rule) resource.TestCheckFunc { return func(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).wafconn @@ -496,3 +545,62 @@ resource "aws_waf_rule" "wafrule" { } `, name, name, name) } + +func testAccAWSWafRuleConfigTags1(rName, tag1Key, tag1Value string) string { + return fmt.Sprintf(` +resource "aws_waf_ipset" "ipset" { + name = "%s" + + ip_set_descriptors { + type = "IPV4" + value = "192.0.7.0/24" + } +} + +resource "aws_waf_rule" "wafrule" { + depends_on = ["aws_waf_ipset.ipset"] + name = "%s" + metric_name = "%s" + + predicates { + data_id = "${aws_waf_ipset.ipset.id}" + negated = false + type = "IPMatch" + } + + tags = { + %q = %q + } +} +`, rName, rName, rName, tag1Key, tag1Value) +} + +func testAccAWSWafRuleConfigTags2(rName, tag1Key, tag1Value, tag2Key, tag2Value string) string { + return fmt.Sprintf(` +resource "aws_waf_ipset" "ipset" { + name = "%s" + + ip_set_descriptors { + type = "IPV4" + value = "192.0.7.0/24" + } +} + +resource "aws_waf_rule" "wafrule" { + depends_on = ["aws_waf_ipset.ipset"] + name = "%s" + metric_name = "%s" + + predicates { + data_id = "${aws_waf_ipset.ipset.id}" + negated = false + type = "IPMatch" + } + + tags = { + %q = %q + %q = %q + } +} +`, rName, rName, rName, tag1Key, tag1Value, tag2Key, tag2Value) +} diff --git a/aws/resource_aws_waf_web_acl.go b/aws/resource_aws_waf_web_acl.go index cda331ab52a1..dbe65c2bd40c 100644 --- a/aws/resource_aws_waf_web_acl.go +++ b/aws/resource_aws_waf_web_acl.go @@ -9,6 +9,7 @@ import ( "github.com/aws/aws-sdk-go/service/waf" "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 resourceAwsWafWebAcl() *schema.Resource { @@ -140,12 +141,14 @@ func resourceAwsWafWebAcl() *schema.Resource { }, }, }, + "tags": tagsSchema(), }, } } func resourceAwsWafWebAclCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).wafconn + tags := keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().WafTags() wr := newWafRetryer(conn) out, err := wr.RetryWithToken(func(token *string) (interface{}, error) { @@ -156,6 +159,10 @@ func resourceAwsWafWebAclCreate(d *schema.ResourceData, meta interface{}) error Name: aws.String(d.Get("name").(string)), } + if len(tags) > 0 { + params.Tags = tags + } + return conn.CreateWebACL(params) }) if err != nil { @@ -212,6 +219,17 @@ func resourceAwsWafWebAclRead(d *schema.ResourceData, meta interface{}) error { } d.Set("name", resp.WebACL.Name) d.Set("metric_name", resp.WebACL.MetricName) + + tagList, err := conn.ListTagsForResource(&waf.ListTagsForResourceInput{ + ResourceARN: aws.String(arn), + }) + if err != nil { + return fmt.Errorf("Failed to get WAF ACL parameter tags for %s: %s", d.Get("name"), err) + } + if err := d.Set("tags", keyvaluetags.WafKeyValueTags(tagList.TagInfoForResource.TagList).IgnoreAws().Map()); err != nil { + return fmt.Errorf("error setting tags: %s", err) + } + if err := d.Set("rules", flattenWafWebAclRules(resp.WebACL.Rules)); err != nil { return fmt.Errorf("error setting rules: %s", err) } @@ -286,6 +304,14 @@ func resourceAwsWafWebAclUpdate(d *schema.ResourceData, meta interface{}) error } + if d.HasChange("tags") { + o, n := d.GetChange("tags") + + if err := keyvaluetags.WafUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return fmt.Errorf("error updating tags: %s", err) + } + } + return resourceAwsWafWebAclRead(d, meta) } diff --git a/aws/resource_aws_waf_web_acl_test.go b/aws/resource_aws_waf_web_acl_test.go index 81dccb0c7256..9b22ceaca64f 100644 --- a/aws/resource_aws_waf_web_acl_test.go +++ b/aws/resource_aws_waf_web_acl_test.go @@ -131,6 +131,7 @@ func TestAccAWSWafWebAcl_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "default_action.4234791575.type", "ALLOW"), resource.TestCheckResourceAttr(resourceName, "metric_name", rName), resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), resource.TestCheckResourceAttr(resourceName, "rules.#", "0"), ), }, @@ -162,6 +163,7 @@ func TestAccAWSWafWebAcl_changeNameForceNew(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "default_action.4234791575.type", "ALLOW"), resource.TestCheckResourceAttr(resourceName, "metric_name", rName1), resource.TestCheckResourceAttr(resourceName, "name", rName1), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), resource.TestCheckResourceAttr(resourceName, "rules.#", "0"), ), }, @@ -173,6 +175,7 @@ func TestAccAWSWafWebAcl_changeNameForceNew(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "default_action.4234791575.type", "ALLOW"), resource.TestCheckResourceAttr(resourceName, "metric_name", rName2), resource.TestCheckResourceAttr(resourceName, "name", rName2), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), resource.TestCheckResourceAttr(resourceName, "rules.#", "0"), ), }, @@ -336,6 +339,65 @@ func TestAccAWSWafWebAcl_disappears(t *testing.T) { }) } +func TestAccAWSWafWebAcl_Tags(t *testing.T) { + var webACL waf.WebACL + rName := fmt.Sprintf("wafacl%s", acctest.RandString(5)) + resourceName := "aws_waf_web_acl.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSWaf(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSWafWebAclDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSWafWebAclConfigTags1(rName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafWebAclExists(resourceName, &webACL), + resource.TestCheckResourceAttr(resourceName, "default_action.#", "1"), + resource.TestCheckResourceAttr(resourceName, "default_action.4234791575.type", "ALLOW"), + resource.TestCheckResourceAttr(resourceName, "metric_name", rName), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + resource.TestCheckResourceAttr(resourceName, "rules.#", "0"), + ), + }, + { + Config: testAccAWSWafWebAclConfigTags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafWebAclExists(resourceName, &webACL), + resource.TestCheckResourceAttr(resourceName, "default_action.#", "1"), + resource.TestCheckResourceAttr(resourceName, "default_action.4234791575.type", "ALLOW"), + resource.TestCheckResourceAttr(resourceName, "metric_name", rName), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + resource.TestCheckResourceAttr(resourceName, "rules.#", "0"), + ), + }, + { + Config: testAccAWSWafWebAclConfigTags1(rName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSWafWebAclExists(resourceName, &webACL), + resource.TestCheckResourceAttr(resourceName, "default_action.#", "1"), + resource.TestCheckResourceAttr(resourceName, "default_action.4234791575.type", "ALLOW"), + resource.TestCheckResourceAttr(resourceName, "metric_name", rName), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + resource.TestCheckResourceAttr(resourceName, "rules.#", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testAccCheckAWSWafWebAclDisappears(v *waf.WebACL) resource.TestCheckFunc { return func(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).wafconn @@ -683,3 +745,36 @@ resource "aws_kinesis_firehose_delivery_stream" "test" { } `, rName) } + +func testAccAWSWafWebAclConfigTags1(rName, tag1Key, tag1Value string) string { + return fmt.Sprintf(` +resource "aws_waf_web_acl" "test" { + metric_name = %q + name = %q + + default_action { + type = "ALLOW" + } + tags = { + %q = %q + } +} +`, rName, rName, tag1Key, tag1Value) +} + +func testAccAWSWafWebAclConfigTags2(rName, tag1Key, tag1Value, tag2Key, tag2Value string) string { + return fmt.Sprintf(` +resource "aws_waf_web_acl" "test" { + metric_name = %q + name = %q + + default_action { + type = "ALLOW" + } + tags = { + %q = %q + %q = %q + } +} +`, rName, rName, tag1Key, tag1Value, tag2Key, tag2Value) +} diff --git a/website/docs/r/waf_rate_based_rule.html.markdown b/website/docs/r/waf_rate_based_rule.html.markdown index 9a8d6dc6d95c..fcf126e2e78a 100644 --- a/website/docs/r/waf_rate_based_rule.html.markdown +++ b/website/docs/r/waf_rate_based_rule.html.markdown @@ -69,3 +69,11 @@ See the [WAF Documentation](https://docs.aws.amazon.com/waf/latest/APIReference/ In addition to all arguments above, the following attributes are exported: * `id` - The ID of the WAF rule. + +## Import + +WAF Rated Based Rule can be imported using the id, e.g. + +``` +$ terraform import aws_waf_rate_based_rule.wafrule a1b2c3d4-d5f6-7777-8888-9999aaaabbbbcccc +``` \ No newline at end of file diff --git a/website/docs/r/waf_rule.html.markdown b/website/docs/r/waf_rule.html.markdown index 0745fdeabc5f..13fb781395b0 100644 --- a/website/docs/r/waf_rule.html.markdown +++ b/website/docs/r/waf_rule.html.markdown @@ -41,6 +41,7 @@ The following arguments are supported: * `metric_name` - (Required) The name or description for the Amazon CloudWatch metric of this rule. The name can contain only alphanumeric characters (A-Z, a-z, 0-9); the name can't contain whitespace. * `name` - (Required) The name or description of the rule. * `predicates` - (Optional) The objects to include in a rule (documented below). +* `tags` - (Optional) Key-value mapping of resource tags ## Nested Blocks diff --git a/website/docs/r/waf_rule_group.html.markdown b/website/docs/r/waf_rule_group.html.markdown index 232405864135..248007be215a 100644 --- a/website/docs/r/waf_rule_group.html.markdown +++ b/website/docs/r/waf_rule_group.html.markdown @@ -39,6 +39,7 @@ The following arguments are supported: * `name` - (Required) A friendly name of the rule group * `metric_name` - (Required) A friendly name for the metrics from the rule group * `activated_rule` - (Optional) A list of activated rules, see below +* `tags` - (Optional) Key-value mapping of resource tags ## Nested Blocks diff --git a/website/docs/r/waf_web_acl.html.markdown b/website/docs/r/waf_web_acl.html.markdown index 78e856fb4197..50dc7da5a3f0 100644 --- a/website/docs/r/waf_web_acl.html.markdown +++ b/website/docs/r/waf_web_acl.html.markdown @@ -87,6 +87,7 @@ The following arguments are supported: * `name` - (Required) The name or description of the web ACL. * `rules` - (Optional) Configuration blocks containing rules to associate with the web ACL and the settings for each rule. Detailed below. * `logging_configuration` - (Optional) Configuration block to enable WAF logging. Detailed below. +* `tags` - (Optional) Key-value mapping of resource tags ### `default_action` Configuration Block