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

[Dependencies:] azurerm_cdn_frontdoor_rule, azurerm_cdn_frontdoor_ruleset and cdn_frontdoor_rule_actions - upgrade resources to 2024-02-01 API to enable support for the JS Challenge field #28308

Merged
merged 19 commits into from
Feb 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
31 changes: 17 additions & 14 deletions internal/services/cdn/cdn_frontdoor_firewall_policy_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package cdn
import (
"fmt"
"strconv"
"strings"
"time"

"github.com/hashicorp/go-azure-helpers/lang/pointer"
Expand Down Expand Up @@ -428,16 +429,13 @@ func resourceCdnFrontDoorFirewallPolicy() *pluginsdk.Resource {
},
},

// NOTE: 'ActionTypeAnomalyScoring' is only valid with 2.0 and above
// 'ActionTypeJSChallenge' is only valid with BotManagerRuleSets
"action": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{
string(waf.ActionTypeAllow),
string(waf.ActionTypeLog),
string(waf.ActionTypeBlock),
string(waf.ActionTypeRedirect),
string(waf.ActionTypeAnomalyScoring), // Only valid with 2.0 and above
}, false),
ValidateFunc: validation.StringInSlice(waf.PossibleValuesForActionType(),
false),
},
},
},
Expand Down Expand Up @@ -840,7 +838,7 @@ func expandCdnFrontDoorFirewallManagedRules(input []interface{}) (*waf.ManagedRu
return nil, fmt.Errorf("the managed rule set type %q and version %q is not supported. If you wish to use the 'Microsoft_DefaultRuleSet' type please update your 'version' field to be '1.1', '2.0' or '2.1', got %q", ruleType, version, version)
}

ruleGroupOverrides, err := expandCdnFrontDoorFirewallManagedRuleGroupOverride(overrides, version, fVersion)
ruleGroupOverrides, err := expandCdnFrontDoorFirewallManagedRuleGroupOverride(overrides, version, fVersion, ruleType)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -887,7 +885,7 @@ func expandCdnFrontDoorFirewallManagedRuleGroupExclusion(input []interface{}) *[
return &results
}

