diff --git a/.changelog/14319.txt b/.changelog/14319.txt new file mode 100644 index 00000000000..71d707d9f4d --- /dev/null +++ b/.changelog/14319.txt @@ -0,0 +1,7 @@ +```release-note:bug +resource/aws_wafv2_web_acl_logging_configuration: Ensure `redacted_fields` are applied to the resource +``` + +```release-note:note +resource/aws_wafv2_web_acl_logging_configuration: The `redacted_fields` configuration block `all_query_arguments`, `body`, and `single_query_argument` arguments have been deprecated to match the WAF API documentation +``` \ No newline at end of file diff --git a/aws/resource_aws_wafv2_web_acl_logging_configuration.go b/aws/resource_aws_wafv2_web_acl_logging_configuration.go index a1285ec62c5..b9b0e65f909 100644 --- a/aws/resource_aws_wafv2_web_acl_logging_configuration.go +++ b/aws/resource_aws_wafv2_web_acl_logging_configuration.go @@ -1,12 +1,16 @@ package aws import ( + "bytes" "fmt" "log" + "regexp" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/wafv2" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/hashcode" ) func resourceAwsWafv2WebACLLoggingConfiguration() *schema.Resource { @@ -34,11 +38,68 @@ func resourceAwsWafv2WebACLLoggingConfiguration() *schema.Resource { Description: "AWS Kinesis Firehose Delivery Stream ARNs", }, "redacted_fields": { - Type: schema.TypeSet, - Optional: true, - MaxItems: 100, - Elem: wafv2FieldToMatchBaseSchema(), - Description: "Parts of the request to exclude from logs", + // To allow this argument and its nested fields with Empty Schemas (e.g. "method") + // to be correctly interpreted, this argument must be of type List, + // otherwise, at apply-time a field configured as an empty block + // (e.g. body {}) will result in a nil redacted_fields attribute + Type: schema.TypeList, + Optional: true, + MaxItems: 100, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + // TODO: remove attributes marked as Deprecated + // as they are not supported by the WAFv2 API + // in the context of WebACL Logging Configurations + "all_query_arguments": wafv2EmptySchemaDeprecated(), + "body": wafv2EmptySchemaDeprecated(), + "method": wafv2EmptySchema(), + "query_string": wafv2EmptySchema(), + "single_header": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 40), + // The value is returned in lower case by the API. + // Trying to solve it with StateFunc and/or DiffSuppressFunc resulted in hash problem of the rule field or didn't work. + validation.StringMatch(regexp.MustCompile(`^[a-z0-9-_]+$`), "must contain only lowercase alphanumeric characters, underscores, and hyphens"), + ), + Deprecated: "Not supported by WAFv2 API", + }, + }, + }, + }, + "single_query_argument": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 30), + // The value is returned in lower case by the API. + // Trying to solve it with StateFunc and/or DiffSuppressFunc resulted in hash problem of the rule field or didn't work. + validation.StringMatch(regexp.MustCompile(`^[a-z0-9-_]+$`), "must contain only lowercase alphanumeric characters, underscores, and hyphens"), + ), + Deprecated: "Not supported by WAFv2 API", + }, + }, + }, + Deprecated: "Not supported by WAFv2 API", + }, + "uri_path": wafv2EmptySchema(), + }, + }, + Description: "Parts of the request to exclude from logs", + DiffSuppressFunc: suppressRedactedFieldsDiff, }, "resource_arn": { Type: schema.TypeString, @@ -60,8 +121,8 @@ func resourceAwsWafv2WebACLLoggingConfigurationPut(d *schema.ResourceData, meta ResourceArn: aws.String(resourceArn), } - if v, ok := d.GetOk("redacted_fields"); ok && v.(*schema.Set).Len() > 0 { - config.RedactedFields = expandWafv2RedactedFields(v.(*schema.Set).List()) + if v, ok := d.GetOk("redacted_fields"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + config.RedactedFields = expandWafv2RedactedFields(v.([]interface{})) } else { config.RedactedFields = []*wafv2.FieldToMatch{} } @@ -126,18 +187,129 @@ func resourceAwsWafv2WebACLLoggingConfigurationDelete(d *schema.ResourceData, me return nil } -func flattenWafv2RedactedFields(fields []*wafv2.FieldToMatch) []map[string]interface{} { - redactedFields := make([]map[string]interface{}, 0, len(fields)) +func expandWafv2RedactedFields(fields []interface{}) []*wafv2.FieldToMatch { + redactedFields := make([]*wafv2.FieldToMatch, 0, len(fields)) for _, field := range fields { - redactedFields = append(redactedFields, flattenWafv2FieldToMatch(field).([]interface{})[0].(map[string]interface{})) + redactedFields = append(redactedFields, expandWafv2RedactedField(field)) } return redactedFields } -func expandWafv2RedactedFields(fields []interface{}) []*wafv2.FieldToMatch { - redactedFields := make([]*wafv2.FieldToMatch, 0, len(fields)) +func expandWafv2RedactedField(field interface{}) *wafv2.FieldToMatch { + m := field.(map[string]interface{}) + + f := &wafv2.FieldToMatch{} + + // While the FieldToMatch struct allows more than 1 of its fields to be set, + // the WAFv2 API does not. In addition, in the context of Logging Configuration requests, + // the WAFv2 API only supports the following redacted fields. + // Reference: https://github.com/terraform-providers/terraform-provider-aws/issues/14244 + if v, ok := m["method"]; ok && len(v.([]interface{})) > 0 { + f.Method = &wafv2.Method{} + } + + if v, ok := m["query_string"]; ok && len(v.([]interface{})) > 0 { + f.QueryString = &wafv2.QueryString{} + } + + if v, ok := m["single_header"]; ok && len(v.([]interface{})) > 0 { + f.SingleHeader = expandWafv2SingleHeader(m["single_header"].([]interface{})) + } + + if v, ok := m["uri_path"]; ok && len(v.([]interface{})) > 0 { + f.UriPath = &wafv2.UriPath{} + } + + return f +} + +func flattenWafv2RedactedFields(fields []*wafv2.FieldToMatch) []interface{} { + redactedFields := make([]interface{}, 0, len(fields)) for _, field := range fields { - redactedFields = append(redactedFields, expandWafv2FieldToMatch([]interface{}{field})) + redactedFields = append(redactedFields, flattenWafv2RedactedField(field)) } return redactedFields } + +func flattenWafv2RedactedField(f *wafv2.FieldToMatch) map[string]interface{} { + m := map[string]interface{}{} + + if f == nil { + return m + } + + // In the context of Logging Configuration requests, + // the WAFv2 API only supports the following redacted fields. + // Reference: https://github.com/terraform-providers/terraform-provider-aws/issues/14244 + if f.Method != nil { + m["method"] = make([]map[string]interface{}, 1) + } + + if f.QueryString != nil { + m["query_string"] = make([]map[string]interface{}, 1) + } + + if f.SingleHeader != nil { + m["single_header"] = flattenWafv2SingleHeader(f.SingleHeader) + } + + if f.UriPath != nil { + m["uri_path"] = make([]map[string]interface{}, 1) + } + + return m +} + +// redactedFieldsHash takes a map[string]interface{} as input and generates a +// unique hashcode, taking into account keys defined in the resource's schema +// are present even if not explicitly configured +func redactedFieldsHash(v interface{}) int { + var buf bytes.Buffer + m, ok := v.(map[string]interface{}) + if !ok { + return 0 + } + if v, ok := m["method"].([]interface{}); ok && len(v) > 0 { + buf.WriteString("method-") + } + if v, ok := m["query_string"].([]interface{}); ok && len(v) > 0 { + buf.WriteString("query_string-") + } + if v, ok := m["uri_path"].([]interface{}); ok && len(v) > 0 { + buf.WriteString("uri_path-") + } + if v, ok := m["single_header"].([]interface{}); ok && len(v) > 0 { + sh, ok := v[0].(map[string]interface{}) + if ok { + if name, ok := sh["name"].(string); ok { + buf.WriteString(fmt.Sprintf("%s-", name)) + } + } + } + + return hashcode.String(buf.String()) +} + +func suppressRedactedFieldsDiff(k, old, new string, d *schema.ResourceData) bool { + o, n := d.GetChange("redacted_fields") + oList := o.([]interface{}) + nList := n.([]interface{}) + + if len(oList) == 0 && len(nList) == 0 { + return true + } + + if len(oList) == 0 && len(nList) != 0 { + // account for empty block + return nList[0] == nil + } + + if len(oList) != 0 && len(nList) == 0 { + // account for empty block + return oList[0] == nil + } + + oldSet := schema.NewSet(redactedFieldsHash, oList) + newSet := schema.NewSet(redactedFieldsHash, nList) + return oldSet.Equal(newSet) +} diff --git a/aws/resource_aws_wafv2_web_acl_logging_configuration_test.go b/aws/resource_aws_wafv2_web_acl_logging_configuration_test.go index bce42080a91..a9b16c2a7d4 100644 --- a/aws/resource_aws_wafv2_web_acl_logging_configuration_test.go +++ b/aws/resource_aws_wafv2_web_acl_logging_configuration_test.go @@ -40,7 +40,7 @@ func TestAccAwsWafv2WebACLLoggingConfiguration_basic(t *testing.T) { }) } -func TestAccAwsWafv2WebACLLoggingConfiguration_update(t *testing.T) { +func TestAccAwsWafv2WebACLLoggingConfiguration_updateSingleHeaderRedactedField(t *testing.T) { var v wafv2.LoggingConfiguration rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_wafv2_web_acl_logging_configuration.test" @@ -61,7 +61,7 @@ func TestAccAwsWafv2WebACLLoggingConfiguration_update(t *testing.T) { ), }, { - Config: testAccAwsWafv2WebACLLoggingConfiguration_updateTwoRedactedFields(rName), + Config: testAccAwsWafv2WebACLLoggingConfiguration_updateTwoSingleHeaderRedactedFields(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), @@ -76,12 +76,204 @@ func TestAccAwsWafv2WebACLLoggingConfiguration_update(t *testing.T) { ), }, { - Config: testAccAwsWafv2WebACLLoggingConfiguration_updateOneRedactedField(rName), + Config: testAccAwsWafv2WebACLLoggingConfiguration_updateSingleHeaderRedactedField(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + "single_header.0.name": "user-agent", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +// Reference: https://github.com/terraform-providers/terraform-provider-aws/issues/14248 +func TestAccAwsWafv2WebACLLoggingConfiguration_updateMethodRedactedField(t *testing.T) { + var v wafv2.LoggingConfiguration + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_web_acl_logging_configuration.test" + webACLResourceName := "aws_wafv2_web_acl.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2WebACLLoggingConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2WebACLLoggingConfiguration_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "0"), + ), + }, + { + Config: testAccAwsWafv2WebACLLoggingConfiguration_updateRedactedField(rName, "method"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + "method.#": "1", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +// Reference: https://github.com/terraform-providers/terraform-provider-aws/issues/14248 +func TestAccAwsWafv2WebACLLoggingConfiguration_updateQueryStringRedactedField(t *testing.T) { + var v wafv2.LoggingConfiguration + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_web_acl_logging_configuration.test" + webACLResourceName := "aws_wafv2_web_acl.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2WebACLLoggingConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2WebACLLoggingConfiguration_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "0"), + ), + }, + { + Config: testAccAwsWafv2WebACLLoggingConfiguration_updateRedactedField(rName, "query_string"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + "query_string.#": "1", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +// Reference: https://github.com/terraform-providers/terraform-provider-aws/issues/14248 +func TestAccAwsWafv2WebACLLoggingConfiguration_updateUriPathRedactedField(t *testing.T) { + var v wafv2.LoggingConfiguration + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_web_acl_logging_configuration.test" + webACLResourceName := "aws_wafv2_web_acl.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2WebACLLoggingConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2WebACLLoggingConfiguration_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "0"), + ), + }, + { + Config: testAccAwsWafv2WebACLLoggingConfiguration_updateRedactedField(rName, "uri_path"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + "uri_path.#": "1", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +// Reference: https://github.com/terraform-providers/terraform-provider-aws/issues/14248 +func TestAccAwsWafv2WebACLLoggingConfiguration_updateMultipleRedactedFields(t *testing.T) { + var v wafv2.LoggingConfiguration + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_web_acl_logging_configuration.test" + webACLResourceName := "aws_wafv2_web_acl.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2WebACLLoggingConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2WebACLLoggingConfiguration_updateRedactedField(rName, "uri_path"), Check: resource.ComposeTestCheckFunc( testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + "uri_path.#": "1", + }), + ), + }, + { + Config: testAccAwsWafv2WebACLLoggingConfiguration_updateTwoRedactedFields(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "2"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + "uri_path.#": "1", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + "method.#": "1", + }), + ), + }, + { + Config: testAccAwsWafv2WebACLLoggingConfiguration_updateThreeRedactedFields(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "3"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + "uri_path.#": "1", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + "query_string.#": "1", + }), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ "single_header.0.name": "user-agent", }), @@ -201,6 +393,76 @@ func TestAccAwsWafv2WebACLLoggingConfiguration_disappears(t *testing.T) { }) } +func TestAccAwsWafv2WebACLLoggingConfiguration_emptyRedactedFields(t *testing.T) { + var v wafv2.LoggingConfiguration + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_web_acl_logging_configuration.test" + webACLResourceName := "aws_wafv2_web_acl.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2WebACLLoggingConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2WebACLLoggingConfiguration_emptyRedactedField(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAwsWafv2WebACLLoggingConfiguration_updateEmptyRedactedFields(t *testing.T) { + var v wafv2.LoggingConfiguration + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_web_acl_logging_configuration.test" + webACLResourceName := "aws_wafv2_web_acl.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2WebACLLoggingConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2WebACLLoggingConfiguration_emptyRedactedField(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "0"), + ), + }, + { + Config: testAccAwsWafv2WebACLLoggingConfiguration_updateRedactedField(rName, "uri_path"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLLoggingConfigurationExists(resourceName, &v), + resource.TestCheckResourceAttrPair(resourceName, "resource_arn", webACLResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination_configs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "redacted_fields.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "redacted_fields.*", map[string]string{ + "uri_path.#": "1", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAwsWafv2WebACLLoggingConfiguration_disappears_WebAcl(t *testing.T) { var v wafv2.LoggingConfiguration rName := acctest.RandomWithPrefix("tf-acc-test") @@ -405,7 +667,15 @@ resource "aws_wafv2_web_acl_logging_configuration" "test" { } ` -const testAccWebACLLoggingConfigurationResourceUpdateTwoRedactedFieldsConfig = ` +const testAccWebACLLoggingConfigurationResource_emptyRedactedFieldsConfig = ` +resource "aws_wafv2_web_acl_logging_configuration" "test" { + resource_arn = aws_wafv2_web_acl.test.arn + log_destination_configs = [aws_kinesis_firehose_delivery_stream.test.arn] + redacted_fields {} +} +` + +const testAccWebACLLoggingConfigurationResource_updateTwoSingleHeaderRedactedFieldsConfig = ` resource "aws_wafv2_web_acl_logging_configuration" "test" { resource_arn = aws_wafv2_web_acl.test.arn log_destination_configs = [aws_kinesis_firehose_delivery_stream.test.arn] @@ -424,11 +694,69 @@ resource "aws_wafv2_web_acl_logging_configuration" "test" { } ` -const testAccWebACLLoggingConfigurationResourceUpdateOneRedactedFieldConfig = ` +const testAccWebACLLoggingConfigurationResource_updateSingleHeaderRedactedFieldConfig = ` +resource "aws_wafv2_web_acl_logging_configuration" "test" { + resource_arn = aws_wafv2_web_acl.test.arn + log_destination_configs = [aws_kinesis_firehose_delivery_stream.test.arn] + + redacted_fields { + single_header { + name = "user-agent" + } + } +} +` + +func testAccWebACLLoggingConfigurationResource_updateRedactedFieldConfig(field string) string { + var redactedField string + switch field { + case "method": + redactedField = `method {}` + case "query_string": + redactedField = `query_string {}` + case "uri_path": + redactedField = `uri_path {}` + } + return fmt.Sprintf(` +resource "aws_wafv2_web_acl_logging_configuration" "test" { + resource_arn = aws_wafv2_web_acl.test.arn + log_destination_configs = [aws_kinesis_firehose_delivery_stream.test.arn] + + redacted_fields { + %s + } +} +`, redactedField) +} + +const testAccWebACLLoggingConfigurationResource_updateTwoRedactedFieldsConfig = ` +resource "aws_wafv2_web_acl_logging_configuration" "test" { + resource_arn = aws_wafv2_web_acl.test.arn + log_destination_configs = [aws_kinesis_firehose_delivery_stream.test.arn] + + redacted_fields { + method {} + } + + redacted_fields { + uri_path {} + } +} +` + +const testAccWebACLLoggingConfigurationResource_updateThreeRedactedFieldsConfig = ` resource "aws_wafv2_web_acl_logging_configuration" "test" { resource_arn = aws_wafv2_web_acl.test.arn log_destination_configs = [aws_kinesis_firehose_delivery_stream.test.arn] + redacted_fields { + uri_path {} + } + + redacted_fields { + query_string {} + } + redacted_fields { single_header { name = "user-agent" @@ -451,16 +779,44 @@ func testAccAwsWafv2WebACLLoggingConfiguration_updateLogDestination(rName, rName testAccWebACLLoggingConfigurationResourceConfig) } +func testAccAwsWafv2WebACLLoggingConfiguration_updateTwoSingleHeaderRedactedFields(rName string) string { + return composeConfig( + testAccWebACLLoggingConfigurationDependenciesConfig(rName), + testAccWebACLLoggingConfigurationKinesisDependencyConfig(rName), + testAccWebACLLoggingConfigurationResource_updateTwoSingleHeaderRedactedFieldsConfig) +} + +func testAccAwsWafv2WebACLLoggingConfiguration_updateSingleHeaderRedactedField(rName string) string { + return composeConfig( + testAccWebACLLoggingConfigurationDependenciesConfig(rName), + testAccWebACLLoggingConfigurationKinesisDependencyConfig(rName), + testAccWebACLLoggingConfigurationResource_updateSingleHeaderRedactedFieldConfig) +} + +func testAccAwsWafv2WebACLLoggingConfiguration_updateRedactedField(rName, field string) string { + return composeConfig( + testAccWebACLLoggingConfigurationDependenciesConfig(rName), + testAccWebACLLoggingConfigurationKinesisDependencyConfig(rName), + testAccWebACLLoggingConfigurationResource_updateRedactedFieldConfig(field)) +} + func testAccAwsWafv2WebACLLoggingConfiguration_updateTwoRedactedFields(rName string) string { return composeConfig( testAccWebACLLoggingConfigurationDependenciesConfig(rName), testAccWebACLLoggingConfigurationKinesisDependencyConfig(rName), - testAccWebACLLoggingConfigurationResourceUpdateTwoRedactedFieldsConfig) + testAccWebACLLoggingConfigurationResource_updateTwoRedactedFieldsConfig) +} + +func testAccAwsWafv2WebACLLoggingConfiguration_updateThreeRedactedFields(rName string) string { + return composeConfig( + testAccWebACLLoggingConfigurationDependenciesConfig(rName), + testAccWebACLLoggingConfigurationKinesisDependencyConfig(rName), + testAccWebACLLoggingConfigurationResource_updateThreeRedactedFieldsConfig) } -func testAccAwsWafv2WebACLLoggingConfiguration_updateOneRedactedField(rName string) string { +func testAccAwsWafv2WebACLLoggingConfiguration_emptyRedactedField(rName string) string { return composeConfig( testAccWebACLLoggingConfigurationDependenciesConfig(rName), testAccWebACLLoggingConfigurationKinesisDependencyConfig(rName), - testAccWebACLLoggingConfigurationResourceUpdateOneRedactedFieldConfig) + testAccWebACLLoggingConfigurationResource_emptyRedactedFieldsConfig) } diff --git a/aws/wafv2_helper.go b/aws/wafv2_helper.go index 40e05658afe..32f12615529 100644 --- a/aws/wafv2_helper.go +++ b/aws/wafv2_helper.go @@ -21,6 +21,18 @@ func wafv2EmptySchema() *schema.Schema { } } +func wafv2EmptySchemaDeprecated() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{}, + }, + Deprecated: "Not supported by WAFv2 API", + } +} + func wafv2RootStatementSchema(level int) *schema.Schema { return &schema.Schema{ Type: schema.TypeList, diff --git a/website/docs/r/wafv2_rule_group.html.markdown b/website/docs/r/wafv2_rule_group.html.markdown index ca47b030c7e..b10a5f7b002 100644 --- a/website/docs/r/wafv2_rule_group.html.markdown +++ b/website/docs/r/wafv2_rule_group.html.markdown @@ -307,7 +307,7 @@ Each `rule` supports the following arguments: The `action` block supports the following arguments: -~> **NOTE**: One of `allow`, `block`, or `count`, expressed as an empty configuration block `{}`, is required when specifying an `action` +~> **NOTE:** One of `allow`, `block`, or `count`, expressed as an empty configuration block `{}`, is required when specifying an `action` * `allow` - (Optional) Instructs AWS WAF to allow the web request. * `block` - (Optional) Instructs AWS WAF to block the web request. @@ -429,7 +429,8 @@ The part of a web request that you want AWS WAF to inspect. Include the single ` The `field_to_match` block supports the following arguments: -~> **NOTE**: An empty configuration block `{}` should be used when specifying `all_query_arguments`, `body`, `method`, or `query_string` attributes +~> **NOTE:** Only one of `all_query_arguments`, `body`, `method`, `query_string`, `single_header`, `single_query_argument`, or `uri_path` can be specified. +An empty configuration block `{}` should be used when specifying `all_query_arguments`, `body`, `method`, or `query_string` attributes. * `all_query_arguments` - (Optional) Inspect all query arguments. * `body` - (Optional) Inspect the request body, which immediately follows the request headers. diff --git a/website/docs/r/wafv2_web_acl.html.markdown b/website/docs/r/wafv2_web_acl.html.markdown index f3a91cb8d71..2ce8e49de23 100644 --- a/website/docs/r/wafv2_web_acl.html.markdown +++ b/website/docs/r/wafv2_web_acl.html.markdown @@ -267,14 +267,14 @@ The following arguments are supported: The `default_action` block supports the following arguments: -~> **NOTE**: One of `allow` or `block`, expressed as an empty configuration block `{}`, is required when specifying a `default_action` +~> **NOTE:** One of `allow` or `block`, expressed as an empty configuration block `{}`, is required when specifying a `default_action` * `allow` - (Optional) Specifies that AWS WAF should allow requests by default. * `block` - (Optional) Specifies that AWS WAF should block requests by default. ### Rules -~> **NOTE**: One of `action` or `override_action` is required when specifying a rule +~> **NOTE:** One of `action` or `override_action` is required when specifying a rule Each `rule` supports the following arguments: @@ -289,7 +289,7 @@ Each `rule` supports the following arguments: The `action` block supports the following arguments: -~> **NOTE**: One of `allow`, `block`, or `count`, expressed as an empty configuration block `{}`, is required when specifying an `action` +~> **NOTE:** One of `allow`, `block`, or `count`, expressed as an empty configuration block `{}`, is required when specifying an `action` * `allow` - (Optional) Instructs AWS WAF to allow the web request. Configure as an empty block `{}`. * `block` - (Optional) Instructs AWS WAF to block the web request. Configure as an empty block `{}`. @@ -299,7 +299,7 @@ The `action` block supports the following arguments: The `override_action` block supports the following arguments: -~> **NOTE**: One of `count` or `none`, expressed as an empty configuration block `{}`, is required when specifying an `override_action` +~> **NOTE:** One of `count` or `none`, expressed as an empty configuration block `{}`, is required when specifying an `override_action` * `count` - (Optional) Override the rule action setting to count (i.e. only count matches). Configured as an empty block `{}`. * `none` - (Optional) Don't override the rule action setting. Configured as an empty block `{}`. @@ -466,7 +466,8 @@ The part of a web request that you want AWS WAF to inspect. Include the single ` The `field_to_match` block supports the following arguments: -~> **NOTE**: An empty configuration block `{}` should be used when specifying `all_query_arguments`, `body`, `method`, `query_string`, or `uri_path` attributes +~> **NOTE:** Only one of `all_query_arguments`, `body`, `method`, `query_string`, `single_header`, `single_query_argument`, or `uri_path` can be specified. +An empty configuration block `{}` should be used when specifying `all_query_arguments`, `body`, `method`, or `query_string` attributes. * `all_query_arguments` - (Optional) Inspect all query arguments. * `body` - (Optional) Inspect the request body, which immediately follows the request headers. diff --git a/website/docs/r/wafv2_web_acl_logging_configuration.html.markdown b/website/docs/r/wafv2_web_acl_logging_configuration.html.markdown index 62955499241..56537095307 100644 --- a/website/docs/r/wafv2_web_acl_logging_configuration.html.markdown +++ b/website/docs/r/wafv2_web_acl_logging_configuration.html.markdown @@ -38,13 +38,15 @@ The following arguments are supported: The `redacted_fields` block supports the following arguments: -* `all_query_arguments` - (Optional) Redact all query arguments. -* `body` - (Optional) Redact the request body, which immediately follows the request headers. -* `method` - (Optional) Redact the HTTP method. The method indicates the type of operation that the request is asking the origin to perform. -* `query_string` - (Optional) Redact the query string. This is the part of a URL that appears after a `?` character, if any. +~> **NOTE:** Only one of `method`, `query_string`, `single_header` or `uri_path` can be specified. + +* `all_query_arguments` - (Optional, **DEPRECATED**) Redact all query arguments. +* `body` - (Optional, **DEPRECATED**) Redact the request body, which immediately follows the request headers. +* `method` - (Optional) Redact the HTTP method. Must be specified as an empty configuration block `{}`. The method indicates the type of operation that the request is asking the origin to perform. +* `query_string` - (Optional) Redact the query string. Must be specified as an empty configuration block `{}`. This is the part of a URL that appears after a `?` character, if any. * `single_header` - (Optional) Redact a single header. See [Single Header](#single-header) below for details. -* `single_query_argument` - (Optional) Redact a single query argument. See [Single Query Argument](#single-query-argument) below for details. -* `uri_path` - (Optional) Redact the request URI path. This is the part of a web request that identifies a resource, for example, `/images/daily-ad.jpg`. +* `single_query_argument` - (Optional, **DEPRECATED**) Redact a single query argument. See [Single Query Argument](#single-query-argument) below for details. +* `uri_path` - (Optional) Redact the request URI path. Must be specified as an empty configuration block `{}`. This is the part of a web request that identifies a resource, for example, `/images/daily-ad.jpg`. ### Single Header @@ -54,7 +56,7 @@ The `single_header` block supports the following arguments: * `name` - (Optional) The name of the query header to redact. This setting must be provided as lower case characters. -### Single Query Argument +### Single Query Argument (**DEPRECATED**) Redact a single query argument. Provide the name of the query argument to redact, such as `UserName` or `SalesRegion` (provided as lowercase strings).