From bcd72ed0a1748a45487d6ef795858f1519be985a Mon Sep 17 00:00:00 2001 From: Jeffrey Cline <20408400+WodansSon@users.noreply.github.com> Date: Fri, 13 Dec 2024 23:47:00 -0700 Subject: [PATCH] Initial Check-in... --- ...n_frontdoor_firewall_policy_data_source.go | 9 ++ ...ntdoor_firewall_policy_data_source_test.go | 29 +++++- .../cdn_frontdoor_firewall_policy_resource.go | 25 +++++- ...frontdoor_firewall_policy_resource_test.go | 90 +++++++++++++++++++ ...dn_frontdoor_firewall_policy.html.markdown | 2 + ...dn_frontdoor_firewall_policy.html.markdown | 14 +-- 6 files changed, 161 insertions(+), 8 deletions(-) diff --git a/internal/services/cdn/cdn_frontdoor_firewall_policy_data_source.go b/internal/services/cdn/cdn_frontdoor_firewall_policy_data_source.go index 11b6efcfaf6b..4bbaefcb7f52 100644 --- a/internal/services/cdn/cdn_frontdoor_firewall_policy_data_source.go +++ b/internal/services/cdn/cdn_frontdoor_firewall_policy_data_source.go @@ -49,6 +49,11 @@ func dataSourceCdnFrontDoorFirewallPolicy() *pluginsdk.Resource { Computed: true, }, + "js_challenge_cookie_expiration_in_minutes": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + "redirect_url": { Type: pluginsdk.TypeString, Computed: true, @@ -103,6 +108,10 @@ func dataSourceCdnFrontDoorFirewallPolicyRead(d *pluginsdk.ResourceData, meta in d.Set("enabled", pointer.From(policy.EnabledState) == waf.PolicyEnabledStateEnabled) d.Set("mode", pointer.From(policy.Mode)) d.Set("redirect_url", policy.RedirectURL) + + if policy.JavascriptChallengeExpirationInMinutes != nil { + d.Set("js_challenge_cookie_expiration_in_minutes", int(pointer.From(policy.JavascriptChallengeExpirationInMinutes))) + } } } } diff --git a/internal/services/cdn/cdn_frontdoor_firewall_policy_data_source_test.go b/internal/services/cdn/cdn_frontdoor_firewall_policy_data_source_test.go index 8abf621ceb4f..7ee2742b6352 100644 --- a/internal/services/cdn/cdn_frontdoor_firewall_policy_data_source_test.go +++ b/internal/services/cdn/cdn_frontdoor_firewall_policy_data_source_test.go @@ -27,13 +27,38 @@ func TestAccCdnFrontDoorFirewallPolicyDataSource_basic(t *testing.T) { }) } +func TestAccCdnFrontDoorFirewallPolicyJsChallengeDataSource_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_cdn_frontdoor_firewall_policy", "test") + d := CdnFrontDoorFirewallPolicyDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: d.basicJsChallenge(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("redirect_url").MatchesOtherKey(check.That("azurerm_cdn_frontdoor_firewall_policy.test").Key("redirect_url")), + ), + }, + }) +} + func (CdnFrontDoorFirewallPolicyDataSource) basic(data acceptance.TestData) string { return fmt.Sprintf(` %s data "azurerm_cdn_frontdoor_firewall_policy" "test" { - name = azurerm_cdn_frontdoor_firewall_policy.test.name + name = "accTestDataSourceBasic-%d" + resource_group_name = azurerm_cdn_frontdoor_profile.test.resource_group_name +} +`, CdnFrontDoorFirewallPolicyResource{}.basic(data), data.RandomInteger) +} + +func (CdnFrontDoorFirewallPolicyDataSource) basicJsChallenge(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data "azurerm_cdn_frontdoor_firewall_policy" "test" { + name = "accTestDataSourceJsChallenge-%d" resource_group_name = azurerm_cdn_frontdoor_profile.test.resource_group_name } -`, CdnFrontDoorFirewallPolicyResource{}.complete(data)) +`, CdnFrontDoorFirewallPolicyResource{}.basicJsChallenge(data), data.RandomInteger) } diff --git a/internal/services/cdn/cdn_frontdoor_firewall_policy_resource.go b/internal/services/cdn/cdn_frontdoor_firewall_policy_resource.go index 4b295ce41af5..198e7990db9d 100644 --- a/internal/services/cdn/cdn_frontdoor_firewall_policy_resource.go +++ b/internal/services/cdn/cdn_frontdoor_firewall_policy_resource.go @@ -77,6 +77,15 @@ func resourceCdnFrontDoorFirewallPolicy() *pluginsdk.Resource { Default: true, }, + // NOTE: This cannot have a default value as that would always set the js challenge expiration + // value meaning you could not remove that policy from the WAF and would be a breaking change + // to pre-existing resources... + "js_challenge_cookie_expiration_in_minutes": { + Type: pluginsdk.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(5, 1440), + }, + "redirect_url": { Type: pluginsdk.TypeString, Optional: true, @@ -490,6 +499,7 @@ func resourceCdnFrontDoorFirewallPolicyCreate(d *pluginsdk.ResourceData, meta in sku := d.Get("sku_name").(string) mode := waf.PolicyMode(d.Get("mode").(string)) redirectUrl := d.Get("redirect_url").(string) + jsChallengeExpirationInMinutes := int64(d.Get("js_challenge_cookie_expiration_in_minutes").(int)) customBlockResponseStatusCode := d.Get("custom_block_response_status_code").(int) customBlockResponseBody := d.Get("custom_block_response_body").(string) customRules := d.Get("custom_rule").([]interface{}) @@ -518,6 +528,10 @@ func resourceCdnFrontDoorFirewallPolicyCreate(d *pluginsdk.ResourceData, meta in Tags: tags.Expand(d.Get("tags").(map[string]interface{})), } + if jsChallengeExpirationInMinutes > 0 { + payload.Properties.PolicySettings.JavascriptChallengeExpirationInMinutes = pointer.To(jsChallengeExpirationInMinutes) + } + if managedRules != nil { payload.Properties.ManagedRules = managedRules } @@ -570,7 +584,7 @@ func resourceCdnFrontDoorFirewallPolicyUpdate(d *pluginsdk.ResourceData, meta in props := *model.Properties - if d.HasChanges("custom_block_response_body", "custom_block_response_status_code", "enabled", "mode", "redirect_url", "request_body_check_enabled") { + if d.HasChanges("custom_block_response_body", "custom_block_response_status_code", "enabled", "mode", "redirect_url", "request_body_check_enabled", "js_challenge_cookie_expiration_in_minutes") { enabled := waf.PolicyEnabledStateDisabled if d.Get("enabled").(bool) { enabled = waf.PolicyEnabledStateEnabled @@ -580,12 +594,17 @@ func resourceCdnFrontDoorFirewallPolicyUpdate(d *pluginsdk.ResourceData, meta in if d.Get("request_body_check_enabled").(bool) { requestBodyCheck = waf.PolicyRequestBodyCheckEnabled } + props.PolicySettings = &waf.PolicySettings{ EnabledState: pointer.To(enabled), Mode: pointer.To(waf.PolicyMode(d.Get("mode").(string))), RequestBodyCheck: pointer.To(requestBodyCheck), } + if jsChallengeExpirationInMinutes := int64(d.Get("js_challenge_cookie_expiration_in_minutes").(int)); jsChallengeExpirationInMinutes > 0 { + props.PolicySettings.JavascriptChallengeExpirationInMinutes = pointer.To(jsChallengeExpirationInMinutes) + } + if redirectUrl := d.Get("redirect_url").(string); redirectUrl != "" { props.PolicySettings.RedirectURL = pointer.To(redirectUrl) } @@ -679,6 +698,10 @@ func resourceCdnFrontDoorFirewallPolicyRead(d *pluginsdk.ResourceData, meta inte d.Set("redirect_url", policy.RedirectURL) d.Set("custom_block_response_status_code", int(pointer.From(policy.CustomBlockResponseStatusCode))) d.Set("custom_block_response_body", policy.CustomBlockResponseBody) + + if policy.JavascriptChallengeExpirationInMinutes != nil { + d.Set("js_challenge_cookie_expiration_in_minutes", int(pointer.From(policy.JavascriptChallengeExpirationInMinutes))) + } } } diff --git a/internal/services/cdn/cdn_frontdoor_firewall_policy_resource_test.go b/internal/services/cdn/cdn_frontdoor_firewall_policy_resource_test.go index 3b51daec370c..7af666d936fb 100644 --- a/internal/services/cdn/cdn_frontdoor_firewall_policy_resource_test.go +++ b/internal/services/cdn/cdn_frontdoor_firewall_policy_resource_test.go @@ -33,6 +33,21 @@ func TestAccCdnFrontDoorFirewallPolicy_basic(t *testing.T) { }) } +func TestAccCdnFrontDoorFirewallPolicyJsChallenge_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_firewall_policy", "test") + r := CdnFrontDoorFirewallPolicyResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basicJsChallenge(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("js_challenge_cookie_expiration_in_minutes").HasValue("30"), + ), + }, + data.ImportStep(), + }) +} + func TestAccCdnFrontDoorFirewallPolicy_requiresImport(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_firewall_policy", "test") r := CdnFrontDoorFirewallPolicyResource{} @@ -75,6 +90,47 @@ func TestAccCdnFrontDoorFirewallPolicy_update(t *testing.T) { }) } +func TestAccCdnFrontDoorFirewallPolicyJsChallenge_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_firewall_policy", "test") + r := CdnFrontDoorFirewallPolicyResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("request_body_check_enabled").HasValue("false"), + check.That(data.ResourceName).Key("js_challenge_cookie_expiration_in_minutes").DoesNotExist(), + ), + }, + data.ImportStep(), + { + Config: r.basicJsChallenge(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("js_challenge_cookie_expiration_in_minutes").HasValue("30"), + ), + }, + data.ImportStep(), + { + Config: r.basicJsChallengeUpdate(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("js_challenge_cookie_expiration_in_minutes").HasValue("1440"), + ), + }, + data.ImportStep(), + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("request_body_check_enabled").HasValue("false"), + check.That(data.ResourceName).Key("js_challenge_cookie_expiration_in_minutes").DoesNotExist(), + ), + }, + data.ImportStep(), + }) +} + func TestAccCdnFrontDoorFirewallPolicy_complete(t *testing.T) { // NOTE: Regression test case for issue #19088 data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_firewall_policy", "test") @@ -303,6 +359,38 @@ resource "azurerm_cdn_frontdoor_firewall_policy" "test" { `, tmp, data.RandomInteger) } +func (r CdnFrontDoorFirewallPolicyResource) basicJsChallenge(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 + mode = "Prevention" + + js_challenge_cookie_expiration_in_minutes = 30 +} +`, tmp, data.RandomInteger) +} + +func (r CdnFrontDoorFirewallPolicyResource) basicJsChallengeUpdate(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 + mode = "Prevention" + + js_challenge_cookie_expiration_in_minutes = 1440 +} +`, tmp, data.RandomInteger) +} + func (r CdnFrontDoorFirewallPolicyResource) requiresImport(data acceptance.TestData) string { return fmt.Sprintf(` %s @@ -389,6 +477,8 @@ resource "azurerm_cdn_frontdoor_firewall_policy" "test" { custom_block_response_status_code = 403 custom_block_response_body = "PGh0bWw+CjxoZWFkZXI+PHRpdGxlPkhlbGxvPC90aXRsZT48L2hlYWRlcj4KPGJvZHk+CkhlbGxvIHdvcmxkCjwvYm9keT4KPC9odG1sPg==" + js_challenge_cookie_expiration_in_minutes = 30 + custom_rule { name = "Rule1" enabled = true diff --git a/website/docs/d/cdn_frontdoor_firewall_policy.html.markdown b/website/docs/d/cdn_frontdoor_firewall_policy.html.markdown index 4c7d1e3c6af9..75920f0a14b3 100644 --- a/website/docs/d/cdn_frontdoor_firewall_policy.html.markdown +++ b/website/docs/d/cdn_frontdoor_firewall_policy.html.markdown @@ -37,6 +37,8 @@ The following attributes are exported: * `frontend_endpoint_ids` - The Front Door Profiles frontend endpoints associated with this Front Door Firewall Policy. +* `js_challenge_cookie_expiration_in_minutes` - The Front Door Firewall Policy JavaScript challenge cookie lifetime in minutes. + * `mode` - The Front Door Firewall Policy mode. * `redirect_url` - The redirect URL for the client. diff --git a/website/docs/r/cdn_frontdoor_firewall_policy.html.markdown b/website/docs/r/cdn_frontdoor_firewall_policy.html.markdown index f04bb74d98be..0be70ae7ca08 100644 --- a/website/docs/r/cdn_frontdoor_firewall_policy.html.markdown +++ b/website/docs/r/cdn_frontdoor_firewall_policy.html.markdown @@ -137,15 +137,19 @@ The following arguments are supported: * `sku_name` - (Required) The sku's pricing tier for this Front Door Firewall Policy. Possible values include `Standard_AzureFrontDoor` or `Premium_AzureFrontDoor`. Changing this forces a new resource to be created. --> **NOTE:** The `Standard_AzureFrontDoor` Front Door Firewall Policy sku may contain `custom` rules only. The `Premium_AzureFrontDoor` Front Door Firewall Policy skus may contain both `custom` and `managed` rules. +-> **Note:** The `Standard_AzureFrontDoor` Front Door Firewall Policy sku may contain `custom` rules only. The `Premium_AzureFrontDoor` Front Door Firewall Policy skus may contain both `custom` and `managed` rules. * `enabled` - (Optional) Is the Front Door Firewall Policy enabled? Defaults to `true`. +* `js_challenge_cookie_expiration_in_minutes` - (Optional) Specifies the JavaScript challenge cookie lifetime in minutes, after which the user will be revalidated. Possible values are between `5` to `1440` minutes. + +!> **Important:** Azure Web Application Firewall JavaScript challenge is currently in **PREVIEW**. See the [Supplemental Terms of Use for Microsoft Azure Previews](https://azure.microsoft.com/support/legal/preview-supplemental-terms/) for legal terms that apply to Azure features that are in beta, preview, or otherwise not yet released into general availability. + * `mode` - (Required) The Front Door Firewall Policy mode. Possible values are `Detection`, `Prevention`. * `request_body_check_enabled` - (Optional) Should policy managed rules inspect the request body content? Defaults to `true`. --> **NOTE:** When run in `Detection` mode, the Front Door Firewall Policy doesn't take any other actions other than monitoring and logging the request and its matched Front Door Rule to the Web Application Firewall logs. +-> **Note:** When run in `Detection` mode, the Front Door Firewall Policy doesn't take any other actions other than monitoring and logging the request and its matched Front Door Rule to the Web Application Firewall logs. * `redirect_url` - (Optional) If action type is redirect, this field represents redirect URL for the client. @@ -227,7 +231,7 @@ A `rule` block supports the following: * `action` - (Required) The action to be applied when the managed rule matches or when the anomaly score is 5 or greater. Possible values for DRS `1.1` and below are `Allow`, `Log`, `Block`, and `Redirect`. For DRS `2.0` and above the possible values are `Log` or `AnomalyScoring`. -->**NOTE:** Please see the DRS [product documentation](https://learn.microsoft.com/azure/web-application-firewall/afds/waf-front-door-drs?tabs=drs20#anomaly-scoring-mode) for more information. +->**Note:** Please see the DRS [product documentation](https://learn.microsoft.com/azure/web-application-firewall/afds/waf-front-door-drs?tabs=drs20#anomaly-scoring-mode) for more information. * `enabled` - (Optional) Is the managed rule override enabled or disabled. Defaults to `false` @@ -239,13 +243,13 @@ An `exclusion` block supports the following: * `match_variable` - (Required) The variable type to be excluded. Possible values are `QueryStringArgNames`, `RequestBodyPostArgNames`, `RequestCookieNames`, `RequestHeaderNames`, `RequestBodyJsonArgNames` --> **NOTE:** `RequestBodyJsonArgNames` is only available on Default Rule Set (DRS) 2.0 or later +-> **Note:** `RequestBodyJsonArgNames` is only available on Default Rule Set (DRS) 2.0 or later * `operator` - (Required) Comparison operator to apply to the selector when specifying which elements in the collection this exclusion applies to. Possible values are: `Equals`, `Contains`, `StartsWith`, `EndsWith`, `EqualsAny`. * `selector` - (Required) Selector for the value in the `match_variable` attribute this exclusion applies to. --> **NOTE:** `selector` must be set to `*` if `operator` is set to `EqualsAny`. +-> **Note:** `selector` must be set to `*` if `operator` is set to `EqualsAny`. ## Attributes Reference