diff --git a/internal/services/network/web_application_firewall_policy_resource.go b/internal/services/network/web_application_firewall_policy_resource.go index 56bae3fae79d..3c8df05f18de 100644 --- a/internal/services/network/web_application_firewall_policy_resource.go +++ b/internal/services/network/web_application_firewall_policy_resource.go @@ -1,10 +1,13 @@ package network import ( + "context" "fmt" "log" "time" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" @@ -20,7 +23,7 @@ import ( ) func resourceWebApplicationFirewallPolicy() *pluginsdk.Resource { - return &pluginsdk.Resource{ + resource := &pluginsdk.Resource{ Create: resourceWebApplicationFirewallPolicyCreateUpdate, Read: resourceWebApplicationFirewallPolicyRead, Update: resourceWebApplicationFirewallPolicyCreateUpdate, @@ -272,11 +275,41 @@ func resourceWebApplicationFirewallPolicy() *pluginsdk.Resource { Required: true, ValidateFunc: validate.ValidateWebApplicationFirewallPolicyRuleGroupName, }, - "disabled_rules": { + "rule": { Type: pluginsdk.TypeList, Optional: true, - Elem: &pluginsdk.Schema{ - Type: pluginsdk.TypeString, + Computed: !features.FourPointOhBeta(), + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: func() interface{} { + if !features.FourPointOhBeta() { + return nil + } + + return false + }(), + }, + + "action": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(network.ActionTypeAllow), + string(network.ActionTypeAnomalyScoring), + string(network.ActionTypeBlock), + string(network.ActionTypeLog), + }, false), + }, + }, }, }, }, @@ -344,7 +377,40 @@ func resourceWebApplicationFirewallPolicy() *pluginsdk.Resource { "tags": tags.Schema(), }, + + CustomizeDiff: pluginsdk.CustomizeDiffShim(func(ctx context.Context, diff *pluginsdk.ResourceDiff, v interface{}) error { + if !features.FourPointOhBeta() { + // Since ConflictsWith cannot be used on these properties and the properties are optional and computed, diff.GetOK may still return value even the property is not configured. Have to check the configuration with GetRawConfig + managedRuleSetList := diff.GetRawConfig().AsValueMap()["managed_rules"].AsValueSlice()[0].AsValueMap()["managed_rule_set"].AsValueSlice() + for _, managedRuleSetVal := range managedRuleSetList { + ruleGroupOverrideList := managedRuleSetVal.AsValueMap()["rule_group_override"].AsValueSlice() + for _, ruleGroupOverrideVal := range ruleGroupOverrideList { + disabledRules := ruleGroupOverrideVal.AsValueMap()["disabled_rules"] + ruleList := ruleGroupOverrideVal.AsValueMap()["rule"].AsValueSlice() + if !disabledRules.IsNull() && len(ruleList) != 0 { + return fmt.Errorf("`disabled_rules` cannot be set when `rule` is set under `rule_group_override`") + } + } + } + } + + return nil + }), } + + if !features.FourPointOhBeta() { + resource.Schema["managed_rules"].Elem.(*pluginsdk.Resource).Schema["managed_rule_set"].Elem.(*pluginsdk.Resource).Schema["rule_group_override"].Elem.(*pluginsdk.Resource).Schema["disabled_rules"] = &pluginsdk.Schema{ + Type: pluginsdk.TypeList, + Optional: true, + Computed: true, + Deprecated: "`disabled_rules` will be removed in favour of the `rule` property in version 4.0 of the AzureRM Provider.", + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + } + } + + return resource } func resourceWebApplicationFirewallPolicyCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { @@ -634,12 +700,19 @@ func expandWebApplicationFirewallPolicyRuleGroupOverrides(input []interface{}) * RuleGroupName: utils.String(ruleGroupName), } - if disabledRules := v["disabled_rules"].([]interface{}); len(disabledRules) > 0 { - result.Rules = expandWebApplicationFirewallPolicyRules(disabledRules) + if !features.FourPointOhBeta() { + if disabledRules := v["disabled_rules"].([]interface{}); len(disabledRules) > 0 { + result.Rules = expandWebApplicationFirewallPolicyRules(disabledRules) + } + } + + if rules := v["rule"].([]interface{}); len(rules) > 0 { + result.Rules = expandWebApplicationFirewallPolicyOverrideRules(rules) } results = append(results, result) } + return &results } @@ -658,6 +731,31 @@ func expandWebApplicationFirewallPolicyRules(input []interface{}) *[]network.Man return &results } +func expandWebApplicationFirewallPolicyOverrideRules(input []interface{}) *[]network.ManagedRuleOverride { + results := make([]network.ManagedRuleOverride, 0) + for _, item := range input { + v := item.(map[string]interface{}) + state := network.ManagedRuleEnabledStateDisabled + if v["enabled"].(bool) { + state = network.ManagedRuleEnabledStateEnabled + } + + result := network.ManagedRuleOverride{ + RuleID: utils.String(v["id"].(string)), + State: state, + } + + action := v["action"].(string) + if action != "" { + result.Action = network.ActionType(action) + } + + results = append(results, result) + } + + return &results +} + func expandWebApplicationFirewallPolicyMatchCondition(input []interface{}) *[]network.MatchCondition { results := make([]network.MatchCondition, 0) for _, item := range input { @@ -862,14 +960,19 @@ func flattenWebApplicationFirewallPolicyRuleGroupOverrides(input *[]network.Mana v := make(map[string]interface{}) v["rule_group_name"] = item.RuleGroupName - v["disabled_rules"] = flattenWebApplicationFirewallPolicyManagedRuleOverrides(item.Rules) + + if !features.FourPointOhBeta() { + v["disabled_rules"] = flattenWebApplicationFirewallPolicyRules(item.Rules) + } + + v["rule"] = flattenWebApplicationFirewallPolicyOverrideRules(item.Rules) results = append(results, v) } return results } -func flattenWebApplicationFirewallPolicyManagedRuleOverrides(input *[]network.ManagedRuleOverride) []string { +func flattenWebApplicationFirewallPolicyRules(input *[]network.ManagedRuleOverride) []string { results := make([]string, 0) if input == nil || len(*input) == 0 { return results @@ -886,6 +989,28 @@ func flattenWebApplicationFirewallPolicyManagedRuleOverrides(input *[]network.Ma return results } +func flattenWebApplicationFirewallPolicyOverrideRules(input *[]network.ManagedRuleOverride) []interface{} { + results := make([]interface{}, 0) + if input == nil || len(*input) == 0 { + return results + } + + for _, item := range *input { + v := make(map[string]interface{}) + if item.RuleID != nil { + v["id"] = *item.RuleID + } + + v["enabled"] = item.State == network.ManagedRuleEnabledStateEnabled + + v["action"] = string(item.Action) + + results = append(results, v) + } + + return results +} + func flattenWebApplicationFirewallPolicyMatchCondition(input *[]network.MatchCondition) []interface{} { results := make([]interface{}, 0) if input == nil { diff --git a/internal/services/network/web_application_firewall_policy_resource_test.go b/internal/services/network/web_application_firewall_policy_resource_test.go index b917bbe5f461..91b4138caff0 100644 --- a/internal/services/network/web_application_firewall_policy_resource_test.go +++ b/internal/services/network/web_application_firewall_policy_resource_test.go @@ -80,12 +80,16 @@ func TestAccWebApplicationFirewallPolicy_complete(t *testing.T) { check.That(data.ResourceName).Key("managed_rules.0.exclusion.1.selector_match_operator").HasValue("EndsWith"), check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.#").HasValue("1"), check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.type").HasValue("OWASP"), - check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.version").HasValue("3.1"), + check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.version").HasValue("3.2"), check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.rule_group_override.#").HasValue("1"), check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.rule_group_override.0.rule_group_name").HasValue("REQUEST-920-PROTOCOL-ENFORCEMENT"), - check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.rule_group_override.0.disabled_rules.#").HasValue("2"), - check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.rule_group_override.0.disabled_rules.0").HasValue("920300"), - check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.rule_group_override.0.disabled_rules.1").HasValue("920440"), + check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.rule_group_override.0.rule.#").HasValue("2"), + check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.rule_group_override.0.rule.0.id").HasValue("920300"), + check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.rule_group_override.0.rule.0.enabled").HasValue("true"), + check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.rule_group_override.0.rule.0.action").HasValue("Log"), + check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.rule_group_override.0.rule.1.id").HasValue("920440"), + check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.rule_group_override.0.rule.1.enabled").HasValue("true"), + check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.rule_group_override.0.rule.1.action").HasValue("Block"), check.That(data.ResourceName).Key("policy_settings.#").HasValue("1"), check.That(data.ResourceName).Key("policy_settings.0.enabled").HasValue("true"), check.That(data.ResourceName).Key("policy_settings.0.mode").HasValue("Prevention"), @@ -154,12 +158,16 @@ func TestAccWebApplicationFirewallPolicy_update(t *testing.T) { check.That(data.ResourceName).Key("managed_rules.0.exclusion.1.selector_match_operator").HasValue("EndsWith"), check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.#").HasValue("1"), check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.type").HasValue("OWASP"), - check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.version").HasValue("3.1"), + check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.version").HasValue("3.2"), check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.rule_group_override.#").HasValue("1"), check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.rule_group_override.0.rule_group_name").HasValue("REQUEST-920-PROTOCOL-ENFORCEMENT"), - check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.rule_group_override.0.disabled_rules.#").HasValue("2"), - check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.rule_group_override.0.disabled_rules.0").HasValue("920300"), - check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.rule_group_override.0.disabled_rules.1").HasValue("920440"), + check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.rule_group_override.0.rule.#").HasValue("2"), + check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.rule_group_override.0.rule.0.id").HasValue("920300"), + check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.rule_group_override.0.rule.0.enabled").HasValue("true"), + check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.rule_group_override.0.rule.0.action").HasValue("Log"), + check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.rule_group_override.0.rule.1.id").HasValue("920440"), + check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.rule_group_override.0.rule.1.enabled").HasValue("true"), + check.That(data.ResourceName).Key("managed_rules.0.managed_rule_set.0.rule_group_override.0.rule.1.action").HasValue("Block"), check.That(data.ResourceName).Key("policy_settings.#").HasValue("1"), check.That(data.ResourceName).Key("policy_settings.0.enabled").HasValue("true"), check.That(data.ResourceName).Key("policy_settings.0.mode").HasValue("Prevention"), @@ -172,7 +180,7 @@ func TestAccWebApplicationFirewallPolicy_update(t *testing.T) { }) } -func TestAccWebApplicationFirewallPolicy_updateDisabledRules(t *testing.T) { +func TestAccWebApplicationFirewallPolicy_updateOverrideRules(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_web_application_firewall_policy", "test") r := WebApplicationFirewallResource{} @@ -185,7 +193,7 @@ func TestAccWebApplicationFirewallPolicy_updateDisabledRules(t *testing.T) { }, data.ImportStep(), { - Config: r.updateDisabledRules(data), + Config: r.updateOverrideRules(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), @@ -367,14 +375,21 @@ resource "azurerm_web_application_firewall_policy" "test" { managed_rule_set { type = "OWASP" - version = "3.1" + version = "3.2" rule_group_override { rule_group_name = "REQUEST-920-PROTOCOL-ENFORCEMENT" - disabled_rules = [ - "920300", - "920440", - ] + rule { + id = "920300" + enabled = true + action = "Log" + } + + rule { + id = "920440" + enabled = true + action = "Block" + } } } } @@ -387,7 +402,7 @@ resource "azurerm_web_application_firewall_policy" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomInteger) } -func (WebApplicationFirewallResource) updateDisabledRules(data acceptance.TestData) string { +func (WebApplicationFirewallResource) updateOverrideRules(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -470,7 +485,7 @@ resource "azurerm_web_application_firewall_policy" "test" { managed_rule_set { type = "OWASP" - version = "3.1" + version = "3.2" rule_group_override { rule_group_name = "REQUEST-920-PROTOCOL-ENFORCEMENT" diff --git a/website/docs/r/web_application_firewall_policy.html.markdown b/website/docs/r/web_application_firewall_policy.html.markdown index 2f879133291f..933467403b87 100644 --- a/website/docs/r/web_application_firewall_policy.html.markdown +++ b/website/docs/r/web_application_firewall_policy.html.markdown @@ -92,17 +92,23 @@ resource "azurerm_web_application_firewall_policy" "example" { managed_rule_set { type = "OWASP" - version = "3.1" + version = "3.2" rule_group_override { rule_group_name = "REQUEST-920-PROTOCOL-ENFORCEMENT" - disabled_rules = [ - "920300", - "920440" - ] + rule { + id = "920300" + enabled = true + action = "Log" + } + + rule { + id = "920440" + enabled = true + action = "Block" + } } } } - } ``` @@ -226,9 +232,19 @@ The `managed_rule_set` block supports the following: The `rule_group_override` block supports the following: -* `rule_group_name` - (Required) The name of the Rule Group +* `rule_group_name` - (Required) The name of the Rule Group. + +* `rule` - (Optional) One or more `rule` block defined below. + +--- + +The `rule` block supports the following: + +* `id` - (Required) Identifier for the managed rule. + +* `enabled` - (Optional) Describes if the managed rule is in enabled state or disabled state. Defaults to `false`. -* `disabled_rules` - (Optional) One or more Rule IDs +* `action` - (Optional) Describes the override action to be applied when rule matches. Possible values are `Allow`, `AnomalyScoring`, `Block` and `Log`. ## Attributes Reference