func expandCdnFrontDoorFirewallManagedRuleGroupOverride(input []interface{}, versionRaw string, version float64) (*[]waf.ManagedRuleGroupOverride, error) {
func expandCdnFrontDoorFirewallManagedRuleGroupOverride(input []interface{}, versionRaw string, version float64, ruleType string) (*[]waf.ManagedRuleGroupOverride, error) {
result := make([]waf.ManagedRuleGroupOverride, 0)
if len(input) == 0 {
return nil, nil
Expand All @@ -898,7 +896,7 @@ func expandCdnFrontDoorFirewallManagedRuleGroupOverride(input []interface{}, ver

exclusions := expandCdnFrontDoorFirewallManagedRuleGroupExclusion(override["exclusion"].([]interface{}))
ruleGroupName := override["rule_group_name"].(string)
rules, err := expandCdnFrontDoorFirewallRuleOverride(override["rule"].([]interface{}), versionRaw, version)
rules, err := expandCdnFrontDoorFirewallRuleOverride(override["rule"].([]interface{}), versionRaw, version, ruleType)
if err != nil {
return nil, err
}
Expand All @@ -913,7 +911,7 @@ func expandCdnFrontDoorFirewallManagedRuleGroupOverride(input []interface{}, ver
return &result, nil
}

func expandCdnFrontDoorFirewallRuleOverride(input []interface{}, versionRaw string, version float64) (*[]waf.ManagedRuleOverride, error) {
func expandCdnFrontDoorFirewallRuleOverride(input []interface{}, versionRaw string, version float64, ruleType string) (*[]waf.ManagedRuleOverride, error) {
result := make([]waf.ManagedRuleOverride, 0)
if len(input) == 0 {
return nil, nil
Expand All @@ -932,10 +930,15 @@ func expandCdnFrontDoorFirewallRuleOverride(input []interface{}, versionRaw stri

// NOTE: Default Rule Sets(DRS) 2.0 and above rules only use action type of 'AnomalyScoring' or 'Log'. Issues 19088 and 19561
// This will still work for bot rules as well since it will be the default value of 1.0
if version < 2.0 && action == waf.ActionTypeAnomalyScoring {
return nil, fmt.Errorf("'AnomalyScoring' is only valid in managed rules that are DRS 2.0 and above, got %q", versionRaw)
} else if version >= 2.0 && action != waf.ActionTypeAnomalyScoring && action != waf.ActionTypeLog {
switch {
case version < 2.0 && action == waf.ActionTypeAnomalyScoring:
return nil, fmt.Errorf("%q is only valid in managed rules where 'type' is DRS and `version` is '2.0' or above, got %q", waf.ActionTypeAnomalyScoring, versionRaw)

case version >= 2.0 && action != waf.ActionTypeAnomalyScoring && action != waf.ActionTypeLog:
return nil, fmt.Errorf("the managed rules 'action' field must be set to 'AnomalyScoring' or 'Log' if the managed rule is DRS 2.0 or above, got %q", action)

case !strings.Contains(strings.ToLower(ruleType), "botmanagerruleset") && action == waf.ActionTypeJSChallenge:
return nil, fmt.Errorf("%q is only valid if the managed rules 'type' is 'Microsoft_BotManagerRuleSet', got %q", waf.ActionTypeJSChallenge, ruleType)
}

exclusions := expandCdnFrontDoorFirewallManagedRuleGroupExclusion(rule["exclusion"].([]interface{}))
Expand Down
157 changes: 149 additions & 8 deletions internal/services/cdn/cdn_frontdoor_firewall_policy_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@ import (
"regexp"
"testing"

"github.com/hashicorp/go-azure-helpers/lang/response"
"github.com/hashicorp/go-azure-helpers/lang/pointer"
waf "github.com/hashicorp/go-azure-sdk/resource-manager/frontdoor/2024-02-01/webapplicationfirewallpolicies"
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance"
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check"
"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
"github.com/hashicorp/terraform-provider-azurerm/utils"
)

type CdnFrontDoorFirewallPolicyResource struct{}
Expand Down Expand Up @@ -197,7 +196,7 @@ func TestAccCdnFrontDoorFirewallPolicy_DRSOnePointOhError(t *testing.T) {
data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.DRSOnePointOhError(data),
ExpectError: regexp.MustCompile("'AnomalyScoring' is only valid in managed rules that are DRS 2.0 and above"),
ExpectError: regexp.MustCompile(`"AnomalyScoring" is only valid in managed rules where 'type' is DRS`),
},
})
}
Expand Down Expand Up @@ -309,21 +308,71 @@ func TestAccCdnFrontDoorFirewallPolicy_DRSTwoPointOneActionError(t *testing.T) {
})
}

func TestAccCdnFrontDoorFirewallPolicy_JSChallengeDRSError(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_firewall_policy", "test")
r := CdnFrontDoorFirewallPolicyResource{}
data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.JSChallengeDRSError(data),
ExpectError: regexp.MustCompile(`"JSChallenge" is only valid if the managed rules 'type' is 'Microsoft_BotManagerRuleSet', got "DefaultRuleSet"`),
},
})
}

func TestAccCdnFrontDoorFirewallPolicy_JSChallengeBasic(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_firewall_policy", "test")
r := CdnFrontDoorFirewallPolicyResource{}
data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.JSChallengeBasic(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
})
}

func TestAccCdnFrontDoorFirewallPolicy_JSChallengeUpdate(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_firewall_policy", "test")
r := CdnFrontDoorFirewallPolicyResource{}
data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.JSChallengeBasic(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
{
Config: r.JSChallengeRemove(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
{
Config: r.JSChallengeBasic(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
})
}

func (CdnFrontDoorFirewallPolicyResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) {
id, err := waf.ParseFrontDoorWebApplicationFirewallPolicyID(state.ID)
if err != nil {
return nil, err
}

result, err := clients.Cdn.FrontDoorFirewallPoliciesClient.PoliciesGet(ctx, *id)
_, err = clients.Cdn.FrontDoorFirewallPoliciesClient.PoliciesGet(ctx, *id)
if err != nil {
if response.WasNotFound(result.HttpResponse) {
return utils.Bool(false), nil
}
return nil, fmt.Errorf("retrieving %s: %+v", id, err)
}

return utils.Bool(true), nil
return pointer.To(true), nil
}

func (CdnFrontDoorFirewallPolicyResource) template(data acceptance.TestData) string {
Expand Down Expand Up @@ -883,3 +932,95 @@ resource "azurerm_cdn_frontdoor_firewall_policy" "test" {
}
`, tmp, data.RandomInteger)
}

func (r CdnFrontDoorFirewallPolicyResource) JSChallengeDRSError(data acceptance.TestData) string {
tmp := r.template(data)
return fmt.Sprintf(`
%s

resource "azurerm_cdn_frontdoor_firewall_policy" "test" {
name = "accTestWAF%d"
resource_group_name = azurerm_resource_group.test.name
sku_name = azurerm_cdn_frontdoor_profile.test.sku_name
enabled = true
mode = "Prevention"
redirect_url = "https://www.contoso.com"
custom_block_response_status_code = 403
custom_block_response_body = "PGh0bWw+CjxoZWFkZXI+PHRpdGxlPkhlbGxvPC90aXRsZT48L2hlYWRlcj4KPGJvZHk+CkhlbGxvIHdvcmxkCjwvYm9keT4KPC9odG1sPg=="

managed_rule {
type = "DefaultRuleSet"
version = "preview-0.1"
action = "Block"

override {
rule_group_name = "PHP"

rule {
rule_id = "933100"
enabled = false
action = "JSChallenge"
}
}
}
}
`, tmp, data.RandomInteger)
}

func (r CdnFrontDoorFirewallPolicyResource) JSChallengeBasic(data acceptance.TestData) string {
tmp := r.template(data)
return fmt.Sprintf(`
%s

resource "azurerm_cdn_frontdoor_firewall_policy" "test" {
name = "accTestWAF%d"
resource_group_name = azurerm_resource_group.test.name
sku_name = azurerm_cdn_frontdoor_profile.test.sku_name
enabled = true
mode = "Prevention"
redirect_url = "https://www.contoso.com"
custom_block_response_status_code = 403
custom_block_response_body = "PGh0bWw+CjxoZWFkZXI+PHRpdGxlPkhlbGxvPC90aXRsZT48L2hlYWRlcj4KPGJvZHk+CkhlbGxvIHdvcmxkCjwvYm9keT4KPC9odG1sPg=="

managed_rule {
type = "Microsoft_BotManagerRuleSet"
version = "1.0"
action = "Log"

override {
rule_group_name = "BadBots"

rule {
rule_id = "Bot100200"
enabled = true
action = "JSChallenge"
}
}
}
}
`, tmp, data.RandomInteger)
}

func (r CdnFrontDoorFirewallPolicyResource) JSChallengeRemove(data acceptance.TestData) string {
tmp := r.template(data)
return fmt.Sprintf(`
%s

resource "azurerm_cdn_frontdoor_firewall_policy" "test" {
name = "accTestWAF%d"
resource_group_name = azurerm_resource_group.test.name
sku_name = azurerm_cdn_frontdoor_profile.test.sku_name
enabled = true
mode = "Prevention"
redirect_url = "https://www.contoso.com"
custom_block_response_status_code = 403
custom_block_response_body = "PGh0bWw+CjxoZWFkZXI+PHRpdGxlPkhlbGxvPC90aXRsZT48L2hlYWRlcj4KPGJvZHk+CkhlbGxvIHdvcmxkCjwvYm9keT4KPC9odG1sPg=="

managed_rule {
type = "Microsoft_BotManagerRuleSet"
version = "1.0"
action = "Log"
}
}
`, tmp, data.RandomInteger)
}
Loading
Loading